DllImport 与 VB.NET 中的声明
Posted
技术标签:
【中文标题】DllImport 与 VB.NET 中的声明【英文标题】:DllImport vs Declare in VB.NET 【发布时间】:2010-10-30 12:57:14 【问题描述】:我注意到在 MSDN 文档中,有 multiple ways 用于声明从 VB.NET 程序中对外部 DLL 中的函数的引用。
令人困惑的是,MSDN 声称您只能将 DllImportAttribute 类与共享函数原型“in rare cases”一起使用,但我找不到该声明的解释,而您可以简单地使用 @987654324 @ 关键字。
为什么这些不同,我应该在哪里适当地使用每种情况?
【问题讨论】:
【参考方案1】:Declare 实际上是维护P/Invoke 语法的一种尝试,转换为VB.NET 的Visual Basic 6.0 用户会更熟悉这种语法。它具有许多与 P/Invoke 相同的功能,但某些类型(尤其是字符串)的编组非常不同,并且可能会给更熟悉 DllImport 规则的人带来一些混淆。
我不完全确定文档所暗示的“罕见”区别是什么。我经常从 VB.NET 和 C# 在我的代码中使用 DllImport,没有问题。
一般来说,我会使用 DllImport 而不是 Declare,除非您具有 Visual Basic 6.0 背景。 DllImport 的文档和示例要好得多,并且有许多工具旨在生成 DllImport 声明。
【讨论】:
【参考方案2】:在我看来,因为从我搜索的内容来看,这个关键字看起来并没有被弃用等等,所以只需使用编译时关键字而不是属性。
另外,当您使用Declare
时,您无需编写End Function
。这样做的好处是您可以逐行创建一个完整的函数导入声明模块,而无需使用DllImport
s 和End Function
s 编写代码。
当您使用Declare
关键字声明时,编译器无论如何都会将此函数视为Shared
,因此可以通过其他外部对象访问它。
但我认为在当前的 VB.NET 中,它们都针对相同的目标并且没有性能差异 - 对此没有任何保证。
所以我的结论是:一定要使用 Declare 而不是 DllImport,尤其是阅读您引用的 Microsoft stated 应该在极少数情况下使用它的内容。
【讨论】:
【参考方案3】:显然 Declare 和 DllImport 语句基本相同。你可以使用任何你喜欢的。
以下是对可能在每个方面的工作方式略有不同的几点的讨论,这可能会影响对另一个方面的偏好:
我从 MSDN 上一篇关于 Visual Studio 2003 的文章开始,标题为 Using the DllImport Attribute。 (有点老了,但由于 DllImport 语句似乎起源于 .NET,所以回到开头似乎是合适的。)
给出一个示例 DllImport 语句:
[DllImport("user32.dll", EntryPoint = "MessageBox", CharSet = Unicode)]
int MessageBox(void* hWnd, wchar_t* lpText, wchar_t* lpCaption, unsigned int uType);
它表示如果EntryPoint 值被遗漏,CLR 将查找函数的名称(在本例中为MessageBox)作为默认值。但是,在这种情况下,由于指定了 Unicode 的 CharSet,CLR 将首先查找名为“MessageBoxW”的函数 - 表示 Unicode 返回类型的“W”。 (ANSI 返回类型版本将是“MessageBoxA”。)如果没有找到“MessageBoxW”,则 CLR 将查找实际上称为“MessageBox”的 API 函数。
可以在此处找到有关 DllImportAttribute 类的当前详细信息,我在此处查看了 .NET Framework 4 版本:DLLImportAttribute Class
此 .NET Framework 4 页面的备注部分中的关键注释是:
您将此属性直接应用于 C# 和 C++ 方法定义;但是,当您使用 Declare 语句时,Visual Basic 编译器会发出此属性。
因此,在 VB.NET 中,使用 Declare
语句会导致编译器生成 DLLImportAttribute
。
本页还有一个重要说明:
DllImportAttribute 不支持泛型类型的编组。
因此,如果您想使用泛型类型,您似乎必须使用Declare
语句。
接下来,我前往Declare statement information。 Visual Studio 2010 版本(Visual Basic 语句信息)在这里:Declare Statement
这里的一个关键项目是这个注释:
您只能在模块级别使用 Declare。这意味着外部引用的声明上下文必须是类、结构或模块,不能是源文件、命名空间、接口、过程或块。
显然,如果您想在类、结构或模块之外设置 API 调用,则必须使用 DllImport 语句而不是 Declare
。
此页面上的示例Declare
语句为:
Declare Function getUserName Lib "advapi32.dll" Alias "GetUserNameA" (
ByVal lpBuffer As String, ByRef nSize As Integer) As Integer
以下是这个小信息:
DllImportAttribute 提供了在非托管代码中使用函数的另一种方法。下面的示例声明了一个导入的函数,而不使用 Declare 语句。
当然还有一个 DllImport 使用示例。
关于 Unicode 与 ANSI 结果,根据此 Declare 页面,如果您指定 CharSet 值(在 Declare 中可用,但在上面的示例中未显示),CLR 将执行与 DllImport 相同类型的自动名称搜索 - 对于Unicode 或 ANSI。
如果您没有在Declare
语句中指定 CharSet 值,那么您必须确保您在 Declare 中的函数名称与实际 API 函数的头文件中的函数名称相同,或者您必须指定一个Alias
值与头文件中的实际函数名匹配(如上例所示)。
我找不到任何特定的 Microsoft 文档说明在上述情况之外的任何情况下,DllImport 或 Declare 都比其他任何情况下更受青睐,甚至被推荐。
因此,我的结论是:
-
除非您需要将定义放在不能使用
Declare
语句的位置之一,否则任何一种技术都可以正常工作,
和
-
如果您使用的是 DllImport,请确保指定所需的 CharSet 值(Unicode 或 ANSI),否则您可能会得到意想不到的结果。
【讨论】:
这是一篇很棒的文章和很棒的研究。非常感谢! 请注意,“因此,至少对于 VB.NET,编译器最终会以 Declare 语句结束。”是倒退的:如果你使用Declare
语句,你最终会得到一个 DllImport
属性。
请注意,@NetMage,当我彻底研究了这个答案时,2012 年的情况可能不是。最新版本的 VB.NET 可能会有所不同。如果您将更改添加为 2022 年的更新,而不是更改原始声明,那就太好了。
@leanne 我的更改基于您回答中的文本,即“但是,当您使用 Declare 语句时,Visual Basic 编译器会发出此属性。”我读到的意思与您的以下陈述相反。【参考方案4】:
如果您需要设置以下选项之一,则使用DllImportAttribute
属性,否则使用Declare
。来自https://msdn.microsoft.com/en-us/library/w4byd5y4.aspx
要应用 BestFitMapping、CallingConvention、ExactSpelling、 PreserveSig、SetLastError 或 ThrowOnUnmappableChar 字段到 Microsoft Visual Basic 2005 声明,您必须使用 DllImportAttribute 属性而不是 Declare 语句。
仅从上述参考中不清楚这是否仅适用于“Visual Basic 2005”,因为上述参考来自 .NET 4.5 文章。但是,我还发现了这篇文章(https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute(v=vs.110).aspx),它是针对 .NET 4.5 中的 DllImportAttribute
类的:
当您使用 声明声明。 对于复杂的方法定义,包括 BestFitMapping、CallingConvention、ExactSpelling、PreserveSig、 SetLastError 或 ThrowOnUnmappableChar 字段,您应用此 属性直接归属于 Visual Basic 方法定义。
这告诉您Declare
选项是VB.net 语法糖,在编译时转换为DllImportAttribute
,并概述了建议直接使用DllImportAttribute
时的确切场景。
【讨论】:
以上是关于DllImport 与 VB.NET 中的声明的主要内容,如果未能解决你的问题,请参考以下文章
在 VB.NET 中制作一个我可以用作 DllImport 的 DLL