DllImport vs VB.NET中的声明

时间:2021-04-18 21:37:02

I notice in the MSDN documentation that there are multiple ways to declare a reference to a function in an external DLL from within a VB.NET program.

我在MSDN文档中注意到有多种方法可以在VB.NET程序中声明对外部DLL中函数的引用。

The confusing thing is that MSDN claims that you can only use the DllImportAttribute class with Shared Function prototypes "in rare cases", but I couldn't find the explanation for this statement, while you can simply use the Declare keyword instead.

令人困惑的是,MSDN声称你只能在极少数情况下使用DllImportAttribute类和共享函数原型,但是我找不到这个语句的解释,而你可以简单地使用Declare关键字。

Why are these different, and where would I appropriately use each case?

为什么这些不同,我会在哪里适当地使用每个案例?

4 个解决方案

#1


Declare is really an attempt to maintain a P/Invoke syntax which would be more familiar to Visual Basic 6.0 users converting to VB.NET. It has many of the same features as P/Invoke but the marshalling of certain types, in particular strings, are very different and can cause a bit of confusion to people more familiar with DllImport rules.

声明实际上是一种维护P / Invoke语法的尝试,对于转换为VB.NET的Visual Basic 6.0用户来说,这种语法更为熟悉。它具有许多与P / Invoke相同的功能,但是某些类型的编组,特别是字符串,是非常不同的,并且可能对更熟悉DllImport规则的人造成一些混淆。

I'm not entirely sure what the documentation is alluding to with the "rare" distinction. I use DllImport in my code frequently from both VB.NET and C# without issue.

我并不完全确定文档中提到的“罕见”区别。我经常在VB.NET和C#的代码中使用DllImport而没有问题。

In general, I would use DllImport over Declare unless you come from a Visual Basic 6.0 background. The documentation and samples for DllImport are much better and there are many tools aimed at generating DllImport declarations.

一般情况下,我会使用DllImport而不是Declare,除非你来自Visual Basic 6.0背景。 DllImport的文档和示例要好得多,并且有许多工具旨在生成DllImport声明。

#2


Apparently the Declare and DllImport statements are basically the same. You can use whichever you prefer.

显然,Declare和DllImport语句基本相同。您可以使用您喜欢的任何一种。

Following is a discussion of the few points that may work a little differently in each, which may influence a preference for one over the other:

以下是对可能在每种方面略有不同的几点的讨论,这可能会影响对一方的偏好:

I started with an article from MSDN regarding Visual Studio 2003 titled Using the DllImport Attribute. (A bit old, but since the DllImport statement seems to have originated in .NET, it seemed appropriate to go back to the beginning.)

我从MSDN开始撰写一篇名为“使用DllImport属性”的Visual Studio 2003文章。 (有点旧,但由于DllImport语句似乎起源于.NET,因此回到开头似乎是合适的。)

Given an example DllImport statement of:

给出一个示例DllImport语句:

[DllImport("user32.dll", EntryPoint = "MessageBox", CharSet = Unicode)]
int MessageBox(void* hWnd, wchar_t* lpText, wchar_t* lpCaption, unsigned int uType);

It says that if the EntryPoint value is left out, the CLR will look for the name of the function (MessageBox, in this case) as a default. However, in this instance, since a CharSet of Unicode was specified, the CLR would FIRST look for a function called "MessageBoxW" - the 'W' indicating a Unicode return type. (The ANSI return type version would be "MessageBoxA".) If no "MessageBoxW" were found, THEN the CLR would look for an API function actually called "MessageBox".

它表示如果遗漏了EntryPoint值,CLR将查找函数的名称(在本例中为MessageBox)作为默认值。但是,在这种情况下,由于指定了Unicode的CharSet,因此CLR将首先查找名为“MessageBoxW”的函数 - 表示Unicode返回类型的“W”。 (ANSI返回类型版本将是“MessageBoxA”。)如果没有找到“MessageBoxW”,那么CLR将寻找实际上称为“MessageBox”的API函数。

Current specifics about the DllImportAttribute class can be found here, where I viewed the .NET Framework 4 version: DLLImportAttribute Class

关于DllImportAttribute类的当前细节可以在这里找到,我查看了.NET Framework 4版本:DLLImportAttribute类

A key comment in the Remarks section of this .NET Framework 4 page is that:

此.NET Framework 4页面的“备注”部分中的关键注释是:

You apply this attribute directly to C# and C++ method definitions; however, the Visual Basic compiler emits this attribute when you use the Declare statement.

