|
|
Comments and Discussions
|
|
 |

|
So, I am experimenting with this and added the creation of additional code to create corresponding dynamically-generated methods to serialize a structure TO a byte[] buffer (using the stobj opcode).
Now, I can serialize structures between the two dynamic methods (to and from a byte[] buffer) just fine using the dynamic methods. Structures round-trip between the methods with no problem. However, the binary layout of the serialized data is not the same as if I used Marshal.StructureToPtr(...).
Using Marshal.StructureToPtr(...), the binary layout seems to match the structure's layout just fine. But the dynamic code seems to use a reordered form of the structure's fields.
I have the structure decorated with the [StructLayout( LayoutKind.Sequential)] attribute.
I have no idea why this attribute appears to be getting ignored by the stobj/ldobj opcodes.
Anyone else have a similar issue?
Also, TypeExtensions.ThrowIfNotUnmanagedType(...) will throw an exception for byte[] types, even though the comments in the code say that fixed-sized arrays of unmanaged types shoulg be allowed. I got around it by changing the first if() to the following:
if( type.IsArray )
{
if( type.GetElementType().IsValueType == false )
{
throw new ArgumentException( String.Format( "Type {0} is not an unmanaged type", type.FullName ) );
}
}
else if ((!type.IsValueType && !type.IsPointer) || type.IsGenericType || type.IsGenericParameter )
throw new ArgumentException(String.Format("Type {0} is not an unmanaged type", type.FullName));
(I do not yet know of a way to determine if an array's size is fixed or not from its Type data.)
Peace!
-=- James
Please rate this message - let me know if I helped or not!
If you think it costs a lot to do it right, just wait until you find out how much it costs to do it wrong! Remember that Professional Driver on Closed Course does not mean your Dumb Ass on a Public Road! Watch out for motorcyclists!
|
|
|
|

|
To check fixed arrays you can use fieldInfo.GetCustomAttributes(typeof (FixedBufferAttribute), false). Take a look at TimeSeriesDb's ValidateNoRefStruct(...)[^]. The timeseries database library works fine with fixed size buffers - the unit tests for _FixedByteBuff7 specifically test for that.
|
|
|
|

|
Thanks. However, it seems that the above code will look for fields declared as fixed (i.e. unsafe), not for fields that use [MarshalAs( UnmanagedType.ByValArray, SizeConst = n )], which is what I am trying to look for. I suppose I could just add code to check for and examine the properties of MarshalAsAttribute on the array type. Although custom attributes do not seem to exist on the type when it gets down where this code is being executed - calling type.GetCustomAttributes( false ) always returns an empty collection when called on the byte[] type as declared in the example structure below. Granted, I am not that good with type information and reflection, but it seems that it should work...
Anyway - no idea about the fields getting reordered? It seems to work correctly if I manually lay out the structures using LayoutKind.Explicit, but then arrays break down if the alignment is not quite right.
For example, if I have a structure defined as:
[StructLayout( LayoutKind.Explicit)]
public struct TestStruct
{
[FieldOffset( 0 )]
public Byte Field1;
[FieldOffset( 1 )]
public UInt16 Field2;
[FieldOffset( 3 )]
public UInt32 Field3;
[FieldOffset( 7 )]
public UInt64 Field4;
[FieldOffset( 15 )]
public SByte Field5;
[FieldOffset( 16 )]
public Int16 Field6;
[FieldOffset( 18 )]
public Int32 Field7;
[FieldOffset( 22)]
public Int64 Field8;
[FieldOffset( 30 )]
public Single Field9;
[FieldOffset( 34 )]
public Double Field10;
[FieldOffset( 48 )]
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 32 )]
public byte[] Field11;
}
Then the structure works, serializes the same as StructureToPtr(...), and round-trips correctly. But if I change the offset of Field11 to a non-aligned value of 42 [I wrote 43 originally, but both fail], which is where it really starts at in the binary stream, a TypeLoadException is thrown. It seems to work if I break up the arrays into multiple single-element fields, but this gets sloppy with lots of arrays, and introduces overhead when converting the byte data back into strings. (N.b. the structures I am reading may contain data Unicode or ANSI strings depending on other fields in the structure, so I cannot declare a CharSet for the entire structure.)
BTW: I am going to look into what it would take to be able to release my changes to create a "ToBytes" version of the dynamic code and the offset optimization.
-=- James
Please rate this message - let me know if I helped or not!
If you think it costs a lot to do it right, just wait until you find out how much it costs to do it wrong! Remember that Professional Driver on Closed Course does not mean your Dumb Ass on a Public Road! Watch out for motorcyclists!
modified 3 Oct '12 - 13:02.
|
|
|
|

