C#通过DllImport引入dll中的C++非托管类

C#通过DllImport引入dll中的C++非托管类

(2011-08-05 15:00:57)

转载

标签:

c

dllimport

c非托管类

分类: C#

首先从msdn了解到,DllImport是用来将特性化方法由非托管动态链接库 (DLL) 作为静态入口点公开

从以上语句我们可以理解出三点:

1.C++编写的非托管dll可以通过DllImport引入到C#中;

2.引入到C#中的只能是C++方法(或者说函数),而不能是数据(或者说变量);

3.引入到C#中后只能声明为静态函数;

msdn关于DllImport属性类的链接:

http://msdn.microsoft.com/zh-cn/library/system.runtime.interopservices.dllimportattribute(v=VS.100).aspx

DllImport的其使用格式如下所示:

 

[DllImport("compute.dll", EntryPoint = "FunName", CharSet = CharSet.Auto)]

public static extern int FunName(type var);

 

注:其引入格式中static和extern是必不可少的;

 

接下来主要讲述如何通过DllImport将C++类通过dll引入到C#:

1.生成包含C++类的dll

源代码如下所示:

computer.h:

 

#pragma once

class computer

{

computer();

public:

__declspec(dllexport) int sum(int mem1,int mem2);//计算两个参数之和:mem1+mem2

__declspec(dllexport) int sum();//计算两个成员变量之和:computer::mem1+computer::mem2

__declspec(dllexport) int sub(int mem1,int mem2);//计算两个参数之差:mem1-mem2

__declspec(dllexport) int sub();//计算两个成员变量之差:computer::mem1-computer::mem2

__declspec(dllexport) void setmember(int m1,int m2);//设定两个成员静态变量mem1和mem2的值

__declspec(dllexport) int getmember(int index);//index=1或2,分别读取mem1和mem2的值

private:

static int mem1;//只有声明为静态变量,才可以在C#中访问修改

static int mem2;

};

int computer::mem1=8;//静态成员初始化

int computer::mem2=9;

 

实现代码不在此赘述,编译生成dll。

2.将生成dll放置到C#工程的debug\bin目录下;

3.向C#工程中引入此dll并导入编写的类

向C#工程中添加新类computer,在生成的computer.cs文件中添加代码:

using System.Runtime.InteropServices;//此语句保证能够调用DllImport

编写computer类的代码如下:

    class computer

    {

        [DllImport("compute.dll", EntryPoint = "?getmember@computer@@QAEHH@Z", CharSet = CharSet.Auto)]

        public static extern int getmember(int index);

        [DllImport ("compute.dll",EntryPoint="?setmember@computer@@QAEXHH@Z",CharSet=CharSet.Auto )]

        public static extern void setMember(int m1,int m2);

        [DllImport("compute.dll", EntryPoint = "?sum@computer@@QAEHHH@Z", CharSet = CharSet.Auto)]

        public static extern int sum(int mem1, int mem2);

        [DllImport("compute.dll", EntryPoint = "?sum@computer@@QAEHXZ", CharSet = CharSet.Auto)]

        public static extern int sum();

        [DllImport("compute.dll", EntryPoint = "?sub@computer@@QAEHHH@Z", CharSet = CharSet.Auto)]

        public static extern int sub(int mem1, int mem2);

        [DllImport("computer.dll", EntryPoint = "?sub@computer@@QAEHXZ", CharSet = CharSet.Auto)]

        public static extern int sub();

    }

具体DllImport的参数属性等请参考如下链接:

http://blog.csdn.net/jame_peng/article/details/4387906

4.在主程序中进行验证

由于引入的方法都是静态的,所以不能通过对象进行调用,而只能通过类来调用,具体代码如下:

            computer.setMember(4, 5);

            Console.WriteLine(computer.getmember(1));

            Console.WriteLine(computer.getmember(2));

            Console.WriteLine(computer.sum());

            Console.WriteLine(computer.sum(1,2));

            Console.WriteLine(computer.sub(3,1));

经验证,这样的方法是可行的。

 

最后,总结一下:

1.非托管的C++函数是可以通过dll经由DllImport引入到C#中,不过就变成静态的了;

2.非托管的C++类也可以通过以上方法引入到C#中,不过就相当于成为了静态类,使用受到了很大限制,不可以再定义多个对象了;

常用的三大模块:

 

user32.dll

是Windows用户界面相关应用程序接口,用于包括Windows处理,基本用户界面等特性,如创建窗口和发送消息

 

gdi32.dll

gdi32.dll是Windows GDI图形用户界面相关程序,包含的函数用来绘制图像和显示文字

 

kernel32.dll

控制着系统的内存管理、数据的输入输出操作和中断处理

 

微软就是靠这三个模块起家的

Windows SDK只利用这三个模块就能构建基本的Windows程序

 

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);

}

 

posted @ 2011-12-09 12:34  非洲人  阅读(1645)  评论(0)    收藏  举报