C# 动态装载 DLL
C# 动态装载 DLL
LoadDllTool.cs 如下:
using System;
using System.Collections.Generic;
using System.Text; using System.Runtime.InteropServices; // 用 DllImport 需用此 命名空间
using System.Reflection; // 使用 Assembly 类需用此 命名空间
using System.Reflection.Emit; // 使用 ILGenerator 需用此 命名空间 namespace CallDephiDll
{ public class LoadDllTool
{ //+------------------------------------------------------------------
//+ 动态装载
//+------------------------------------------------------------------ /// <summary>
/// 参数传递方式枚举 ,ByValue 表示值传递 ,ByRef 表示址传递
/// </summary>
public enum ModePass
{
ByValue = 0x0001,
ByRef = 0x0002
} /// <summary>
/// 原型是 :HMODULE LoadLibrary(LPCTSTR lpFileName);
/// </summary>
/// <param name="lpFileName">DLL 文件名 </param>
/// <returns> 函数库模块的句柄 </returns>
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName); /// <summary>
/// 原型是 : FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);
/// </summary>
/// <param name="hModule"> 包含需调用函数的函数库模块的句柄 </param>
/// <param name="lpProcName"> 调用函数的名称 </param>
/// <returns> 函数指针 </returns>
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); /// <summary>
/// 原型是 : BOOL FreeLibrary(HMODULE hModule);
/// </summary>
/// <param name="hModule"> 需释放的函数库模块的句柄 </param>
/// <returns> 是否已释放指定的 Dll</returns>
[DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]
static extern bool FreeLibrary(IntPtr hModule); /// <summary>
/// Loadlibrary 返回的函数库模块的句柄
/// </summary>
public static IntPtr hModule = IntPtr.Zero; /// <summary>
/// GetProcAddress 返回的函数指针
/// </summary>
private static IntPtr farProc = IntPtr.Zero; /// <summary>
/// 装载 Dll
/// </summary>
/// <param name="lpFileName">DLL 文件名 </param>
public static void LoadDll(string lpFileName)
{ hModule = LoadLibrary(lpFileName);
if (hModule == IntPtr.Zero)
throw (new Exception(" 没有找到 :" + lpFileName + ".")); } //若已有已装载Dll的句柄,可以使用LoadDll方法的第二个版本:
public static void LoadDll(IntPtr HMODULE)
{
if (HMODULE == IntPtr.Zero)
throw (new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 ."));
hModule = HMODULE;
} /// <summary>
/// 获得函数指针
/// </summary>
/// <param name="lpProcName"> 调用函数的名称 </param>
public static void LoadFun(string lpProcName)
{ // 若函数库模块的句柄为空,则抛出异常 if (hModule == IntPtr.Zero)
throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); // 取得函数指针
farProc = GetProcAddress(hModule, lpProcName); // 若函数指针,则抛出异常
if (farProc == IntPtr.Zero)
throw (new Exception(" 没有找到 :" + lpProcName + " 这个函数的入口点 ")); } /// <summary>
/// 获得函数指针
/// </summary>
/// <param name="lpFileName"> 包含需调用函数的 DLL 文件名 </param>
/// <param name="lpProcName"> 调用函数的名称 </param>
public static 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 + " 这个函数的入口点 ")); } /// <summary>
/// 卸载 Dll
/// </summary>
public static void UnLoadDll()
{ FreeLibrary(hModule);
hModule = IntPtr.Zero;
farProc = IntPtr.Zero;
} // Invoke方法的第一个版本:
/// <summary>
/// 调用所设定的函数
/// </summary>
/// <param name="ObjArray_Parameter"> 实参 </param>
/// <param name="TypeArray_ParameterType"> 实参类型 </param>
/// <param name="ModePassArray_Parameter"> 实参传送方式 </param>
/// <param name="Type_Return"> 返回类型 </param>
/// <returns> 返回所调用函数的 object</returns>
public static 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"; // 生成单模块配件
AssemblyBuilder MyAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(MyAssemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder MyModuleBuilder = MyAssemblyBuilder.DefineDynamicModule("InvokeDll"); // 定义要调用的方法 , 方法名为“ MyFun ”,返回类型是“ Type_Return ”参数类型是“ TypeArray_ParameterType ”
MethodBuilder MyMethodBuilder = MyModuleBuilder.DefineGlobalMethod("MyFun", MethodAttributes.Public | MethodAttributes.Static, Type_Return, TypeArray_ParameterType); // 获取一个 ILGenerator ,用于发送所需的 IL
ILGenerator IL = MyMethodBuilder.GetILGenerator(); int i;
for (i = ; i < ObjArray_Parameter.Length; i++)
{// 用循环将参数依次压入堆栈 switch (ModePassArray_Parameter[i])
{
case ModePass.ByValue:
IL.Emit(OpCodes.Ldarg, i);
break;
case ModePass.ByRef:
IL.Emit(OpCodes.Ldarga, i);
break;
default:
throw (new Exception(" 第 " + (i + ).ToString() + " 个参数没有给定正确的传递方式 .")); } } if (IntPtr.Size == )
{// 判断处理器类型
IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32());
}
else if (IntPtr.Size == )
{
IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64());
}
else
{
throw new PlatformNotSupportedException();
} IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, Type_Return, TypeArray_ParameterType);
IL.Emit(OpCodes.Ret); // 返回值 MyModuleBuilder.CreateGlobalFunctions(); // 取得方法信息
MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod("MyFun");
return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 调用方法,并返回其值
} // Invoke方法的第二个版本,它是调用了第一个版本的:
/// <summary>
/// 调用所设定的函数
/// </summary>
/// <param name="IntPtr_Function"> 函数指针 </param>
/// <param name="ObjArray_Parameter"> 实参 </param>
/// <param name="TypeArray_ParameterType"> 实参类型 </param>
/// <param name="ModePassArray_Parameter"> 实参传送方式 </param>
/// <param name="Type_Return"> 返回类型 </param>
/// <returns> 返回所调用函数的 object</returns>
public static object Invoke(IntPtr IntPtr_Function, object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return)
{ // 下面 2 个 if 是进行安全检查 , 若不能通过 , 则抛出异常
if (hModule == IntPtr.Zero)
throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));
if (IntPtr_Function == IntPtr.Zero)
throw (new Exception(" 函数指针 IntPtr_Function 为空 !"));
farProc = IntPtr_Function;
return Invoke(ObjArray_Parameter, TypeArray_ParameterType, ModePassArray_Parameter, Type_Return); } }
}
相关调用示例:
1. 加载 dll 和 调用方法
dllPath = Application.StartupPath + @"\test.dll";
LoadDllTool.LoadDll(dllPath);
LoadDllTool.LoadFun("OpenForm");
object[] param = new object[];
param[] = handle;
param[] = loginUserID;
param[] = loginUserName;
param[] = accID;
param[] = year;
param[] = menuName;
param[] = path;
Type[] paramType = new Type[];
paramType[] = typeof(IntPtr);
paramType[] = typeof(string);
paramType[] = typeof(string);
paramType[] = typeof(string);
paramType[] = typeof(string);
paramType[] = typeof(string);
paramType[] = typeof(string);
LoadDllTool.ModePass[] paramModePass = new LoadDllTool.ModePass[];
paramModePass[] = LoadDllTool.ModePass.ByValue;
paramModePass[] = LoadDllTool.ModePass.ByValue;
paramModePass[] = LoadDllTool.ModePass.ByValue;
paramModePass[] = LoadDllTool.ModePass.ByValue;
paramModePass[] = LoadDllTool.ModePass.ByValue;
paramModePass[] = LoadDllTool.ModePass.ByValue;
paramModePass[] = LoadDllTool.ModePass.ByValue;
this.DelphiHandle = (IntPtr)LoadDllTool.Invoke(param, paramType, paramModePass, typeof(IntPtr));
2. 释放Dll 和 调用函数示例
/// <summary>
/// 释放 加载的 Dll
/// </summary>
private void unLoadDll()
{
try
{
LoadDllTool.LoadFun("CloseForm");
IntPtr rtnResult = (IntPtr)LoadDllTool.Invoke(new object[] { "hello" }, new Type[] { typeof(string) }, new LoadDllTool.ModePass[] { LoadDllTool.ModePass.ByValue }, typeof(IntPtr)); LoadDllTool.UnLoadDll();
}
catch (Exception ex)
{
MessageBox.Show("释放Dll错误:" + ex.Message.ToString());
}
}
如果在释放的时候出现异常的话,需要在 VS里做如下设置:
我是 VS2010
首先:Ctrl + Alt + E 调用 异常窗口 ,展开: Managed Debugging Assistants 然后, 再找到: LoaderLock 这个节点,把勾去掉,确定 就可以了.