您可以将此属性直接应用于C#和C ++方法定义;但是,当您使用Declare语句时,Visual Basic编译器会发出此属性。

So, at least as pertains to VB.NET, the compiler ends up with a Declare statement anyway.

因此,至少与VB.NET相关,编译器最终会以Declare语句结束。

There is also an important note in this page:

此页面中还有一个重要说明:

The DllImportAttribute does not support marshaling of generic types.

DllImportAttribute不支持泛型类型的编组。

So, it would appear that if you want to use a generic type, you'd have to use a Declare statement.

因此,看起来如果要使用泛型类型,则必须使用Declare语句。

Next, I headed to the Declare statement information. A Visual Studio 2010 version (Visual Basic statement info) was here: Declare Statement

接下来,我前往Declare声明信息。 Visual Studio 2010版本(Visual Basic语句信息)在这里:声明语句

A key item here was this note:

这里的一个关键项目是这个说明:

You can use Declare only at module level. This means the declaration context for an external reference must be a class, structure, or module, and cannot be a source file, namespace, interface, procedure, or block.

您只能在模块级别使用Declare。这意味着外部引用的声明上下文必须是类,结构或模块,并且不能是源文件,命名空间,接口,过程或块。

Apparently, if you want to set up an API call outside of a class, structure, or module, you'll have to use the DllImport statement instead of the Declare.

显然,如果要在类,结构或模块之外设置API调用,则必须使用DllImport语句而不是Declare。

The example Declare statement on this page is:

此页面上的示例Declare语句是:

Declare Function getUserName Lib "advapi32.dll" Alias "GetUserNameA" (
  ByVal lpBuffer As String, ByRef nSize As Integer) As Integer

Following that example is this little tidbit of information:

在这个例子之后是这个小小的信息:

The DllImportAttribute provides an alternative way of using functions in unmanaged code. The following example declares an imported function without using a Declare statement.

DllImportAttribute提供了在非托管代码中使用函数的替代方法。以下示例在不使用Declare语句的情况下声明导入的函数。

followed by, of course, an example of DllImport usage.

当然,后面是DllImport用法的一个例子。

Regarding Unicode vs ANSI results, according to this Declare page, if you specify a CharSet value (available in Declare, but not shown in the example above) the CLR will do the same type of automatic name search that DllImport does - for either Unicode or ANSI.

关于Unicode与ANSI结果,根据此Declare页面,如果指定CharSet值(在Declare中可用,但未在上面的示例中显示),CLR将执行与DllImport相同类型的自动名称搜索 - 对于Unicode或ANSI。

If you do not specify a CharSet value in the Declare statement, then you must make sure that your function name in the Declare is the same as the function name in the actual API function's header file, OR you must specifiy an Alias value that matches the actual function name in the header file (as shown in the example above).

如果未在Declare语句中指定CharSet值,则必须确保Declare中的函数名与实际API函数头文件中的函数名相同,或者必须指定与之匹配的Alias值。头文件中的实际函数名称(如上例所示)。

I was not able to find any specific Microsoft documentation stating that either DllImport or Declare were preferred, or even recommended, over one another in any situation other than those noted above.

我无法找到任何特定的Microsoft文档,声明DllImport或Declare在上述情况之外的任何情况下都是首选或甚至推荐的。

My conclusion, therefore, is:

因此,我的结论是:

1) Unless you need to place your definition in one of the places a Declare statement cannot be used, either technique will work fine,

1)除非你需要将你的定义放在其中一个地方,否则不能使用Declare语句,这两种技术都可以正常工作,

and

2) if you're using DllImport, make sure you specify the CharSet value you want (Unicode or ANSI), or you may get unexpected results.

2)如果您正在使用DllImport,请确保指定所需的CharSet值(Unicode或ANSI),否则可能会出现意外结果。

#3


In my opinion, since this keyword doesn't look deprected, etc. from what I searched, simply use compile-time keywords rather than attributes.

在我看来,由于这个关键字看起来并不像我搜索的那样,所以只需使用编译时关键字而不是属性。

Also, when you use the Declare, you don't need to write the End Function. The advantage of that is that you can create a whole module of declarations of function imports line by line, with no need to pulute your code with DllImports and End Functions.

此外,当您使用Declare时,您不需要编写End Function。这样做的好处是,您可以逐行创建函数导入声明的整个模块,而无需使用DllImports和End Functions来清空代码。

When you declare using the Declare keyword, the compiler treats this function as Shared anyway, so it can be accessed via other extenal objects.

