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