我在 MSDN 文档中注意到,有多种方法可以在 VB.NET 程序中声明对外部 DLL 中函数的引用。
令人困惑的是,MSDN 声称您只能在“在极少数情况下”使用带有共享函数原型的DllImportAttribute类,但我找不到该声明的解释,而您可以简单地使用关键字来代替。Declare
为什么这些不同,我应该在哪里适当地使用每种情况?
显然 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 类
此 .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
不能使用语句的地方之一,否则任何一种技术都可以正常工作,和
Declare 确实是维护P/Invoke语法的一种尝试,这对于转换为VB.NET的 Visual Basic 6.0 用户来说会更熟悉。它具有许多与 P/Invoke 相同的功能,但某些类型(尤其是字符串)的编组非常不同,并且可能会给更熟悉 DllImport 规则的人带来一些混淆。
我不完全确定文档所暗示的“罕见”区别是什么。我经常从 VB.NET 和 C# 在我的代码中使用 DllImport 没有问题。
通常,除非您来自 Visual Basic 6.0 背景,否则我会使用 DllImport 而不是 Declare。DllImport 的文档和示例要好得多,并且有许多工具旨在生成 DllImport 声明。
在我看来,由于从我搜索的内容来看,这个关键字看起来并没有被弃用等,所以只需使用编译时关键字而不是属性。
此外,当您使用 时Declare
,您不需要编写End Function
. 这样做的好处是您可以逐行创建函数导入声明的整个模块,而无需使用DllImport
s 和End Function
s 来处理您的代码。
当您使用Declare
关键字声明时,编译器将此函数视为Shared
无论如何,因此可以通过其他外部对象访问它。
但我认为在当前的 VB.NET 中,它们都针对相同的目标并且没有性能差异——对此没有任何保证。
所以我的结论是:一定要使用 Declare 而不是 DllImport,尤其是阅读你引用的微软声明它应该在极少数情况下使用的内容。
如果您需要设置以下选项之一,则使用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),它特定DllImportAttribute
于 .NET 中的类4.5:
当您使用 Declare 语句时,Visual Basic 编译器会发出此属性。对于包括 BestFitMapping、CallingConvention、ExactSpelling、PreserveSig、SetLastError 或 ThrowOnUnmappableChar 字段的复杂方法定义,您可以将此属性直接应用于 Visual Basic 方法定义。
这告诉您该选项是在编译时Declare
转换为的 VB.net 语法糖,并概述了建议直接使用时的确切场景。DllImportAttribute
DllImportAttribute