当您声明使用Declare关键字时,编译器无论如何都会将此函数视为Shared,因此可以通过其他extenal对象访问它。

But I think in the current VB.NET they're both addressed to the same target and no performance difference - no warranty on this one.

但我认为在目前的VB.NET中,它们都是针对相同的目标而没有性能差异 - 对此没有保证。

So my conclusion is: Do use the Declare instead of DllImport, especially reading what you quoted that Microsoft stated that it should be used in rare cases.

所以我的结论是:使用Declare而不是DllImport,特别是阅读你引用的内容,微软声称它应该在极少数情况下使用。

#4


If you need to set one of the following options, then use DllImportAttribute attribute, else use Declare. From https://msdn.microsoft.com/en-us/library/w4byd5y4.aspx

如果需要设置以下选项之一,则使用DllImportAttribute属性,否则使用Declare。来自https://msdn.microsoft.com/en-us/library/w4byd5y4.aspx

To apply the BestFitMapping, CallingConvention, ExactSpelling, PreserveSig, SetLastError, or ThrowOnUnmappableChar fields to a Microsoft Visual Basic 2005 declaration, you must use the DllImportAttribute attribute instead of the Declare statement.

若要将BestFitMapping,CallingConvention,ExactSpelling,PreserveSig,SetLastError或ThrowOnUnmappableChar字段应用于Microsoft Visual Basic 2005声明,必须使用DllImportAttribute属性而不是Declare语句。

It is unclear from the above reference only whether this applies to only "Visual Basic 2005" or not, as the above reference is from a .NET 4.5 article. However, I also found this article (https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute(v=vs.110).aspx ) which is specific to the DllImportAttribute class in .NET 4.5 :

从上面的参考文献中不清楚这是否仅适用于“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:

the Visual Basic compiler emits this attribute when you use the Declare statement. For complex method definitions that include BestFitMapping, CallingConvention, ExactSpelling, PreserveSig, SetLastError, or ThrowOnUnmappableChar fields, you apply this attribute directly to Visual Basic method definitions.

使用Declare语句时,Visual Basic编译器会发出此属性。对于包含BestFitMapping,CallingConvention,ExactSpelling,PreserveSig,SetLastError或ThrowOnUnmappableChar字段的复杂方法定义,可以将此属性直接应用于Visual Basic方法定义。

This tells you that the Declare option is VB.net syntactical sugar which is converted to DllImportAttribute at compile time, and outlines the exact scenarios when using DllImportAttribute directly is recommended.

这告诉您Declare选项是VB.net语法糖,它在编译时转换为DllImportAttribute,并概述了建议直接使用DllImportAttribute时的确切方案。

#1


Declare is really an attempt to maintain a P/Invoke syntax which would be more familiar to Visual Basic 6.0 users converting to VB.NET. It has many of the same features as P/Invoke but the marshalling of certain types, in particular strings, are very different and can cause a bit of confusion to people more familiar with DllImport rules.

声明实际上是一种维护P / Invoke语法的尝试,对于转换为VB.NET的Visual Basic 6.0用户来说,这种语法更为熟悉。它具有许多与P / Invoke相同的功能,但是某些类型的编组,特别是字符串,是非常不同的,并且可能对更熟悉DllImport规则的人造成一些混淆。

I'm not entirely sure what the documentation is alluding to with the "rare" distinction. I use DllImport in my code frequently from both VB.NET and C# without issue.

我并不完全确定文档中提到的“罕见”区别。我经常在VB.NET和C#的代码中使用DllImport而没有问题。

In general, I would use DllImport over Declare unless you come from a Visual Basic 6.0 background. The documentation and samples for DllImport are much better and there are many tools aimed at generating DllImport declarations.

一般情况下,我会使用DllImport而不是Declare,除非你来自Visual Basic 6.0背景。 DllImport的文档和示例要好得多,并且有许多工具旨在生成DllImport声明。

#2


Apparently the Declare and DllImport statements are basically the same. You can use whichever you prefer.

显然,Declare和DllImport语句基本相同。您可以使用您喜欢的任何一种。

Following is a discussion of the few points that may work a little differently in each, which may influence a preference for one over the other:

以下是对可能在每种方面略有不同的几点的讨论,这可能会影响对一方的偏好:

I started with an article from MSDN regarding Visual Studio 2003 titled Using the DllImport Attribute. (A bit old, but since the DllImport statement seems to have originated in .NET, it seemed appropriate to go back to the beginning.)

