从 C# 调用 Windows API 函数时,应信任哪个签名源:.NET Framework 源代码还是 PInvoke?
例如,这是来自 .NET Framework 源文件UnsafeNativeMethods.cs:
[DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)]
public static extern bool GetWindowRect(HandleRef hWnd,
[In, Out] ref NativeMethods.RECT rect);
这是来自 PInvoke.Net:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(HandleRef hwnd, out RECT lpRect);
哪个是此函数的正确/最佳签名? (其中只有一个具有
[return: MarshalAs(UnmanagementType.Bool)]
或[In, Out] ref
等)我注意到在 .NET Framework 源文件中,许多/大多数签名都有
ExactSpelling=true, CharSet=CharSet.Auto
,但在 PInvoke 上却没有。这是必需的吗?
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);
Which is the correct/best signature for this function? (only one of them has
[return: MarshalAs(UnmanagedType.Bool)]
, or[In, Out] ref
, etc.)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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
他们都会完成工作。给pinvoke猫剥皮的方法不止一种。特别针对此示例:
ExactSpelling=true
是一项优化,它避免 pinvoke 编组器查找GetWindowRectA
和GetWindowRectW
版本。对于这个特定的 API 函数来说,它们不存在,因为它不接受字符串参数。看到运行时的实际差异将是一个奇迹。CharSet=CharSet.Auto
始终是一个好主意,因为默认值 (Ansi) 效率很低。它恰好在这里没有任何区别,因为该函数不接受任何字符串参数。[In, Out]
是不必要的,因为这是 blittable 类型的默认值。这是一个昂贵的词,意味着 pinvoke 编组器可以直接传递指向托管内存的指针,不需要转换。尽可能高效。不过,与CharSet
的想法相同,明确它有助于创建自记录代码并记住处理异常情况。只能使用[In]
或[Out]
可能是一项重大优化,但这里不适用,因为它已经优化了。 Fwiw,[Out] 是正确的选择。out
与ref
,与上面的想法相同。使用out
更为正确,因为 API 实际上并不使用RECT
内的任何传入值。然而,它在运行时没有任何区别,因为 JIT 编译器总是初始化一个结构。[return: MarshalAs(UnmanagedType.Bool)]
是不必要的,它是 WindowsBOOL
的默认封送处理。不知道为什么 pinvoke.net 总是包含它。简而言之,两者都不是完美的,但它们都可以工作。这就是pinvoke的危害。
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 theGetWindowRectA
andGetWindowRectW
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 asCharSet
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
vsref
, same idea as above. Usingout
is more correct since the API doesn't actually use any passed-in values inside theRECT
. 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 WindowsBOOL
. 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.