使用 DllImport 属性

本主题说明 DllImport 属性的常见用法。第一节讨论使用 DllImport 从托管应用程序调用本机代码的优点。第二节集中讨论封送处理和 DllImport 属性的各个方面。

从托管应用程序调用非托管代码

当在托管应用程序中重用现有的非托管代码时,DllImport 属性非常有用。例如,托管应用程序可能需要调用非托管 WIN32 API。

下面的代码示例说明此通用方案,此示例将调用 MessageBox(位于 User32.lib 中):

 

# using   < mscorlib.dll >
using   namespace  System::Runtime::InteropServices; 
//  for DllImportAttribute

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


int  main( )
{
   SysWin32::MessageBox( 
0, L"Hello world!", L"Greetings"0 );
}

主要注意包含 DllImport 的代码行。此代码行根据参数值通知编译器,使之声明位于 User32.dll 中的函数并将签名中出现的所有字符串(如参数或返回值)视为 Unicode 字符串。如果缺少 EntryPoint 参数,则默认值为函数名。另外,由于 CharSet 参数指定 Unicode,因此公共语言运行库将首先查找称为 MessageBoxW(有 W 是因为 Unicode 规范)的函数。如果运行库未找到此函数,它将根据调用约定查找 MessageBox 以及相应的修饰名。受支持的调用约定只有 __cdecl__stdcall

当调用用户定义的 DLL 中所包含的函数时,有必要将 extern "C" 添加在 DLL 函数声明之前,如下所示:

 

//  The function declaration in SampleDLL.h file
extern   " C "  SAMPLEDLL_API  int  fnSampleDLL( void );

有关受支持的其他参数值的更多信息,请参见 DllImport

将非结构化参数由托管封送处理为非托管

除使用上述方法外,还可以使用另一种方法将托管参数(来自托管应用程序)封送处理为非托管参数(在非托管 DLL 中)。

以下代码示例说明封送处理技术:

 

# using   < mscorlib.dll >
using   namespace  System;  //  To bring System::String in
using   namespace  System::Runtime::InteropServices; 
//  for DllImportAttribute
namespace  SysWin32
{
   [DllImport(
"user32.dll", EntryPoint = "MessageBox", CharSet = Unicode)]
   Int32 MessageBox( Int32 hWnd, String
* lpText, String* lpCaption, 
                     UInt32 uType );
}


int  main( )
{
   SysWin32::MessageBox(
0, S"Hello world!", S"Greetings"0);
}

完成实际的调用后,由于 CharSet 参数值的作用,所有参数字符串都自动转换为 wchar_t*。同样,所有 Int32 参数类型都转换为非托管 int,而 UInt32 参数类型转换为非托管 unsigned int

下表提供关于转换非托管和托管上下文的指导:

非托管代码C++ 的托管扩展
int Int32
unsigned int UInt32
short Int16
char* 用于 [in] 参数的 String* (CharSet = Ansi),用于 [out] 参数或返回值的 Text::StringBuilder*
wchar_t* 用于 [in] 参数的 String* (CharSet = Unicode),用于 [out] 参数或返回值的 Text::StringBuilder*
函数指针(回调)
限制:函数指针必须具有 __stdcall 调用约定,因为这是 DllImport 支持的唯一类型。
委托类型
数组(如 wchar_t*[])
限制:CharSet 参数仅应用于函数参数的根类型。因此,无论 CharSet 的值是什么,String* __gc[] 都将被封送处理为 wchar_t* []
相应类型的托管数组(如 String*__gc[]

将结构化类型由非托管封送处理为托管

除简单类型外,运行库还提供了一种机制,可以将简单结构由托管上下文封送处理为非托管上下文。简单结构不包含任何内部数据成员指针、结构化类型的成员或其他元素。

例如,本主题显示如何调用本机 DLL 中具有以下签名的函数:

 

#include  < stdio.h >
struct  S
{
   
char* str;
   
int n;
}
;

int  __cdecl func(  struct  S *  p )
{
   printf( 
"%s/n", p->str );
   
return p->n;
}

若要创建此函数的托管包装,请将 StructLayout 属性应用到调用类。此属性确定封送处理结构时结构的组织方式。若要确保以传统的 C 格式组织结构,请指定顺序布局 (LayoutKind::Sequential)。结果代码如下:

 

# using   < mscorlib.dll >
using   namespace  System;
using   namespace  System::Runtime::InteropServices;

//  CharSet = Ansi(Unicode) means that everything that is a string 
//  in this structure should be marshaled as Ansi(Unicode) 
//  strings
[StructLayout( LayoutKind::Sequential, CharSet = Ansi )]
__gc 
class  MS  //  To be compatible with the type in the native 
//  code, this structure should have the members laid out in
//  the same order as those in the native struct
{
public:
   String
* m_str;
   Int32 m_n;
}
;

[DllImport(
" some.dll " )]
Int32 func( MS
*  ptr );
int  main( )
{
   MS
* p = new MS;
   p
->m_str = S"Hello native!";
   p
->m_n = 7;
   Console::WriteLine(func(p)); 
// Should print 7
}

也可以在托管应用程序中使用 __nogc 关键字,以确保不发生封送处理:

 

#include  < stdlib.h >
#include 
< string .h >
#
using   < mscorlib.dll >
using   namespace  System;
using   namespace  System::Runtime::InteropServices;
__nogc 
class  UMS
{
public:
   
char* m_str;
   
int m_n;
}
;
[DllImport(
" some.dll " )]
Int32 func( UMS
*  ptr );
int  main( )
{
   UMS
* p = new UMS;
   p
->m_str = strdup( "Hello native!" );
   p
->m_n = 7;
   Console::WriteLine(func(p)); 
// Should print 7
   free( p->m_str );
   delete p;
}

第二个方案是:

 

#include  < stdio.h >
struct  S
{
   wchar_t
* str;
   
int n;
}
;
int  __cdecl func(  struct  S p )
{
   printf( 
"%S/n", p.str );
   
return p.n;
}

注意参数是通过值传递的。若要在托管应用程序中包装此调用,请使用值而不是 __gc 类型。结果代码如下:

# using   < mscorlib.dll >
using   namespace  System;
using   namespace  System::Runtime::InteropServices;
[StructLayout( LayoutKind::Sequential, CharSet
= Unicode )]
__value 
class  VS
{
public:
   String
* m_str;
   Int32 m_n;
}
;
[DllImport( 
" some.dll "  )]
Int32 func( VS ptr );
int  main( )
{
   VS v;
   v.m_str 
= S"Hello native!";
   v.m_n 
= 7;
   Console::WriteLine(func(v)); 
// should print 7 also
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值