我从MSDN开始撰写一篇名为“使用DllImport属性”的Visual Studio 2003文章。 (有点旧,但由于DllImport语句似乎起源于.NET,因此回到开头似乎是合适的。)

Given an example DllImport statement of:

给出一个示例DllImport语句:

[DllImport("user32.dll", EntryPoint = "MessageBox", CharSet = Unicode)]
int MessageBox(void* hWnd, wchar_t* lpText, wchar_t* lpCaption, unsigned int uType);

It says that if the EntryPoint value is left out, the CLR will look for the name of the function (MessageBox, in this case) as a default. However, in this instance, since a CharSet of Unicode was specified, the CLR would FIRST look for a function called "MessageBoxW" - the 'W' indicating a Unicode return type. (The ANSI return type version would be "MessageBoxA".) If no "MessageBoxW" were found, THEN the CLR would look for an API function actually called "MessageBox".

它表示如果遗漏了EntryPoint值,CLR将查找函数的名称(在本例中为MessageBox)作为默认值。但是,在这种情况下,由于指定了Unicode的CharSet,因此CLR将首先查找名为“MessageBoxW”的函数 - 表示Unicode返回类型的“W”。 (ANSI返回类型版本将是“MessageBoxA”。)如果没有找到“MessageBoxW”,那么CLR将寻找实际上称为“MessageBox”的API函数。

Current specifics about the DllImportAttribute class can be found here, where I viewed the .NET Framework 4 version: DLLImportAttribute Class

关于DllImportAttribute类的当前细节可以在这里找到,我查看了.NET Framework 4版本:DLLImportAttribute类

A key comment in the Remarks section of this .NET Framework 4 page is that:

此.NET Framework 4页面的“备注”部分中的关键注释是:

You apply this attribute directly to C# and C++ method definitions; however, the Visual Basic compiler emits this attribute when you use the Declare statement.

您可以将此属性直接应用于C#和C ++方法定义;但是,当您使用Declare语句时,Visual Basic编译器会发出此属性。

So, at least as pertains to VB.NET, the compiler ends up with a Declare statement anyway.

因此,至少与VB.NET相关,编译器最终会以Declare语句结束。

There is also an important note in this page:

此页面中还有一个重要说明:

The DllImportAttribute does not support marshaling of generic types.

DllImportAttribute不支持泛型类型的编组。

So, it would appear that if you want to use a generic type, you'd have to use a Declare statement.

因此,看起来如果要使用泛型类型,则必须使用Declare语句。

Next, I headed to the Declare statement information. A Visual Studio 2010 version (Visual Basic statement info) was here: Declare Statement

接下来,我前往Declare声明信息。 Visual Studio 2010版本(Visual Basic语句信息)在这里:声明语句

A key item here was this note:

这里的一个关键项目是这个说明:

You can use Declare only at module level. This means the declaration context for an external reference must be a class, structure, or module, and cannot be a source file, namespace, interface, procedure, or block.

您只能在模块级别使用Declare。这意味着外部引用的声明上下文必须是类,结构或模块,并且不能是源文件,命名空间,接口,过程或块。

Apparently, if you want to set up an API call outside of a class, structure, or module, you'll have to use the DllImport statement instead of the Declare.

显然,如果要在类,结构或模块之外设置API调用,则必须使用DllImport语句而不是Declare。

The example Declare statement on this page is:

此页面上的示例Declare语句是:

Declare Function getUserName Lib "advapi32.dll" Alias "GetUserNameA" (
  ByVal lpBuffer As String, ByRef nSize As Integer) As Integer

Following that example is this little tidbit of information:

在这个例子之后是这个小小的信息:

The DllImportAttribute provides an alternative way of using functions in unmanaged code. The following example declares an imported function without using a Declare statement.

DllImportAttribute提供了在非托管代码中使用函数的替代方法。以下示例在不使用Declare语句的情况下声明导入的函数。

followed by, of course, an example of DllImport usage.

当然,后面是DllImport用法的一个例子。

Regarding Unicode vs ANSI results, according to this Declare page, if you specify a CharSet value (available in Declare, but not shown in the example above) the CLR will do the same type of automatic name search that DllImport does - for either Unicode or ANSI.

关于Unicode与ANSI结果,根据此Declare页面,如果指定CharSet值(在Declare中可用,但未在上面的示例中显示),CLR将执行与DllImport相同类型的自动名称搜索 - 对于Unicode或ANSI。

