Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating a struct at run-time based on data types defined in a string

Tags:

c#

.net

How can I generate a struct at run-time from a sequence of types defined in a string?

I have a header that e.g. contains "float, float, byte[255]". I have binary data that follows this header and keeps the data in sequence. I don't know the data type until I read the header at run-time, and I would like to generate a struct that I can used to Marshal the binary data into an array of structs.

Any ideas?

like image 360
lejon Avatar asked Dec 09 '25 12:12

lejon


1 Answers

Obviously you know what the requirements of your application are, but as lejon stated the problem is how to access the struct once you create it. In c# you will have to jump through hoops, as opposed to vb where you don't need to explicitly declare types.

I can think of several ways to accomplish what you want. You could use CodeDom to generate your code, see this http://www.15seconds.com/issue/020917.htm

Personally for a simply struct, I would constuct the code mandually

  string szCode =@"using System;
  using System.Windows.Forms;

  namespace RunTimeCompile
  {
      [StructLayoutAttribute(LayoutKind.Sequential)]
      public string MyStruct
      {"

Then for each data type in your head file append the member to the szCode String. (You'll need to build a basic function to parse your types this):

szCode += "float Field1;\n"; 
szCode += "float Field2;\n"; 
szCode += "byte[255] Field3;\n";

and close off your code...

szeCode+=";\n;\n";

now that you have the source code use CodeDom to compile it.

oCodeDomProvider = CodeDomProvider.CreateProvider("CSharp");
// Add what referenced assemblies
CompilerParameters oCompilerParameters = new CompilerParameters();
oCompilerParameters.ReferencedAssemblies.Add("system.dll");
// set the compiler to create a DLL
oCompilerParameters.GenerateExecutable = false;
// set the dll to be created in memory and not on the hard drive
oCompilerParameters.GenerateInMemory = true;
oCompilerResults =
  oCodeDomProvider.CompileAssemblyFromSource(oCompilerParameters, szCode);

Note: You could also compile from a file as opposed to in memory source code using CompileAssemblyFromFile

Now before continuing check there are no compilation errors

if (oCompilerResults.Errors.Count!=0) return; // add you own error handling

now you can retrieve an instance of the dynamically generated struct like this

oAssembly = oCompilerResults.CompiledAssembly;
oObject = oAssembly.CreateInstance("RunTimeCompile.MyStruct");
oType = oObject.GetType();

Now you can read all of the rows of your binary file and Marshal them into an array of MyStruct[] using Marshal or any other method your would like to serialize the binary data (binary serializer perhaps).

For example using Runtime.Interop.Marshal you could do something like this (you'll need to fiddle with this a bit, specifically you can't declare MyStruct[] in your code so you need to do something like oObjectArray = oAssembly.CreateInstance("RunTimeCompile.MyStruct[]") to declare the destValues result array):

byte[] sourceData = ReadSourceData(); // TODO: generate method to load your BLOB
MyStruct[] destValues = new MyStruct[Marshal.SizeOf(oType) + 1]
int arrayIndex = 0;

GCHandle handle = GCHandle.Alloc(sourceData, GCHandleType.Pinned);
try
{
IntPtr buffer = handle.AddrOfPinnedObject();
buffer = (IntPtr)(buffer.ToInt32() +
(arrayIndex*Marshal.SizeOf(typeof(MyStruct))));
destStruct = (MyStruct)Marshal.PtrToStructure(buffer, typeof(MyStruct));
}
finally
{
handle.Free();
}

 return MyStruct;

Now you have your array of struct.

like image 124
Alexander Higgins Avatar answered Dec 11 '25 22:12

Alexander Higgins



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!