• 使用 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
    ", 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
    ", 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
    }
    
  • 相关阅读:
    3.消息队列和事件循环
    2.V8工作原理
    1.浏览器中的Javascript执行机制
    入前端之门半年的感想
    前端面试相关知识点整理记录
    Nginx报错——upstream timed out 10060
    浅谈偏向锁、轻量级锁、重量级锁
    Debug 的一点思路
    Shiro 之 HashedCredentialsMatcher 认证匹配
    计算机网络基础 之六:应用层
  • 原文地址:https://www.cnblogs.com/zhaoxinshanwei/p/3983861.html
Copyright © 2020-2023  润新知