If you do not specify a CharSet value in the Declare statement, then you must make sure that your function name in the Declare is the same as the function name in the actual API function's header file, OR you must specifiy an Alias value that matches the actual function name in the header file (as shown in the example above).

如果未在Declare语句中指定CharSet值,则必须确保Declare中的函数名与实际API函数头文件中的函数名相同,或者必须指定与之匹配的Alias值。头文件中的实际函数名称(如上例所示)。

I was not able to find any specific Microsoft documentation stating that either DllImport or Declare were preferred, or even recommended, over one another in any situation other than those noted above.

我无法找到任何特定的Microsoft文档,声明DllImport或Declare在上述情况之外的任何情况下都是首选或甚至推荐的。

My conclusion, therefore, is:

因此,我的结论是:

1) Unless you need to place your definition in one of the places a Declare statement cannot be used, either technique will work fine,

1)除非你需要将你的定义放在其中一个地方,否则不能使用Declare语句,这两种技术都可以正常工作,

and

2) if you're using DllImport, make sure you specify the CharSet value you want (Unicode or ANSI), or you may get unexpected results.

2)如果您正在使用DllImport,请确保指定所需的CharSet值(Unicode或ANSI),否则可能会出现意外结果。

#3


In my opinion, since this keyword doesn't look deprected, etc. from what I searched, simply use compile-time keywords rather than attributes.

在我看来,由于这个关键字看起来并不像我搜索的那样,所以只需使用编译时关键字而不是属性。

Also, when you use the Declare, you don't need to write the End Function. The advantage of that is that you can create a whole module of declarations of function imports line by line, with no need to pulute your code with DllImports and End Functions.

此外,当您使用Declare时,您不需要编写End Function。这样做的好处是,您可以逐行创建函数导入声明的整个模块,而无需使用DllImports和End Functions来清空代码。

When you declare using the Declare keyword, the compiler treats this function as Shared anyway, so it can be accessed via other extenal objects.

当您声明使用Declare关键字时,编译器无论如何都会将此函数视为Shared,因此可以通过其他extenal对象访问它。

But I think in the current VB.NET they're both addressed to the same target and no performance difference - no warranty on this one.

但我认为在目前的VB.NET中,它们都是针对相同的目标而没有性能差异 - 对此没有保证。

So my conclusion is: Do use the Declare instead of DllImport, especially reading what you quoted that Microsoft stated that it should be used in rare cases.

所以我的结论是:使用Declare而不是DllImport,特别是阅读你引用的内容,微软声称它应该在极少数情况下使用。

#4


If you need to set one of the following options, then use DllImportAttribute attribute, else use Declare. From https://msdn.microsoft.com/en-us/library/w4byd5y4.aspx

如果需要设置以下选项之一,则使用DllImportAttribute属性,否则使用Declare。来自https://msdn.microsoft.com/en-us/library/w4byd5y4.aspx

To apply the BestFitMapping, CallingConvention, ExactSpelling, PreserveSig, SetLastError, or ThrowOnUnmappableChar fields to a Microsoft Visual Basic 2005 declaration, you must use the DllImportAttribute attribute instead of the Declare statement.

若要将BestFitMapping,CallingConvention,ExactSpelling,PreserveSig,SetLastError或ThrowOnUnmappableChar字段应用于Microsoft Visual Basic 2005声明,必须使用DllImportAttribute属性而不是Declare语句。

It is unclear from the above reference only whether this applies to only "Visual Basic 2005" or not, as the above reference is from a .NET 4.5 article. However, I also found this article (https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute(v=vs.110).aspx ) which is specific to the DllImportAttribute class in .NET 4.5 :

从上面的参考文献中不清楚这是否仅适用于“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:

the Visual Basic compiler emits this attribute when you use the Declare statement. For complex method definitions that include BestFitMapping, CallingConvention, ExactSpelling, PreserveSig, SetLastError, or ThrowOnUnmappableChar fields, you apply this attribute directly to Visual Basic method definitions.

使用Declare语句时,Visual Basic编译器会发出此属性。对于包含BestFitMapping,CallingConvention,ExactSpelling,PreserveSig,SetLastError或ThrowOnUnmappableChar字段的复杂方法定义,可以将此属性直接应用于Visual Basic方法定义。

This tells you that the Declare option is VB.net syntactical sugar which is converted to DllImportAttribute at compile time, and outlines the exact scenarios when using DllImportAttribute directly is recommended.

这告诉您Declare选项是VB.net语法糖,它在编译时转换为DllImportAttribute,并概述了建议直接使用DllImportAttribute时的确切方案。