|
My example never supported non-fixed arrays -- what you have is a struct with a reference to another object (array), which means it's not a single continuous memory block that can be copied directly to disk or read from it, but has to be marshaled instead. [MarshalAs] attribute does not change struct's memory layout, only how marshaling functions work with it. On the other hand, the fixed keyword, [FieldOffset] and [StructLayout] attributes do change memory layout, and that's what is being used for direct copying.
|
|
|
|

|
OK - I get that part now. I misread the comment in TypeExtensions.ThrowIfNotUnmanagedType(...): "Fixed-size arrays of unmanaged-types are allowed" - I interpreted "fixed-size" to include array types decorated with MarshalAs-SizeConst, not only as arrays declared with the fixed modifier. Sorry.
I think I have a handle on exactly what this code can and cannot do now, WRT structure members, layout and arrays.
Thanks!
Peace!
-=- James
Please rate this message - let me know if I helped or not!
If you think it costs a lot to do it right, just wait until you find out how much it costs to do it wrong! Remember that Professional Driver on Closed Course does not mean your Dumb Ass on a Public Road! Watch out for motorcyclists!
|
|
|
|

|
currently I am getting some byte data(market data) on one udp port and i save these bytes in message queue.
using program below i read message and convert into its structure and then save into database (MS sqlserver).
when i start this program it gets unresponsive within 1 min.
when i read these message queue using single thread it works longer time but very slow manner and suddenly unknown error occurs..
kindly you please let me about the solution so that i could consume the queue continuously for 4 hours..
thanks in advace..
public partial class Frmrcv: Form
{
private MessageQueue messageQueue;
private bool isRunning;
public Frmrcv()
{
InitializeComponent();
InitializeQueue();
}
private void InitializeQueue()
{
receivedCounter = 0;
string queuePath = @".\private$\myquelocal";
if (!MessageQueue.Exists(queuePath))
{
messageQueue = MessageQueue.Create(queuePath);
}
else
{
messageQueue = new MessageQueue(queuePath);
}
isRunning = true;
messageQueue.Formatter = new BinaryMessageFormatter();
messageQueue.ReceiveCompleted += OnReceiveCompleted;
messageQueue.BeginReceive();
}
private delegate void LogMessageDelegate(string text);
private void LogMessage(string text)
{
if (InvokeRequired)
{
Invoke(new LogMessageDelegate(LogMessage), text);
}
else
{
messageTextBox.AppendText(text + Environment.NewLine);
}
}
private int receivedCounter;
private void OnReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
{
try
{
MessageQueue mq = (MessageQueue)source;
if (mq != null)
{
try
{
System.Messaging.Message message = null;
try
{
message = mq.EndReceive(asyncResult.AsyncResult);
}
catch (Exception ex)
{
LogMessage(ex.Message);
}
if (message != null)
{
byte[] buffer = (byte[])message.Body;
if (buffer.Length > 0)
{
receivedCounter++;
if ((receivedCounter % 1) == 0)
{
TBCastMessageHeader bcastHeader = new TBCastMessageHeader();
IntPtr bcastHeaderPtr;
bcastHeaderPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TBCastMessageHeader)));
Marshal.Copy(buffer, 0, bcastHeaderPtr, buffer.Length);
bcastHeader = (TBCastMessageHeader)(Marshal.PtrToStructure(bcastHeaderPtr, typeof(TBCastMessageHeader)));
//MessageBox.Show(bcastHeader.MessageCode.ToString());
try
{
LogMessage(bcastHeader.MessageCode.ToString());
string ChngSign = "";
switch (bcastHeader.MessageCode)
{
case 1001: //usp_Insert1001data
TMarketUpdateMsg marketupdMsg;
IntPtr marketPtr;
marketPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TMarketUpdateMsg)));
Marshal.Copy(buffer, 0, marketPtr, buffer.Length);
marketupdMsg = (TMarketUpdateMsg)(Marshal.PtrToStructure(marketPtr, typeof(TMarketUpdateMsg)));
////*MessageBox.Show(marketupdMsg.BCastMessageHeader.ToString());
try
{
for (int i = 0; i < marketupdMsg.NoOfRecs; i++)
{
LogMessage(marketupdMsg.BCastMessageHeader.TimeStamp.ToString());
}
}
catch (Exception ex) { LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(marketPtr); GC.Collect(); }
break;
case 1037:
TBSETouchLineMsg BSEtouchData;
IntPtr bsetouchPtr;
bsetouchPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TBSETouchLineMsg)));
Marshal.Copy(buffer, 0, bsetouchPtr, buffer.Length);
BSEtouchData = (TBSETouchLineMsg)(Marshal.PtrToStructure(bsetouchPtr, typeof(TBSETouchLineMsg)));
try
{
for (int i = 0; i < BSEtouchData.NoOfRecs; i++)
{
LogMessage(BSEtouchData.BCastMessageHeader.TimeStamp.ToString());
}
}
catch (Exception ex) { LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(bsetouchPtr); GC.Collect(); }
break;
case 1000:
TtouchLineMsg touchMsg;
IntPtr touchPtr;
touchPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TtouchLineMsg)));
Marshal.Copy(buffer, 0, touchPtr, buffer.Length);
touchMsg = (TtouchLineMsg)(Marshal.PtrToStructure(touchPtr, typeof(TtouchLineMsg)));
//touchMsg.TouchLineInfo.Length.ToString();
try
{
for (int k = 0; k < touchMsg.NoOfRecs; k++)
{
LogMessage(touchMsg.TouchLineInfo[k].ExchangeCode.ToString());
}
}
//MessageBox.Show("done");
catch (Exception ex) { LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(touchPtr); GC.Collect(); }
break;
case 1068: //usp_Insert1068data
TlastRegularTradeData lastTradeData;
IntPtr tradePtr;
tradePtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TlastRegularTradeData)));
Marshal.Copy(buffer, 0, tradePtr, buffer.Length);
lastTradeData = (TlastRegularTradeData)(Marshal.PtrToStructure(tradePtr, typeof(TlastRegularTradeData)));
//*MessageBox.Show("type:" + lastTradeData.MarketType.ToString());
try
{
LogMessage(lastTradeData.BCastMessageHeader.TimeStamp.ToString());
}
catch (Exception ex) { LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(tradePtr); GC.Collect(); }
break;
case 1064: //usp_insert1064data
TBCastMarketStatistics BcastmarktStatics;
IntPtr bcastMrktStatPtr;
bcastMrktStatPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TBCastMarketStatistics)));
Marshal.Copy(buffer, 0, bcastMrktStatPtr, buffer.Length);
BcastmarktStatics = (TBCastMarketStatistics)(Marshal.PtrToStructure(bcastMrktStatPtr, typeof(TBCastMarketStatistics)));
//*MessageBox.Show("type:" + BcastmarktStatics.ExchangeCode.ToString());
try
{
LogMessage(BcastmarktStatics.ExchangeCode.ToString());
}
catch (Exception ex) { LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(bcastMrktStatPtr); GC.Collect(); }
break;
case 1065: //usp_Insert1065data
TBCastSymbolStatistics BcastsymbolStatics;
IntPtr BcastsymbolPtr;
BcastsymbolPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TBCastMarketStatistics)));
Marshal.Copy(buffer, 0, BcastsymbolPtr, buffer.Length);
BcastsymbolStatics = (TBCastSymbolStatistics)(Marshal.PtrToStructure(BcastsymbolPtr, typeof(TBCastSymbolStatistics)));
//*MessageBox.Show("Ex:" + BcastsymbolStatics.ExchangeCode.ToString());
try
{
LogMessage(BcastsymbolStatics.ExchangeCode.ToString());
}
catch (Exception ex) { LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(BcastsymbolPtr); GC.Collect(); }
break;
case 1024: //usp_Insert1024data
TMarketStatisticsMsg MarketStatMsg;
IntPtr MarketMsgPtr;
MarketMsgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TMarketStatisticsMsg)));
Marshal.Copy(buffer, 0, MarketMsgPtr, buffer.Length);
MarketStatMsg = (TMarketStatisticsMsg)Marshal.PtrToStructure(MarketMsgPtr, typeof(TMarketStatisticsMsg));
try
{
for (int i = 0; i < MarketStatMsg.NoOfRecords; i++)
{
LogMessage(MarketStatMsg.MarketStatisticsDetails[i].ExchangeCode.ToString());
}
}
catch (Exception ex) { LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(MarketMsgPtr); GC.Collect(); }
break;
case 1066: //usp_Insert1066data
TDFMIndexUpdateMsg DFMIndexupdate;
IntPtr DFMupdatePtr;
DFMupdatePtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TDFMIndexUpdateMsg)));
Marshal.Copy(buffer, 0, DFMupdatePtr, buffer.Length);
DFMIndexupdate = (TDFMIndexUpdateMsg)(Marshal.PtrToStructure(DFMupdatePtr, typeof(TDFMIndexUpdateMsg)));
//*MessageBox.Show("Ex:" + DFMIndexupdate.IndexInfo[0].ExchangeCode.ToString());
try
{
string indextype = "";
for (int i = 0; i < DFMIndexupdate.NoOfRecords; i++)
{
LogMessage( indextype);//DFMIndexupdate.IndexInfo[i].IndexType[i].ToString());
}
}
catch (Exception ex) { LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(DFMupdatePtr); GC.Collect(); }
break;
case 1026: //usp_Insert1026data
TMarketMBOUpdateMsg MarketMBOupdate;
IntPtr MarketMBoPtr;
MarketMBoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TMarketMBOUpdateMsg)));
Marshal.Copy(buffer, 0, MarketMBoPtr, buffer.Length);
MarketMBOupdate = (TMarketMBOUpdateMsg)(Marshal.PtrToStructure(MarketMBoPtr, typeof(TMarketMBOUpdateMsg)));
//*MessageBox.Show("rec:" + MarketMBOupdate.NoOfRecs.ToString());
try
{
for (int i = 0; i < MarketMBOupdate.NoOfRecs; i++)
{
LogMessage(MarketMBOupdate.BCastMessageHeader.TimeStamp.ToString());
}
}
catch (Exception ex) { LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(MarketMBoPtr); GC.Collect(); }
break;
//case 1043:
//TSGXTradingSessionStatusResponse SGXTResponse;
//Marshal.FreeHGlobal(SGXTPtr);
// break;
}
}
//string messageText = string.Format("Received {0} messages", receivedCounter);
catch (Exception ex)
{ LogMessage(ex.Message); }
finally { Marshal.FreeHGlobal(bcastHeaderPtr); GC.Collect(); }
}
}
}
}
finally
{
if (isRunning)
{
mq.BeginReceive();
}
}
}
return;
}
catch (Exception exc)
{
LogMessage(exc.Message);
}
}
[ StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 0x1)]
public struct TBCastMessageHeader
{
public ushort MessageCode;
public uint TimeStamp;
public ushort MessageLength;
public sbyte NumberOfDecimals;
public ushort Reserved;
}
[ StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 0x1)]
public struct TMarketUpdateMsg
{
public TBCastMessageHeader BCastMessageHeader;
public byte NoOfRecs;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
public TMarketInfo[] MarketInfo;//array [0..MARKET_PER_PACKET - 1] of TMarketInfo;
}
[ StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 0x1)]
public struct TMarketInfo
{
public TtouchLineInfo TouchLineInfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public TMarketDepthInfo[] MarketDepthInfo;//: array [0..MAX_MARKET_DEPTH - 1] of TmarketDepthInfo;
}
// other structures also here...
}
|
|
|
|

