Поделиться через


All types of fun with marshaling!

There are several golden rules that I have learned over the last week while spending some time PInvoking Win32 functions from C#.

1) The content at PInvoke.net can often not be trusted.

2) The overwhelming precentage of articles on the net explaining how to PInvoke certain methods can also not be trusted.

The following are some helpful tricks and tips that I have learned.

Pay attention to whether the Win32 method returns BOOL or BOOLEAN.

The Win32 BOOL is 4 bytes, while a BOOLEAN is only one byte. Why do we care? We care because the .Net Boolean only maps cleanly to BOOL, not to BOOLEAN. The following is an example of how to marshal the TranslateName function.

[return:MarshalAs(UnmanagedType.U1)]

[DllImport("Secur32.dll", CharSet = CharSet.Unicode, SetLastError = true)]

private static extern Boolean TranslateName(

String lpAccountName,

EXTENDED_NAME_FORMAT AccountNameFormat,

EXTENDED_NAME_FORMAT DesiredNameFormat,

StringBuilder lpTranslatedName,

ref uint nSize);

 

Notice in particular the return: attribute specified above. This allows us to marshal back the returned BOOLEAN to the .Net Boolean. If you do not do this, I have noticed that the function will actually fail but will still to appear to return true. This can be very frustrating to debug.

When using EntryPoint, make sure the specify the ‘W’ version.

[DllImport("advapi32.dll", SetLastError = true, EntryPoint = "ChangeServiceConfig2W")]

private static extern bool ChangeServiceConfigDescription(

  IntPtr hService,

     uint dwInfoLevel,

      ref SERVICE_DESCRIPTION lpInfo);

This makes sense, but it is easy to forget. If I do not use the W version above, I will have only one character in the description.

If a string must be passed in a passed structure, it must be passed using an IntPtr.

See the definition of SERVICE_DESCRIPTION used above.

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]

private struct SERVICE_DESCRIPTION

{

    public IntPtr lpDescription;

}

To copy your managed string to the structure, use Marshal.StringToHGlobalUni. Just make sure to call Marshal.FreeHGlobal to free the memory once you no longer need it. The same applies when retrieving a string from a structure, except there you must use Marshal.PtrToStringUni.

Comments