When calling Windows API functions from C#, which source for signatures to trust: .NET Framework source code or PInvoke?
Asked Answered
B

1

6

For example, this is from .NET Framework source file UnsafeNativeMethods.cs:

[DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] 
public static extern bool GetWindowRect(HandleRef hWnd, 
    [In, Out] ref NativeMethods.RECT rect);

and this is from PInvoke.Net:

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(HandleRef hwnd, out RECT lpRect);
  1. Which is the correct/best signature for this function? (only one of them has [return: MarshalAs(UnmanagedType.Bool)], or [In, Out] ref, etc.)

  2. I've noticed that in .NET Framework source files many/most signatures have ExactSpelling=true, CharSet=CharSet.Auto, but on PInvoke they don't. Is this required?

Bondie answered 29/11, 2010 at 15:9 Comment(0)
C
20

They will both get the job done. There's just more than one way to skin a pinvoke cat. Specifically for this example:

  • ExactSpelling=true is an optimization, it avoids having the pinvoke marshaller looking for the GetWindowRectA and GetWindowRectW versions. They don't exist for this particular API function since it doesn't take a string argument. Seeing an actual difference in run time would be a miracle.

  • CharSet=CharSet.Auto is always a good idea since the default (Ansi) is so inefficient. It just so happens to not make any difference here since the function doesn't take any string arguments.

  • [In, Out] is unnecessary because that's the default for a blittable type. An expensive word that means that the pinvoke marshaller can directly pass a pointer to the managed memory, no conversion is required. As efficient as possible. Same idea as CharSet though, being explicit about it helps to create self-documenting code and to remember to deal with the unusual case. Being able to only use [In] or [Out] can be a significant optimization, just not here since it is already optimized. Fwiw, [Out] would have been the correct choice.

  • out vs ref, same idea as above. Using out is more correct since the API doesn't actually use any passed-in values inside the RECT. It doesn't however make any difference at runtime since the JIT compiler always initializes a struct anyway.

  • [return: MarshalAs(UnmanagedType.Bool)] is unnecessary, it is the default marshaling for a Windows BOOL. Not sure why pinvoke.net always includes it.

So in a nutshell, neither is perfect but they both will work. Such are the hazards of pinvoke.

Cutler answered 29/11, 2010 at 16:3 Comment(1)
FxCop will complain if you don't explicitly state the marshalling type where it is ambiguous. That is the case for bool (since there is Win32 BOOL, C++ bool and VARIANT_BOOL) or for strings.Utmost

© 2022 - 2024 — McMap. All rights reserved.