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。这样做的好处是您可以逐行创建一个完整的函数导入声明模块,而无需使用DllImports 和End Functions 编写代码。

当您使用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

VB.Net中该怎么调用windows的API?

使用 user32.dll 从 vb.net 中的 javascript 警报框中获取文本

vb.net2010中的隐式声明和显示声明分别是啥

VB.NET 中的线程安全变量

vb.net 全局键盘钩子