DynamicAssembly, FieldBuilder and fixed size buffers

M

Marek

Hi
I am trying to dynamically create the following structure using
AssemblyBuilder, TypeBuilder and DefineField:

[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct SimpleType2
{
public double dScalar1;
public fixed double dArray[5];
public int iScalar1;
}

I've got pretty much everything working except the fixed keyword on the
dArray[5] member. I tried to add this using a custom attribute, but as soon
as I did that, the field was not added to the created type. Is this the
right approach? Could it be that I need to mark the code as unsafe or
something?

Any help would be much appreciated.

Best regards

Marek
 
J

Jialiang Ge [MSFT]

Hello Marek,

I'd suggest you use the utility "ILReader" from:
http://blogs.msdn.com/yirutang/archive/2006/06/07/621125.aspx
plus the .net Reflector from
http://www.aisto.com/roeder/dotnet/

The two tools can help us write codes to dynamically create
assembly/module/type. With the help of the tools, I wrote the following
code for your SimpleType2 type:
(Note: because Newsgroup system may wrap the long sentences and make it
hard for you to read my codes, I attach the .cs file to this message for
your convenience. You can download the .cs code file with outlook express
or windows mail)

public class Test
{
static TypeBuilder fixedBufTypeBuilder;

public static void Main()
{
AppDomain.CurrentDomain.TypeResolve += new
ResolveEventHandler(CurrentDomain_TypeResolve);

#region AttributeBuilders
// UnsafeValueTypeAttribute
ConstructorInfo unsafeValueTypeCtor =
typeof(UnsafeValueTypeAttribute).GetConstructor(new Type[] { });
CustomAttributeBuilder unsafeAttrBuilder = new
CustomAttributeBuilder(unsafeValueTypeCtor, new object[] { });

// UnverifiableCodeAttribute
ConstructorInfo unverifiableCtor =
typeof(UnverifiableCodeAttribute).GetConstructor(new Type[] { });
CustomAttributeBuilder unverifiableAttrBuilder = new
CustomAttributeBuilder(unverifiableCtor, new object[] { });

// CompilerGeneratedAttribute
ConstructorInfo compilerGenCtor =
typeof(CompilerGeneratedAttribute).GetConstructor(new Type[] { });
CustomAttributeBuilder compilerGenAttrBuilder = new
CustomAttributeBuilder(compilerGenCtor, new object[] { });

// FixedBufferAttribute
ConstructorInfo fixedBufCtor =
typeof(FixedBufferAttribute).GetConstructor(new Type[] { typeof(Type),
typeof(int) });
CustomAttributeBuilder fixedBufAttrBuilder = new
CustomAttributeBuilder(fixedBufCtor, new object[] { typeof(double), 5 });
#endregion

AssemblyName asmname = new AssemblyName("mytest");
AssemblyBuilder asmbuild =
AppDomain.CurrentDomain.DefineDynamicAssembly(asmname,
AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder =
asmbuild.DefineDynamicModule("mytest", "mytest.dll");
moduleBuilder.SetCustomAttribute(unverifiableAttrBuilder);

TypeBuilder typeBuilder = moduleBuilder.DefineType("SimpleType2",
TypeAttributes.Public | TypeAttributes.SequentialLayout,
typeof(ValueType), PackingSize.Size8);

#region <dArray>e__FixedBuffer0
fixedBufTypeBuilder =
typeBuilder.DefineNestedType("<dArray>e__FixedBuffer0",
TypeAttributes.NestedPublic | TypeAttributes.SequentialLayout,
typeof(ValueType), 40);
fixedBufTypeBuilder.SetCustomAttribute(compilerGenAttrBuilder);
fixedBufTypeBuilder.SetCustomAttribute(unsafeAttrBuilder);
fixedBufTypeBuilder.DefineField("FixedElementField",
typeof(double), FieldAttributes.Public);
#endregion

typeBuilder.DefineField("dScalar1", typeof(double),
FieldAttributes.Public);
FieldBuilder fixedBufFieldBuilder =
typeBuilder.DefineField("dArray", fixedBufTypeBuilder,
FieldAttributes.Public);
fixedBufFieldBuilder.SetCustomAttribute(fixedBufAttrBuilder);
typeBuilder.DefineField("iScalar1", typeof(int),
FieldAttributes.Public);

typeBuilder.CreateType();

asmbuild.Save("mytest");
}

private static Assembly CurrentDomain_TypeResolve(object sender,
ResolveEventArgs args)
{
if (args.Name == "<dArray>e__FixedBuffer0")
{
return fixedBufTypeBuilder.CreateType().Assembly;
}
return null;
}
}

In the code above, the line
AppDomain.CurrentDomain.TypeResolve += new
ResolveEventHandler(CurrentDomain_TypeResolve);
Is used for the nested struct type. See:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?Feedba
ckID=98299

Below is my detailed steps when I write the code above (I think this may
help you if you encounter such cases again in future)
Step1. Compose a simple assembly with our target code:
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public unsafe struct SimpleType2
{
public double dScalar1;
public fixed double dArray[5];
public int iScalar1;
}

Step2. Compile the assembly, use .NET Reflector to open the output dll, and
use ILReader to parse the dll.
Step3. Translate the output of ILReader to our AssemblyBuilder,
TypeBuilder, DefineField codes
Step4. Run our AssemblyBuilder, TypeBuilder, DefineField codes and get the
resulting assembly dll.
Step5. Use .Net Reflector to open the dll, and compare its result with the
that of the dll in step2.

If you have any other concerns or questions, feel free to let me know.

Regards,
Jialiang Ge ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
M

Marek

Hi Jialiang
Fantastic - that has done the trick. Believe it or not this code is now
calling into a FORTRAN dll with a structure, which is exactly what I wanted
to do. As with all these things though, one answer generates another
question:

I would like to display the fixed array field correctly inside the property
grid and I notice the debugger has no problem working out what the field is,
whereas the property grid does. I guess I need some sort of TypeConverter?
Is there a standard way of achieving what I need?

Many thanks again.

Best regards

Marek

Jialiang Ge said:
Hello Marek,

I'd suggest you use the utility "ILReader" from:
http://blogs.msdn.com/yirutang/archive/2006/06/07/621125.aspx
plus the .net Reflector from
http://www.aisto.com/roeder/dotnet/

The two tools can help us write codes to dynamically create
assembly/module/type. With the help of the tools, I wrote the following
code for your SimpleType2 type:
(Note: because Newsgroup system may wrap the long sentences and make it
hard for you to read my codes, I attach the .cs file to this message for
your convenience. You can download the .cs code file with outlook express
or windows mail)

public class Test
{
static TypeBuilder fixedBufTypeBuilder;

public static void Main()
{
AppDomain.CurrentDomain.TypeResolve += new
ResolveEventHandler(CurrentDomain_TypeResolve);

#region AttributeBuilders
// UnsafeValueTypeAttribute
ConstructorInfo unsafeValueTypeCtor =
typeof(UnsafeValueTypeAttribute).GetConstructor(new Type[] { });
CustomAttributeBuilder unsafeAttrBuilder = new
CustomAttributeBuilder(unsafeValueTypeCtor, new object[] { });

// UnverifiableCodeAttribute
ConstructorInfo unverifiableCtor =
typeof(UnverifiableCodeAttribute).GetConstructor(new Type[] { });
CustomAttributeBuilder unverifiableAttrBuilder = new
CustomAttributeBuilder(unverifiableCtor, new object[] { });

// CompilerGeneratedAttribute
ConstructorInfo compilerGenCtor =
typeof(CompilerGeneratedAttribute).GetConstructor(new Type[] { });
CustomAttributeBuilder compilerGenAttrBuilder = new
CustomAttributeBuilder(compilerGenCtor, new object[] { });

// FixedBufferAttribute
ConstructorInfo fixedBufCtor =
typeof(FixedBufferAttribute).GetConstructor(new Type[] { typeof(Type),
typeof(int) });
CustomAttributeBuilder fixedBufAttrBuilder = new
CustomAttributeBuilder(fixedBufCtor, new object[] { typeof(double), 5 });
#endregion

AssemblyName asmname = new AssemblyName("mytest");
AssemblyBuilder asmbuild =
AppDomain.CurrentDomain.DefineDynamicAssembly(asmname,
AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder =
asmbuild.DefineDynamicModule("mytest", "mytest.dll");
moduleBuilder.SetCustomAttribute(unverifiableAttrBuilder);

TypeBuilder typeBuilder = moduleBuilder.DefineType("SimpleType2",
TypeAttributes.Public | TypeAttributes.SequentialLayout,
typeof(ValueType), PackingSize.Size8);

#region <dArray>e__FixedBuffer0
fixedBufTypeBuilder =
typeBuilder.DefineNestedType("<dArray>e__FixedBuffer0",
TypeAttributes.NestedPublic | TypeAttributes.SequentialLayout,
typeof(ValueType), 40);
fixedBufTypeBuilder.SetCustomAttribute(compilerGenAttrBuilder);
fixedBufTypeBuilder.SetCustomAttribute(unsafeAttrBuilder);
fixedBufTypeBuilder.DefineField("FixedElementField",
typeof(double), FieldAttributes.Public);
#endregion

typeBuilder.DefineField("dScalar1", typeof(double),
FieldAttributes.Public);
FieldBuilder fixedBufFieldBuilder =
typeBuilder.DefineField("dArray", fixedBufTypeBuilder,
FieldAttributes.Public);
fixedBufFieldBuilder.SetCustomAttribute(fixedBufAttrBuilder);
typeBuilder.DefineField("iScalar1", typeof(int),
FieldAttributes.Public);

typeBuilder.CreateType();

asmbuild.Save("mytest");
}

private static Assembly CurrentDomain_TypeResolve(object sender,
ResolveEventArgs args)
{
if (args.Name == "<dArray>e__FixedBuffer0")
{
return fixedBufTypeBuilder.CreateType().Assembly;
}
return null;
}
}

In the code above, the line
AppDomain.CurrentDomain.TypeResolve += new
ResolveEventHandler(CurrentDomain_TypeResolve);
Is used for the nested struct type. See:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?Feedba
ckID=98299

Below is my detailed steps when I write the code above (I think this may
help you if you encounter such cases again in future)
Step1. Compose a simple assembly with our target code:
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public unsafe struct SimpleType2
{
public double dScalar1;
public fixed double dArray[5];
public int iScalar1;
}

Step2. Compile the assembly, use .NET Reflector to open the output dll, and
use ILReader to parse the dll.
Step3. Translate the output of ILReader to our AssemblyBuilder,
TypeBuilder, DefineField codes
Step4. Run our AssemblyBuilder, TypeBuilder, DefineField codes and get the
resulting assembly dll.
Step5. Use .Net Reflector to open the dll, and compare its result with the
that of the dll in step2.

If you have any other concerns or questions, feel free to let me know.

Regards,
Jialiang Ge ([email protected], remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights
 
J

Jialiang Ge [MSFT]

Hi Marek,

Would you let me know what the "property grid" is? Do you mean the Property
window in Visual Studio IDE? How do you display the fixed field in it
currently?

Regards,
Jialiang Ge ([email protected], remove 'online.')
Microsoft Online Community Support

=================================================
Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
=================================================
 
M

Marek

Hi Jialiang
The property grid is essentially the standard .NET (Windows Forms) property
grid control which I would like the users to use to populate this fixed array
structure before calling into the FORTRAN DLL.

I have tried to extract the the data from the object using code such as this:

object data =
parameterValues[0].GetType().InvokeMember("dArray", BindingFlags.GetField,
null, parameterValues[0], null);

List<double> values = new List<double>();
double start =
(double)instance.GetType().GetField("FixedElementField").GetValue(instance);

fixed (double* pValue = (double*)(&start))
{
for (int i = 0; i < 5; i++)
values.Add(*(pValue + i));
}

But this is not giving me the values I am expecting. The first one is
correct, after that it all seems to go wrong!

If I could get the above to work, I might be able to attach a TypeConverter
to the field(?) so that it displays correctly in the .NET property grid.

Best regards

Marek
 
J

Jialiang Ge [MSFT]

Hello Marek,

The following solution may work, but I understand it's a very bad and
tricky one. I post it here simply to let you know that I am still
researching the issue and will find a better one soon.

object data = myVal.GetType().InvokeMember("dArray",
BindingFlags.GetField, null, myVal, null);
List<double> values = new List<double>();
double start =
(double)data.GetType().GetField("FixedElementField").GetValue(data);
double* startAddr = &start;

// I find the array's values start from (startAddr + 3) in your
struct SimpleType2
// and the value at (startAddr + 2) is dScalar1
for (int i = 3; i < 3 + 5; i++)
values.Add(*(startAddr + i));

Regards,
Jialiang Ge ([email protected], remove 'online.')
Microsoft Online Community Support

=================================================
Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
=================================================
 
J

Jialiang Ge [MSFT]

Hello Marek,

Based on my further researches, I find we can use the method:
Marshal.OffsetOf to get the correct field offset of the unmanaged form in
the managed struct/class. I write the following sample code which works
well on my side. Please try it and let me know if it helps in your .NET and
FORTRAN DLL interoperability scenario.

unsafe
{
SimpleType2 myVal;
myVal.dScalar1 = 3.0;
myVal.dArray[0] = 1.1;
myVal.dArray[1] = 1.2;
myVal.dArray[2] = 1.3;
myVal.dArray[3] = 1.4;
myVal.dArray[4] = 1.5;
myVal.iScalar1 = 4;

byte* pStruct = (byte*)&myVal;
IntPtr dArrayPtr = Marshal.OffsetOf(myVal.GetType(), "dArray");
pStruct += (int)dArrayPtr;
double* pValue = (double*) pStruct;
List<double> values = new List<double>();
for (int i = 0; i < 5; i++)
values.Add(*(pValue + i));
}

If you have any other concerns or questions, feel free to let me know.

Regards,
Jialiang Ge
Microsoft Online Community Support

=================================================
Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
=================================================
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top