|
Hi,
this is really a nice piece of work to bridge the unmanaged/managed gap for serialized data in the fastest way possible. Keep up the good work.
Yours,
Alois Kraus
|
|
|
|

|
Thanks
|
|
|
|

|
BinaryReader is reading from a stream, and doing a lot
of bookkeeping. Incrementing offsets, etc.
Also, it will be thrashing the cache fairly often.
Your example just hits the same chunk of data in memory
repeatedly.
To be fair, you should allocate an array, and step through
it.
Also, CreateDynamicMethods(out interfaceObj, out staticDelegate) Should be split into two functions if possible.
Since the generated interface version is faster, why would anyone need or want the static interface?
A function would be preferred. This way I can instantiate a private shared reference to the FastReader with one line of code.
Last, an extension method on File would be nice.
Something like File.ReadAllStructures(FileName)(Of T) as T()
|
|
|
|

|
* I used the sample from the original article, so it might not be as optimized. At some point will have to review the code to make the perf more accurate.
* Interface is faster, but less flexible. I, in my code, prefer to use the static method because of the accessibility issue you had - static methods created with new DynamicMethod() object can be created to ignore class/method accessibility rules. See TimeSeriesDB/DynamicCodeFactory.cs for an example (fast file-access project).
* Yes, splitting might make sense for the production code, but this is a sample... Let me think about it
* As for the File extension method - I think that would be the wrong approach that very few would use - you might have file header first (as in my project above), you might be dealing with the network or other stream, and lastly - you do not know how many items you will need to read - calculating it from the stream size would have to process a number of errors.
|
|
|
|