C# 动态装载 DLL的更多相关文章
- C#程序实现动态调用DLL的研究(转)
摘 要:在<csdn开发高手>2004年第03期中的<化功大法——将DLL嵌入EXE>一文,介绍了如何把一个动态链接库作为一个资源嵌入到可执行文件,在可执行文件运行时,自动从资 ...
- 【VB技巧】VB静态调用与动态调用dll详解
本文“[VB技巧]VB静态调用与动态调用dll详解”,来自:Nuclear'Atk 网络安全研究中心,本文地址:http://lcx.cc/?i=489,转载请注明作者及出处! [[请注意]]:在以下 ...
- C#程序实现动态调用DLL的研究[转]
摘 要: 在< csdn 开发高手> 2004 年第 03 期中的<化功大法——将 DLL 嵌入 EXE >一文,介绍了如何把一个动态链接库作为一个资源嵌入到可执行文件,在 ...
- C#程序实现动态调用DLL的研究
摘 要:在<csdn开发高手>2004年第03期中的<化功大法——将DLL嵌入EXE>一文,介绍了如何把一个动态链接库作为一个资源嵌入到可执行文件,在可执行文件运行时,自动从资 ...
- 动态载入DLL所需要的三个函数详解(LoadLibrary,GetProcAddress,FreeLibrary)
动态载入 DLL 动态载入方式是指在编译之前并不知道将会调用哪些 DLL 函数, 完全是在运行过程中根据需要决定应调用哪些函数. 方法是:用 LoadLibrary 函数加载动态链接库到内存,用 Ge ...
- 【转载】动态载入DLL所需要的三个函数详解(LoadLibrary,GetProcAddress,FreeLibrary)
原文地址:https://www.cnblogs.com/westsoft/p/5936092.html 动态载入 DLL 动态载入方式是指在编译之前并不知道将会调用哪些 DLL 函数, 完全是在运行 ...
- win系统动态载入DLL所需要的三个函数详解(LoadLibrary,GetProcAddress,FreeLibrary)
动态载入 DLL 动态载入方式是指在编译之前并不知道将会调用哪些 DLL 函数, 完全是在运行过程中根据需要决定应调用哪些函数. 方法是:用 LoadLibrary 函数加载动态链接库到内存,用 Ge ...
- 卸载AppDomain动态调用DLL异步线程执行失败
应用场景 动态调用DLL中的类,执行类的方法实现业务插件功能 使用Assembly 来实现 但是会出现逻辑线程数异常的问题 使用AppDomain 实现动态调用,并卸载. 发现问题某个插件中开启异步线 ...
- WinForm开发框架--动态读取DLL模式
1\ WinForm开发框架--动态读取DLL模式 http://www.2cto.com/kf/201306/217199.html 2\ 广州爱奇迪 http://www.iqidi. ...
随机推荐
- MPI - 缓冲区和非阻塞通信
转载自: Introduction to MPI - Part II (Youtube) Buffering Suppose we have ) MPI_Send(sendbuf,...,,...) ...
- dev devfs udev sysfs及关系
Linux 下对设备的管理方式主要有/dev和sysfs两种,前者是将设备注册为设备节点放入/dev目录下,而后者是在linux2.6内核后引入的新的文件系统. ➤/dev方式 关于/dev的 ...
- 【坐标变换】【二维偏序】【线段树】Gym - 100820G - Racing Gems
题意:第一象限有n个点,你从x正半轴任选一个位置出发,vy恒定,vx可以任意变化,不过只能在-vy/r到vy/r之间变化,问你最多能经过多少个点. 暴力dp是n^2,不可取. 注意到,一个点,所能到达 ...
- python3-开发进阶Flask的基础(4)
今日内容: 上下文管理:LocalProxy对象 上下文管理: 请求上下文: request/session app上下文:app/g 第三方组件:wtforms 1.使用 ...
- poj 1733 并查集+hashmap
题意:题目:有一个长度 已知的01串,给出多个条件,[l,r]这个区间中1的个数是奇数还是偶数,问前几个是正确的,没有矛盾 链接:点我 解题思路:hash离散化+并查集 首先我们不考虑离散化:s[x] ...
- php-streams扩展学习
一. streams是干嘛的: 用于统一文件.网络.数据压缩等类文件操作方式,并为这些类文件操作提供一组通用的函数接口. 二. stream是具有流式行为的资源对象,这个对象有一个包装类 例如: pr ...
- 用rem设置文字大小
一.px与em 用px设置文字大小是再正常不过的事情,比如 html {font-size: 12px;} 随处可见的在设置width.height使用px,这也是细致稳妥的设置方法,这样做的缺点在于 ...
- 更新yum源/apt-get源
国内开源镜像站有:网易: http://mirrors.163.com/ 搜狐: http://mirrors.sohu.com/阿里云: http://mirrors.aliyun.com/北京理工 ...
- mmap函数使用
UNIX网络编程第二卷进程间通信对mmap函数进行了说明.该函数主要用途有三个:1.将一个普通文件映射到内存中,通常在需要对文件进行频繁读写时使用,这样用内存读写取代I/O读写,以获得较高的性能:2. ...
- c#封装三维向量,另外也看了下别人的C++封装
一. c#实现 /* Vector3 Definition Created by taotao man on 2016-4-12 brief:封装三位向量类Vector3 // 修改记录: date: ...