Inside C# 4 Data Structs |
Written by Mike James | |||||||
Tuesday, 07 September 2010 | |||||||
Page 5 of 6
Manual MarshalingMarshalling works so well most of the time that there is a tendency to forget that it is doing anything at all. However, as soon as you hit something even slightly out of the ordinary you might be surprised at what happens when it stops working. For example, some API calls need you to pass a pointer to a pointer to struct. You already know how to pass a pointer to a struct – it’s just pass by ref - and this might lead you to believe that a simple modification will allow you to pass a pointer to that pointer. But things are more complicated than you might expect. Let’s look at this a step at a time. In the AVIFileCreateStream API call the last two parameters are passed as pointers to an IntPtr and a struct respectively:
[DllImport("avifil32.dll")] To use this API call you would use: result = AVIFileCreateStream(pFile, At this point, given our earlier examples, it would appear easy to take over the marshaling of the pointer to the struct and do it manually. For example, what could be wrong with changing the declaration to: [DllImport("avifil32.dll")] However, if you try to use it by passing the address of the pinned structure: GCHandle handle = GCHandle.Alloc(Sinfo, the result is a runtime error as shown in Figure 2. Protected memory runtime error
The reason is that while you are indeed passing a pointer to the start of the stuct, that struct is in managed memory and unmanaged code cannot access it without generating a protection error. What we are forgetting is that standard marshalling does much more for us than generate addresses to use as pointers. The default marshalling for all parameters passed by ref also makes a copy of the entire data in unmanaged memory before deriving a pointer. It then copies the unmanaged memory back to the managed type when the function ends. It isn’t difficult, and is indeed quite useful, to write a function that does the same job as default marshalling: private IntPtr MarshalToPointer( This simply returns an IntPtr to an area of the global heap that contains a copy of the data. The only problem with this function is that you have to remember to release the allocated heap memory after use. For example: IntPtr lpstruct = MarshalToPointer(Sinfo); works exactly like default marshalling. But don’t forget that lpstruct is itself still being marshalled as a pass-by-value integer. To copy the result back to the struct an additional function is required: private object MarshalToStruct( <ASIN:1449380344> <ASIN:0123745144> <ASIN:0321658701> <ASIN:0321741765> <ASIN:0470495995> <ASIN:0672330792> <ASIN:0521114292> |
|||||||
Last Updated ( Tuesday, 28 September 2010 ) |