Inside C# 4 Data Structs
Written by Mike James   
Tuesday, 07 September 2010
Article Index
Inside C# 4 Data Structs
Being exact
The API
Serialising structs
Manual mashaling
Struct-to-pointer

 

Banner

 

Now that we have mastered the manual marshaling of a simple pointer to a struct the next step is a pointer to a pointer to a struct. Surprisingly this requires nothing new because the struct-to-pointer function will actually convert any data type to an unmanaged pointer – including a pointer.

The  function AVISaveOption is a suitable example as it needs two pointers to pointers as parameters:

[DllImport("avifil32.dll")]
extern static int AVISaveOptions(
IntPtr hWnd,
int uiFlags,
int noStreams,
IntPtr ppavi,
IntPtr ppOptions);

In fact the ppavi parameter is a pointer to a handle (which is itself a pointer) and the ppOptions is a pointer to a pointer to a struct. To call this function we first need the struct:

AVICOMPRESSOPTIONS opts = new 
AVICOMPRESSOPTIONS();

You can look up the definition of the structure in the standard AVI documentation.

Next we need the marshaled pointer to the struct:

IntPtr lpstruct = MarshalToPointer(opts);

and then the pointer to the pointer:

IntPtr lppstruct = MarshalToPointer(
lpstruct);

followed by the pointer to the handle:

IntPtr lphandle = MarshalToPointer(
pStream);

The call to the API function is now simple:

result = AVISaveOptions(
m_hWnd,
ICMF_CHOOSE_KEYFRAME |
ICMF_CHOOSE_DATARATE,
1,
lphandle,
lppstruct);

where the other parameters and constants aren’t of any great interest to us and you can find more details in the API’s documentation.

When the function completes all that is left to do is transfer the data in the unmanaged buffer back into the managed struct:

opts = (AVICOMPRESSOPTIONS) 
MarshalToStruct(lpstruct,
typeof(AVICOMPRESSOPTIONS));

You have to be careful to use the pointer to the struct and not the pointer to the pointer!

Finally we can free all of the unmanaged memory we used:

Marshal.FreeHGlobal(lpstruct);
Marshal.FreeHGlobal(lppstruct);
Marshal.FreeHGlobal(lphandle);

This might all seem complicated. Using pointers-to-pointers is never an easy thing to do and it is one of the reasons that C# makes sure that when you do use pointers then you mark the code as unsafe. However you might like to contemplate just how safe this sort of juggling is and all without an unsafe block in sight.

On the other hand the general principles are very simple. When you pass anything by ref to an API it has to be copied to unmanaged memory and the address of this memory is passed to the function. Normally default marshaling takes care of this and you can ignore it - but it still happens. If you need to go beyond what is provided by the marshaling attributes then you have to perform this copying explicitly.

Banner


Deep C# - Casting the Escape from Strong Typing

Casting is one of the most confusing aspects of any modern language and it often makes beginners think hard. But if you know why you are doing it, then the how makes a lot more sense. We have encounte [ ... ]



Deep C# - Interface

Interfaces - what are they for? Not quite inheritance yet they seem to fit the same purpose. Find out in this extract from my new book, Deep C#: Dive Into Modern C#.


Other Articles

<ASIN:0321658701>
<ASIN:1451531168>

<ASIN:0321637003>

<ASIN:0596800959>

<ASIN:047043452X>

<ASIN:0123745144>



Last Updated ( Tuesday, 28 September 2010 )