Reputation: 1125
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?
Upvotes: 3
Views: 9191
Reputation: 6923
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.
Upvotes: 3
Reputation: 46098
The problem is, once you've defined your struct, how would you access it? You can't reference it statically, as the type is runtime only. You would either have to use reflection, delegates, or dynamic
. Is there a better way of doing what you want (say, a single class that takes the byte array and interprets it as you need) rather than generating a struct at runtime?
Upvotes: 0