|
An unhandled exception of type 'System.TypeLoadException' occurred in mscorlib.dll
Additional information: Access is denied: 'ReadingStructureData.ICall`1[MyProj.MyClass]'.
This is a simple type with a 3 int32's 1 int16 and
a signed byte..
Any ideas.
Calling your lib from VB if that makes a difference.
Thanks in advance.
|
|
|
|

|
You must make your struct public, otherwise the dynamic code generated by the sample will not be able to access it. At some point i will have to fix this by using the dynamicMethods from .NET 2.0 - they have an option to ignore access restrictions.
|
|
|
|

|
You must be psychic..
I had not even thought to check it!
|
|
|
|

|
Looking at the CIL for (byte* packet = data) vs (byte* packet = &data[0]) shows a big difference indeed. However, I believe that in the first case, the compiler is generating code to perform error checking.
* Check for a null reference on the array.
L_0001: dup
L_0002: stloc.2
L_0003: brfalse.s L_000a
* Check to make sure array is not zero length.
L_0006: ldlen
L_0007: conv.i4
L_0008: brtrue.s L_000f
If you instead use (&data[0]), you are assuming that a valid array has been passed to the method. These error checks should still be performed elsewhere in your application, so the "extra" CIL code will simply be moved to another location. Without these you are asking for a null reference exception, buffer overflow, or out of bounds exception.
|
|
|
|

|
Makes sense, although they should have made a comment about the difference in behavior. Does the pointer become null in case the array is null or empty? I will keep parameter validation outside of the dynamic method, keeping generated code to the minimum. Thanks!
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
This article shows how to generate dynamic methods for fast byte to structure conversion
| Type | Article |
| Licence | MIT |
| First Posted | 17 Feb 2009 |
| Views | 21,867 |
| Downloads | 187 |
| Bookmarked | 30 times |
|
|