C#程序实现动态调用非托管的DLL文件

来源:互联网 发布:阿里云9.9学生机怎么用 编辑:程序博客网 时间:2024/05/18 03:16

C#程序实现动态调用非托管的DLL文件

最近的一个项目需要用C#调用C++的Dll,由于C++的Dll是非托管的,所以调用时需要一些技巧。下面分享给新手。

 

一.Dll文件  

动态链接库(也称为DLL,即为“Dynamic Link Library”的缩写)是Microsoft Windows最重要的组成要素之一,打开Windows系统文件夹,你会发现文件夹中有很多DLL文件,Windows就是将一些主要的系统功能以DLL模块的形式实现。

动态链接库是不能直接执行的,也不能接收消息,它只是一个独立的文件,其中包含能被程序或其它DLL调用来完成一定操作的函数(方法。注:C#中一般称为方法”),但这些函数不是执行程序本身的一部分,而是根据进程的需要按需载入,此时才能发挥作用。

DLL只有在应用程序需要时才被系统加载到进程的虚拟空间中,成为调用进程的一部分,此时该DLL也只能被该进程的线程访问,它的句柄可以被调用进程所使用,而调用进程的句柄也可以被该DLL所使用。在内存中,一个DLL只有一个实例,且它的编制与具体的编程语言和编译器都没有关系,所以可以通过DLL来实现混合语言编程。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。

二、      DLL的调用

每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍。首先,您需要了解什么是托管,什么是非托管。一般可以认为:非托管代码主要是基于win 32平台开发的DLLactiveX的组件,托管代码是基于.net平台开发的。如果您想深入了解托管与非托管的关系与区别,及它们的运行机制,请您自行查找资料,本文件在此不作讨论。

(一)     调用DLL中的非托管函数一般方法

首先,应该在C#语言源程序中声明外部方法,其基本形式是:

[DLLImport(“DLL文件”)]

修饰符 extern返回变量类型方法名称 (参数列表)

其中

DLL文件:包含定义外部方法的库文件。

修饰符:访问修饰符,除了abstract以外在声明方法时可以使用的修饰符。

返回变量类型:在DLL文件中你需调用方法的返回变量类型。

方法名称:在DLL文件中你需调用方法的名称。

参数列表:在DLL文件中你需调用方法的列表。

注意:需要在程序声明中使用System.Runtime.InteropServices命名空间。

      DllImport只能放置在方法声明上。

DLL文件必须位于程序当前目录或系统定义的查询路径中(即:系统环境变量中Path所设置的路径)。

返回变量类型、方法名称、参数列表一定要与DLL文件中的定义相一致。

若要使用其它函数名,可以使用EntryPoint属性设置,如:

[DllImport("user32.dll", EntryPoint="MessageBoxA")]

static extern int MsgBox(int hWnd, string msg, string caption, int type);

其它可选的 DllImportAttribute属性:

CharSet 指示用在入口点中的字符集,如:CharSet=CharSet.Ansi

SetLastError指示方法是否保留 Win32"上一错误",如:SetLastError=true

ExactSpelling指示 EntryPoint是否必须与指示的入口点的拼写完全匹配,如:ExactSpelling=false

PreserveSig指示方法的签名应当被保留还是被转换,如:PreserveSig=true

CallingConvention指示入口点的调用约定,如:CallingConvention=CallingConvention.Winapi

 

 

基本的方法就是这样,大家可以尝试一下。




[DllImport("ICcard_dll.dll",CharSet=CharSet.Ansi)]
public static extern int Init_com(char* set);

我用
  [DllImport("ICcard_dll.dll",CharSet=CharSet.Ansi)]
  public static extern int Init_com(string set);
  string StrConnect="1,9600,N,8,4";
  iret = Init_com(StrConnect);

这样用
  [DllImport("ICcard_dll.dll",CharSet=CharSet.Ansi)]
  public static extern int Init_com(ref char set);
  string StrConnect="1,9600,N,8,4";
  要怎么把string数据在c#中表示成char*呢?
这样会说参数出错。
我该怎么用呢,高手救命啊!
  
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
回复次数:12
  • michaelnami用户头像
  • michaelnami
  • (michaelnami)
  • 等 级:

#1楼 得分:5回复于:2010-01-29 14:41:51
public static extern int Init_com(string set);
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:再发两个SQL语句效率,附执行时间图
  • dezhengwei用户头像
  • dezhengwei
  • (dezhengwei)
  • 等 级:

#2楼 得分:0回复于:2010-01-29 14:43:51
引用 1 楼 michaelnami 的回复:
public static extern int Init_com(string set);

我这样用了的,还是不对啊,报参数出错了
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:c# 中 bool 与 Boolean 什么有什么区别
  • hdt用户头像
  • hdt
  • (倦怠)
  • 等 级:

#3楼 得分:5回复于:2010-01-29 14:54:28
那要看c++ 对这个参数的要求,如果是输出参数试试
intptr 或者 stringbuilder
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:年后写的一点代码
  • wanghui0380用户头像
  • wanghui0380
  • (放歌)
  • 等 级:

#4楼 得分:5回复于:2010-01-29 14:57:24
关键问题是:这个char* 是否需要修改,是否out

不out的,可以直接用string 或char[]

out的使用StringBuilder或intptr
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:由1,2,3,4,1.1组合小于6的组合一共有多少种,什么算法可以完全遍历出来
  • yue_shanglin用户头像
  • yue_shanglin
  • (花痴)
  • 等 级:

#5楼 得分:5回复于:2010-01-29 15:04:50
如果ICcard_dll.dll这里面的参数是char*的话,那么你可以这样做:
[DllImport("ICcard_dll.dll",CharSet=CharSet.Ansi)] 
public static extern int Init_com(ref byte* set); 
例如:
string strbuffer = "test";//测试字符串
int result = Init_com(ref Syste.Encoding.Ascii.GetBytes(strbuffer));
这样调用函数就可以了
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:【原创+分享】VS2005水晶报表PUSH模式(DataSet)视频教程+源代码工程示例
  • yue_shanglin用户头像
  • yue_shanglin
  • (花痴)
  • 等 级:

#6楼 得分:5回复于:2010-01-29 15:11:14
补充:C++函数都是通过char*和其他程序进行数据交换的,如果Init_com这个函数的原型是Init_com(char* )的话那么C#中也应该是char*
就该这样:
[DllImport("ICcard_dll.dll",CharSet=CharSet.Ansi)] 
public static extern int Init_com(ref char* set); 
int result = Init_com(ref str.ToCharArray());就OK
如果dll中的原型是Init_com(unsigned char* )的话,才是我上面说的那种
C++和C#数据类型的对应关系是char->char ;unsigned char ->byte
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • bamboo2000用户头像
  • bamboo2000
  • (龙的传人)
  • 等 级:

#7楼 得分:5回复于:2010-01-29 15:40:31
string 或者 System.Text.StringBuilder
如果不是 out 就是 string
如果 out 就用 System.Text.StringBuilder
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • yue_shanglin用户头像
  • yue_shanglin
  • (花痴)
  • 等 级:

#8楼 得分:5回复于:2010-01-29 15:41:18
刚才经过测试,发现char*这种数据类型可以直接用string来做,例如

这样子来构建C#函数也是可以的,这里的数据类型不必和C++函数保持一致的
C# code
[DllImport("ICcard_dll.dll",CharSet=CharSet.Ansi)] public static extern int Init_com(ref string set);
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • bamboo2000用户头像
  • bamboo2000
  • (龙的传人)
  • 等 级:

#9楼 得分:5回复于:2010-01-29 15:49:00
给你个例子:
[DllImport("Del_dll.dll")]
public static extern int GetStrLen();
[DllImport("Del_dll.dll")]
public static extern bool UCase(System.Text.StringBuilder str);

其中Delphi源码为:
function GetStrLen(str:Pchar): integer; stdcall;
begin
  Result := Length(str);
end;

function UCase(str:PChar): boolean; stdcall;
var
  s: string;
begin
  s:=Uppercase(strPas(str));
  CopyMemory(str,pchar(s),Length(s));
  result:=true;
end;

调用:
int len = GetStrLen("abcdefg");
StringBuilder str2 = new StringBuilder("abcdefg hijklmn OPQRST uvwxyz");
if (UCase(str2)) {
...
}
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • mjp1234airen4385用户头像
  • mjp1234airen4385
  • (骏马)
  • 等 级:

#10楼 得分:0回复于:2010-01-29 15:55:09
我同意9楼的意见。
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • dezhengwei用户头像
  • dezhengwei
  • (dezhengwei)
  • 等 级:

#11楼 得分:0回复于:2010-01-29 22:51:03
谢谢大家,问题解决了,大家说的很详细。
 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • holyrong用户头像
  • holyrong

  • 等 级:

#12楼 得分:0回复于:2010-07-29 15:24:16
引用 11 楼 dezhengwei 的回复:
谢谢大家,问题解决了,大家说的很详细。


怎么解决的?

原创粉丝点击