C#调用DLL
来源:互联网 发布:java压缩图片 编辑:程序博客网 时间:2024/04/28 23:25
最近在写一个关于C#调用VC的非MFC动态库的一个小程序,本来原来想用VC写的。后来想用C#画个图
估计很方便,于是就是操起了C#. 虽然以前没有用过C#开发过东西,但是大道都是通的,无非就是工具
而已,虽然前方很模糊,但是我更想看看模糊的背后是什么(呵呵,废话过多,开始了)
运行环境: XP + SP2
1.非托管类(不需要CLR和.NetFramework支持)
注意:需要在程序声明中使用System.Runtime.InteropServices命名空间。
public class Utility
{
[DllImport("User32"]
EntryPoint="MessageBoxA",
CallingConvention=CallingConvention.StdCall]
public static extern int MsgBox (System.IntPtr handle, System.String msg,
System.String caption, System.UInt32 type);
}
class MyClass
{
public static int Main()
{
System.IntPtr handle = new System.IntPtr();
string myString;
Console.Write("Enter your message: ");
myString = Console.ReadLine();
return Utility.MsgBox(handle, "", "", 0);
}
}
值得注意的是,缺省的调用规则(CallingConvention)是Stdcall,同Winapi,在
C++里是__stdcall的形式,还有其他形式, 比如__cdecl, __fastcall
在调用WIN32 API时注意那些类型的转换,如结构(struct)、指针(pointer),
有关各种语言之间类型转换和DllImport属性的详细信息可以参考SDK文档(后面给出)
2.托管类
用Assmebly调用
例: Assembly clientassembly =
Assembly.LoadFrom(@"/dll/CommInfoQuery.dll");
Form clientform =
(Form)clientassembly.CreateInstance("JHManage.CommInfoQueryForm");
clientform.Show();
这种可以防止DLL_HELL,等一下说明DLL_HELL
3 调用标准的DLL
在c#里只需要调用pinvoke (platform invocation) 服务。c# 支持一种sysimport属性
支持这种调用。
下面是完整的语法形式(在例子里没有用到所有的参数):
[sysimport(
dll=dllname,
name=functionname,
charset=charactersettobeused)
]
给出一个调用win32 messagebox函数的例子:
using system;
class pinvokeclient
{
[sysimport(dll="user32.dll")]
public static extern int messageboxa(int hwnd, string message,
string caption, int type);
public static void main()
{
int result = messageboxa(0, "hello world", "pinvoke test", 0);
}
}
注明: 托管与非托管的说法:
简单说明: 托管就是要借助个环境来执行就象.net要装framework
非托管就是代码已经是可以执行的程序就象你平时用的应用程序
复杂说明: 查看MSDN中对于公共语言运行库的介绍(CLR)
4 C#中动态调用DLL中的函数(转)
因为C#中使用DllImport是不能像动态load/unload assembly那样,所以只能借助API函数了。在
kernel32.dll中,与动态库调用有关的函数包括
①LoadLibrary(或MFC 的AfxLoadLibrary),装载动态库。
②GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。
③FreeLibrary(或MFC的AfxFreeLibrary),释放动态链接库。
它们的原型分别是:
HMODULE LoadLibrary(LPCTSTR lpFileName);
FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);
BOOL FreeLibrary(HMODULE hModule);
现在,我们可以用IntPtr hModule=LoadLibrary(“Count.dll”);来获得Dll的句柄,用IntPtr
farProc=GetProcAddress(hModule, "_count@4");来获得函数的入口地址。
但是,知道函数的入口地址后,怎样调用这个函数呢?因为在C#中是没有函数指针的,没有像C++那样的
函数指针调用方式来调用函数,所以我们得借助其它方法。经过研究,发现我们可以通过结合使用
System.Reflection.Emit及System.Reflection.Assembly里的类和函数达到我们的目的。为了以后使用方
便及实现代码的复用,我们可以编写一个类。
1) dld类的编写:
1. 打开项目“Tzb”,打开类视图,右击“Tzb”,选择“添加”-->"类",类名设置为"dld",即dynamic
loading dll 的每个单词的开头字母。
2. 添加所需的命名空间及声明参数传递方式枚举:
using System.Runtime.InteropServices; // 用 DllImport 需用此命名空间
using System.Reflection; // 使用 Assembly 类需用此命名空间(C#的反射)
using System.Reflection.Emit; // 使用 ILGenerator 需用此命名空间
在“public class dld”上面添加如下代码声明参数传递方式枚举:
///
/// 参数传递方式枚举 ,ByValue 表示值传递 ,ByRef 表示址传递
///
public enum ModePass
{
ByValue = 0x0001,
ByRef = 0x0002
}
3. 声明LoadLibrary、GetProcAddress、FreeLibrary及私有变量hModule和farProc:
///
/// 原型是 :HMODULE LoadLibrary(LPCTSTR lpFileName);
///
///DLL 文件名
/// 函数库模块的句柄
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
///
/// 原型是 : FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);
///
///包含需调用函数的函数库模块的句柄
///调用函数的名称
/// 函数指针
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
///
/// 原型是 : BOOL FreeLibrary(HMODULE hModule);
///
///需释放的函数库模块的句柄
/// 是否已释放指定的 Dll
[DllImport("kernel32",EntryPoint="FreeLibrary",SetLastError=true)]
static extern bool FreeLibrary(IntPtr hModule);
///
/// Loadlibrary 返回的函数库模块的句柄
///
private IntPtr hModule=IntPtr.Zero;
///
/// GetProcAddress 返回的函数指针
///
private IntPtr farProc=IntPtr.Zero;
4. 添加LoadDll方法,并为了调用时方便,重载了这个方法:
///
/// 装载 Dll
///
///DLL 文件名
public void LoadDll(string lpFileName)
{
hModule=LoadLibrary(lpFileName);
if(hModule==IntPtr.Zero)
throw(new Exception(" 没有找到 :"+lpFileName+"." ));
}
若已有已装载Dll的句柄,可以使用LoadDll方法的第二个版本:
public void LoadDll(IntPtr HMODULE)
{
if(HMODULE==IntPtr.Zero)
throw(new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 ." ));
hModule=HMODULE;
}
5. 添加LoadFun方法,并为了调用时方便,也重载了这个方法,方法的具体代码及注释如下:
///
/// 获得函数指针
///
///调用函数的名称
public void LoadFun(string lpProcName)
{ // 若函数库模块的句柄为空,则抛出异常
if(hModule==IntPtr.Zero)
throw(new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));
// 取得函数指针
farProc = GetProcAddress(hModule,lpProcName);
// 若函数指针,则抛出异常
if(farProc==IntPtr.Zero)
throw(new Exception(" 没有找到 :"+lpProcName+" 这个函数的入口点 "));
}
///
/// 获得函数指针
///
///包含需调用函数的 DLL 文件名
///调用函数的名称
public void LoadFun(string lpFileName,string lpProcName)
{ // 取得函数库模块的句柄
hModule=LoadLibrary(lpFileName);
// 若函数库模块的句柄为空,则抛出异常
if(hModule==IntPtr.Zero)
throw(new Exception(" 没有找到 :"+lpFileName+"." ));
// 取得函数指针
farProc = GetProcAddress(hModule,lpProcName);
// 若函数指针,则抛出异常
if(farProc==IntPtr.Zero)
throw(new Exception(" 没有找到 :"+lpProcName+" 这个函数的入口点 "));
}
6. 添加UnLoadDll及Invoke方法,Invoke方法也进行了重载:
///
/// 卸载 Dll
///
public void UnLoadDll()
{
FreeLibrary(hModule);
hModule=IntPtr.Zero;
farProc=IntPtr.Zero;
}
Invoke方法的第一个版本:
///
/// 调用所设定的函数
///
///实参
///实参类型
///实参传送方式
///返回类型
/// 返回所调用函数的 object
public object Invoke(object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[]
ModePassArray_Parameter, Type Type_Return)
{
// 下面 3 个 if 是进行安全检查 , 若不能通过 , 则抛出异常
if(hModule==IntPtr.Zero)
throw(new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));
if(farProc==IntPtr.Zero)
throw(new Exception(" 函数指针为空 , 请确保已进行 LoadFun 操作 !" ) );
if(ObjArray_Parameter.Length!=ModePassArray_Parameter.Length)
throw(new Exception(" 参数个数及其传递方式的个数不匹配 ." ) );
// 下面是创建 MyAssemblyName 对象并设置其 Name 属性
AssemblyName MyAssemblyName = new AssemblyName();
MyAssemblyName.Name = "InvokeFun";
// 生成单模块配件
DLL_HELL的说明:
状况1: 动态库没有尽到回溯相容的责任
如果程序A调用的是1.0版本的动态库(X.dll), 而程序B调用的是2.0版本的动态库(X.dll), 因为程
序B开发时间比较晚, 结果程序B在安装到系统的时候, 2.0版本的X.dll也安装到了系统中, 而原来1.0的
版本被2.0所代替.
在大部分情况下2.0向下兼容1.0的版本, 但是有时候也会出现不相容的情况. 此时程序A便无法正
常执行了,此种DLL Hell的起因是设计者, 原因在于动态库没有回溯的原则. (比如A原来在1.0在X.dll中
有的函数, 在更新到2.0的时候取消了, 此时DLL Hell就产生了)
状况2: 动态库尽到了回溯的责任,但是动态库本身出现了bug
另外一种情况就是,2.0的版本向下兼容了,但是出现了程序A和X.dll一起运行的时候出现了问题.
程式 A 用 X 1.0
(X 1.0) (原始版本)
Public SetValue(ByVal Value As Integer)
If Value < 0 Then
Value = 0
End If
m_Value = Value
End Property
程式 B 用 X 2.0
(X 2.0) (更新版本)
Public SetValue(ByVal Value As Integer)
'Fix the bug
If Value < 0 Then
Err.Raise Number:=APP_ERROR, Description:="Negative Value " (错误检测功能)
End If
m_Value = Value
End Property
比如以上出现的问题, 开发人员编写的二进制的dll兼容了以下的版本,内部的GUID的方法名称和参
数都完全相同. 由于X1.0中有一个名字为value的属性名称, 但是次数为负数的时候,就会变成0. 但是确
不会出现错误信息. 这个做法后来发现是错误的,于是X2.0版本解决了value,一旦出现负数就报错.
但是当X2.0用到A上面的时候,编译可以通过,但是运行就DOWN掉了. 所以要完善二进制的dll, 并且
向下兼容,不发生DLL_HELL的情况是非常困难和复杂的。
C#调用DLL文件时参数对应表
Wtypes.h中的非托管类型 非托管 C语言类型 托管类名 说明
HANDLE void* System.IntPtr 32 位
BYTE unsigned char System.Byte 8 位
SHORT short System.Int16 16 位
WORD unsigned short System.UInt16 16 位
INT int System.Int32 32 位
UINT unsigned int System.UInt32 32 位
LONG long System.Int32 32 位
BOOL long System.Int32 32 位
DWORD unsigned long System.UInt32 32 位
ULONG unsigned long System.UInt32 32 位
CHAR char System.Char 用 ANSI 修饰。
LPSTR char* System.String 或
System.StringBuilder 用 ANSI 修饰。
LPCSTR Const char* System.String 或
System.StringBuilder 用 ANSI 修饰。
LPWSTR wchar_t* System.String 或
System.StringBuilder 用 Unicode 修饰。
LPCWSTR Const wchar_t* System.String 或
System.StringBuilder 用 Unicode 修饰。
FLOAT Float System.Single 32 位
DOUBLE Double System.Double 64 位
- C#调用DLL方法
- C#中调用DLL
- C# 中调用DLL
- c#调用外部dll
- c#调用dll
- C#中调用dll
- c# 调用dll文件
- C#中调用DLL
- c#调用DLL
- c#调用DLL
- C#调用dll
- C#调用dll方法
- PB7调用C# dll
- 如何调用C# DLL
- C#调用dll
- c#调用DLL
- PHP 调用 C# dll
- C# 中调用DLL
- iPhone 沙盒路径
- sybase数据库offline修改
- php_mcrypt.dll 无效解决方法
- 最后的冲刺
- AutoMator--苹果自带批处理工具
- C#调用DLL
- Silverlight实例教程 - 理解Navigation导航框架Frame类
- 工信部发布2011年软件收入百强名单 华为居首
- Highcharts 强大的jQuery图表制作功能
- RESTful Web Services Cookbook中文版译者序
- 在CListCtrl中增加CheckBoxs
- 一次团队开发实录--发布接口
- 骄阳的6月
- 首页 > VC世界 > Visual Assist X-v10.6.1849.0修正版/含破解补丁《Visual Studio插件》 Visual Assist X-v10.6.1849.0修正版/含破解补丁《Visual Studio插件》