C# 调用外部dllWord格式文档下载.docx
- 文档编号:3907640
- 上传时间:2023-05-02
- 格式:DOCX
- 页数:24
- 大小:90.29KB
C# 调用外部dllWord格式文档下载.docx
《C# 调用外部dllWord格式文档下载.docx》由会员分享,可在线阅读,更多相关《C# 调用外部dllWord格式文档下载.docx(24页珍藏版)》请在冰点文库上搜索。
(一)调用DLL中的非托管函数一般方法
首先,应该在C#语言源程序中声明外部方法,其根本形式是:
[DLLImport(“DLL文件〞)]
修饰符extern返回变量类型方法名称〔参数列表〕
其中:
DLL文件:
包含定义外部方法的库文件。
修饰符:
访问修饰符,除了abstract以外在声明方法时可以使用的修饰符。
返回变量类型:
在DLL文件中你需调用方法的返回变量类型。
方法名称:
在DLL文件中你需调用方法的名称。
参数列表:
在DLL文件中你需调用方法的列表。
注意:
需要在程序声明中使用System.Runtime.InteropServices命名空间。
DllImport只能放置在方法声明上。
DLL文件必须位于程序当前目录或系统定义的查询路径中〔即:
系统环境变量中Path所设置的路径〕。
返回变量类型、方法名称、参数列表一定要与DLL文件中的定义相一致。
假设要使用其它函数名,可以使用EntryPoint属性设置,如:
[DllImport("
user32.dll"
EntryPoint="
MessageBoxA"
)]
staticexternintMsgBox(inthWnd,stringmsg,stringcaption,inttype);
其它可选的DllImportAttribute属性:
CharSet指示用在入口点中的字符集,如:
CharSet=CharSet.Ansi;
SetLastError指示方法是否保存Win32"
上一错误"
,如:
SetLastError=true;
ExactSpelling指示EntryPoint是否必须与指示的入口点的拼写完全匹配,如:
ExactSpelling=false;
PreserveSig指示方法的签名应当被保存还是被转换,如:
PreserveSig=true;
CallingConvention指示入口点的调用约定,如:
CallingConvention=CallingConvention.Winapi;
此外,关于“数据封送处理〞与“封送数字和逻辑标量〞请参阅其它一些文章[2]。
C#例子:
1.启动VS.NET,新建一个项目,项目名称为“Tzb〞,模板为“Windows应用程序〞。
2.在“工具箱〞的“Windows窗体〞项中双击“Button〞项,向“Form1〞窗体中添加一个按钮。
3.改变按钮的属性:
Name为“B1〞,Text为“用DllImport调用DLL弹出提示框〞,并将按钮B1调整到适当大小,移到适当位置。
4.在类视图中双击“Form1〞,打开“Form1.cs〞代码视图,在“namespaceTzb〞上面输入“usingSystem.Runtime.InteropServices;
〞,以导入该命名空间。
5.在“Form1.cs[设计]〞视图中双击按钮B1,在“B1_Click〞方法上面使用关键字static和extern声明方法“MsgBox〞,将DllImport属性附加到该方法,这里我们要使用的是“user32.dll〞中的“MessageBoxA〞函数,具体代码如下:
然后在“B1_Click〞方法体添加如下代码,以调用方法“MsgBox〞:
MsgBox(0,"
这就是用DllImport调用DLL弹出的提示框哦!
"
"
挑战杯"
0x30);
6.按“F5〞运行该程序,并点击按钮B1,便弹出如下提示框:
(二)动态装载、调用DLL中的非托管函数
在上面已经说明了如何用DllImport调用DLL中的非托管函数,但是这个是全局的函数,假假设DLL中的非托管函数有一个静态变量S,每次调用这个函数的时候,静态变量S就自动加1。
结果,当需要重新计数时,就不能得出想要的结果。
下面将用例子说明:
1)启动VisualC++6.0;
2)新建一个“Win32Dynamic-LinkLibrary〞工程,工程名称为“Count〞;
3)在“Dllkind〞选择界面中选择“Asimpledllproject〞;
4)打开Count.cpp,添加如下代码:
//导出函数,使用“_stdcall〞标准调用
extern"
C"
_declspec(dllexport)int_stdcallcount(intinit);
int_stdcallcount(intinit)
{//count函数,使用参数init初始化静态的整形变量S,并使S自加1后返回该值
staticintS=init;
S++;
returnS;
}
5)按“F7〞进展编译,得到Count.dll〔在工程目录下的Debug文件夹中〕。
2.用DllImport调用DLL中的count函数
1)打开项目“Tzb〞,向“Form1〞窗体中添加一个按钮。
2)改变按钮的属性:
Name为“B2〞,Text为“用DllImport调用DLL中count函数〞,并将按钮B1调整到适当大小,移到适当位置。
3)打开“Form1.cs〞代码视图,使用关键字static和extern声明方法“count〞,并使其具有来自Count.dll的导出函数count的实现,代码如下:
Count.dll"
staticexternintcount(intinit);
4)在“Form1.cs[设计]〞视图中双击按钮B2,在“B2_Click〞方法体添加如下代码:
MessageBox.Show("
用DllImport调用DLL中的count函数,\n传入的实参为0,得到的结果是:
+count(0).ToString(),"
);
用DllImport调用DLL中的count函数,\n传入的实参为10,得到的结果是:
+count(10).ToString()+"
\n结果可不是想要的11哦!
!
所得结果明确:
\n用DllImport调用DLL中的非托管\n函数是全局的、静态的函数!
5)把Count.dll复制到项目“Tzb〞的bin\Debug文件夹中,按“F5〞运行该程序,并点击按钮B2,便弹出如下三个提示框:
第1个提示框显示的是调用“count(0)〞的结果,第2个提示框显示的是调用“count(10)〞的结果,由所得结果可以证明“用DllImport调用DLL中的非托管函数是全局的、静态的函数〞。
所以,有时候并不能达到我们目的,因此我们需要使用下面所介绍的方法:
C#动态调用DLL中的函数。
3.C#动态调用DLL中的函数
因为C#中使用DllImport是不能像动态load/unloadassembly那样,所以只能借助API函数了。
在kernel32.dll中,与动态库调用有关的函数包括[3]:
①LoadLibrary〔或MFC的AfxLoadLibrary〕,装载动态库。
②GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL部地址。
③FreeLibrary〔或MFC的AfxFreeLibrary〕,释放动态库。
它们的原型分别是:
HMODULELoadLibrary(LPCTSTRlpFileName);
FARPROCGetProcAddress(HMODULEhModule,LPCWSTRlpProame);
BOOLFreeLibrary(HMODULEhModule);
现在,我们可以用IntPtrhModule=LoadLibrary(“Count.dll〞);
来获得Dll的句柄,用IntPtrfarProc=GetProcAddress(hModule,〞_count4〞);
来获得函数的入口地址。
但是,知道函数的入口地址后,怎样调用这个函数呢?
因为在C#中是没有函数指针的,没有像C++那样的函数指针调用方式来调用函数,所以我们得借助其它方法。
经过研究,发现我们可以通过结合使用System.Reflection.Emit与System.Reflection.Assembly里的类和函数达到我们的目的。
为了以后使用方便与实现代码的复用,我们可以编写一个类。
1)dld类的编写:
1.打开项目“Tzb〞,打开类视图,右击“Tzb〞,选择“添加〞-->
“类〞,类名设置为“dld〞,即dynamicloadingdll的每个单词的开头字母。
2.添加所需的命名空间与声明参数传递方式枚举:
usingSystem.Runtime.InteropServices;
//用DllImport需用此命名空间
usingSystem.Reflection;
//使用Assembly类需用此命名空间
usingSystem.Reflection.Emit;
//使用ILGenerator需用此命名空间
在“publicclassdld〞上面添加如下代码声明参数传递方式枚举:
///<
summary>
///参数传递方式枚举,ByValue表示值传递,ByRef表示址传递
/summary>
publicenumModePass
{
ByValue=0x0001,
ByRef=0x0002
3.声明LoadLibrary、GetProcAddress、FreeLibrary与私有变量hModule和farProc:
///原型是:
paramname="
lpFileName"
>
DLL文件名<
/param>
returns>
函数库模块的句柄<
/returns>
kernel32.dll"
staticexternIntPtrLoadLibrary(stringlpFileName);
FARPROCGetProcAddress(HMODULEhModule,LPCWSTRlpProame);
hModule"
包含需调用函数的函数库模块的句柄<
lpProame"
调用函数的名称<
函数指针<
staticexternIntPtrGetProcAddress(IntPtrhModule,stringlpProame);
BOOLFreeLibrary(HMODULEhModule);
需释放的函数库模块的句柄<
是否已释放指定的Dll<
kernel32"
EntryPoint="
FreeLibrary"
SetLastError=true)]
staticexternboolFreeLibrary(IntPtrhModule);
///Loadlibrary返回的函数库模块的句柄
privateIntPtrhModule=IntPtr.Zero;
///GetProcAddress返回的函数指针
privateIntPtrfarProc=IntPtr.Zero;
4.添加LoadDll方法,并为了调用时方便,重载了这个方法:
///装载Dll
publicvoidLoadDll(stringlpFileName)
hModule=LoadLibrary(lpFileName);
if(hModule==IntPtr.Zero)
throw(newException("
没有找到:
"
+lpFileName+"
."
));
假设已有已装载Dll的句柄,可以使用LoadDll方法的第二个版本:
publicvoidLoadDll(IntPtrHMODULE)
if(HMODULE==IntPtr.Zero)
所传入的函数库模块的句柄HMODULE为空."
hModule=HMODULE;
5.添加LoadFun方法,并为了调用时方便,也重载了这个方法,方法的具体代码与注释如下:
///获得函数指针
publicvoidLoadFun(stringlpProame)
{//假设函数库模块的句柄为空,如此抛出异常
函数库模块的句柄为空,请确保已进展LoadDll操作!
));
//取得函数指针
farProc=GetProcAddress(hModule,lpProame);
//假设函数指针,如此抛出异常
if(farProc==IntPtr.Zero)
+lpProame+"
这个函数的入口点"
包含需调用函数的DLL文件名<
publicvoidLoadFun(stringlpFileName,stringlpProame)
{//取得函数库模块的句柄
//假设函数库模块的句柄为空,如此抛出异常
6.添加UnLoadDll与Invoke方法,Invoke方法也进展了重载:
///卸载Dll
publicvoidUnLoadDll()
FreeLibrary(hModule);
hModule=IntPtr.Zero;
farProc=IntPtr.Zero;
Invoke方法的第一个版本:
///调用所设定的函数
ObjArray_Parameter"
实参<
TypeArray_ParameterType"
实参类型<
ModePassArray_Parameter"
实参传送方式<
Type_Return"
返回类型<
返回所调用函数的object<
publicobjectInvoke(object[]ObjArray_Parameter,Type[]TypeArray_ParameterType,ModePass[]ModePassArray_Parameter,TypeType_Return)
//下面3个if是进展安全检查,假设不能通过,如此抛出异常
函数指针为空,请确保已进展LoadFun操作!
));
if(ObjArray_Parameter.Length!
=ModePassArray_Parameter.Length)
参数个数与其传递方式的个数不匹配."
//下面是创建MyAssemblyName对象并设置其Name属性
AssemblyNameMyAssemblyName=newAssemblyName();
MyAssemblyName.Name="
InvokeFun"
;
//生成单模块配件
AssemblyBuilderMyAssemblyBuilder=AppDomain.CurrentDomain.DefineDynamicAssembly(MyAssemblyName,AssemblyBuilderAccess.Run);
ModuleBuilderMyModuleBuilder=MyAssemblyBuilder.DefineDynamicModule("
InvokeDll"
//定义要调用的方法,方法名为“MyFun〞,返回类型是“Type_Return〞参数类型是“TypeArray_ParameterType〞
MethodBuilderMyMethodBuilder=MyModuleBuilder.DefineGlobalMethod("
MyFun"
MethodAttributes.Public|MethodAttributes.Static,Type_Return,TypeArray_ParameterType);
//获取一个ILGenerator,用于发送所需的IL
ILGeneratorIL=MyMethodBuilder.GetILGenerator();
inti;
for(i=0;
i<
ObjArray_Parameter.Length;
i++)
{//用循环将参数依次压入堆栈
switch(ModePassArray_Parameter[i])
caseModePass.ByValue:
IL.Emit(OpCodes.Ldarg,i);
break;
caseModePass.ByRef:
IL.Emit(OpCodes.Ldarga,i);
default:
第"
+(i+1).ToString()+"
个参数没有给定正确的传递方式."
if(IntPtr.Size==4){//判断处理器类型
IL.Emit(OpCodes.Ldc_I4,farProc.ToInt32());
elseif(IntPtr.Size==8)
IL.Emit(OpCodes.Ldc_I8,farProc.ToInt64());
else
thrownewPlatformNotSupportedException();
IL.EmitCalli(Op
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 调用外部dll 调用 外部 dll