C#中动态加载和卸载DLL

C#动态加载与卸载DLL
本文介绍如何在C#中使用AppDomain实现DLL的动态加载与卸载,解决了因垃圾回收机制导致的DLL更新难题。通过创建独立的AppDomain,可以在运行时更新DLL。

 

在C++中加载和卸载DLL是一件很容易的事,LoadLibrary和FreeLibrary让你能
够轻易的在程序中加载DLL,然后在任何地方卸载。在C#中我们也能使用Assembly.LoadFile实现动态加载DLL,但是当你试图卸载时,你会很惊讶的发现Assembly没有提供任何卸载的方法。这是由于托管代码的自动垃圾回收机制会做这件事情,所以C#不提供释放资源的函数,一切由垃圾回收来做。

 

这引发了一个问题,用Assembly加载的DLL可能只在程序结束的时候才会被释放,这也意味着在程序运行期间无法更新被加载的DLL。而这个功能在某些程序设计时是非常必要的,考虑你正在用反射机制写一个查看DLL中所有函数详细信息的程序,程序提供一个菜单让用户可以选择DLL文件,这时就需要让程序能够卸载DLL,否则一旦用户重新得到新版本DLL时,必须要重新启动程序,重新选择加载DLL文件,这样的设计是用户无法忍受的。

C#也提供了实现动态卸载DLL的方法,通过AppDomain来实现。AppDomain是一个独立执行应用程序的环境,当AppDomain被卸载的时候,在该环境中的所有资源也将被回收。关于AppDomain的详细资料参考MSDN。下面是使用AppDomain实现动态卸载DLL的代码,

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Reflection;
namespace UnloadDll
{
    class Program
    {
        static void Main(string[] args)
        {
            string callingDomainName = AppDomain.CurrentDomain.FriendlyName;//Thread.GetDomain().FriendlyName;
            Console.WriteLine(callingDomainName);
            AppDomain ad = AppDomain.CreateDomain("DLL Unload test");
            ProxyObject obj = (ProxyObject)ad.CreateInstanceFromAndUnwrap(@"UnloadDll.exe", "UnloadDll.ProxyObject");
            obj.LoadAssembly();
            obj.Invoke("TestDll.Class1", "Test", "It's a test");
            AppDomain.Unload(ad);
            obj = null;
            Console.ReadLine();
        }
    }
    class ProxyObject : MarshalByRefObject
    {
        Assembly assembly = null;
        public void LoadAssembly()
        {
            assembly = Assembly.LoadFile(@"TestDLL.dll");           
        }
        public bool Invoke(string fullClassName, string methodName, params Object[] args)
        {
            if(assembly == null)
                return false;
            Type tp = assembly.GetType(fullClassName);
            if (tp == null)
                return false;
            MethodInfo method = tp.GetMethod(methodName);
            if (method == null)
                return false;
            Object obj = Activator.CreateInstance(tp);
            method.Invoke(obj, args);
            return true;           
        }
    }
}

注意:

1. 要想让一个对象能够穿过AppDomain边界,必须要继承MarshalByRefObject类,否则无法被其他AppDomain使用。

2. 每个线程都有一个默认的AppDomain,可以通过Thread.GetDomain()来得到

<think>好的,我现在需要回答用户关于动态加载卸载DLL文件的方法及实现步骤的问题。首先,我需要回顾一下自己对这个主题的了解,并参考用户提供的引用内容,确保回答准确。 动态加载DLL通常涉及在运行时根据需要加载库,而不是在编译时静态链接。这可以通过不同的编程语言API实现。用户提供的引用中提到了C#Unity中的方法,还有使用Windows API进行远程线程注入卸载的例子。我需要将这些信息整合起来,分步骤说明。 首先,动态加载DLL的方法。在C#中,可以使用Assembly.LoadFrom(),如引用[2]提到的。而在更底层的Windows API中,可能需要使用LoadLibrary函数。Unity中创建DLL加载的步骤在引用[1]中有说明,比如生成DLL后放在Plugins文件夹。此外,引用[4]提到使用CreateRemoteThreadLoadLibrary进行远程注入,这也属于动态加载的一种方式。 接下来是卸载DLL。在C#中,直接卸载程序集可能比较复杂,因为AppDomain的管理。引用[2][3]提到了使用FreeLibraryCreateRemoteThread来卸载DLL,特别是在远程进程中的情况。需要注意卸载DLL的风险,比如资源未正确释放导致的问题。 然后,我需要组织步骤,分加载卸载两部分。每个步骤需要具体说明,并指出注意事项,比如平台依赖、权限问题等。同时,要确保提到不同方法的适用场景,比如在Unity中的处理与普通C#程序的区别,或者远程注入的特殊情况。 可能还需要补充一些用户未提到的点,比如使用P/Invoke在C#中调用LoadLibraryFreeLibrary,或者使用AppDomain来加载卸载程序集,尽管AppDomain在.NET Core中的支持有限。此外,要强调正确卸载的重要性,避免内存泄漏或程序崩溃。 最后,生成相关问题时要考虑用户可能进一步想知道的内容,比如具体API的使用示例、不同方法的优缺点、常见错误处理等。</think>### 动态加载卸载DLL的方法及实现步骤 #### 一、动态加载DLL的方法 1. **使用C#的`Assembly.LoadFrom()`方法** 在已知DLL文件路径时,可直接加载程序集: ```csharp Assembly sampleDll = Assembly.LoadFrom("C:\\Sample.dll"); ``` 此方法适用于需要直接调用DLL中的类或函数的情况[^2]。 2. **通过Windows API `LoadLibrary()`** 在C/C++或C#(通过P/Invoke)中调用系统API动态加载: ```csharp [DllImport("kernel32.dll")] public static extern IntPtr LoadLibrary(string dllPath); IntPtr dllHandle = LoadLibrary("Sample.dll"); ``` 需注意路径问题依赖库的加载顺序。 3. **Unity中加载DLL的特殊处理** - 将编译好的DLL文件放置在Unity项目的`Assets/Plugins`目录下。 - 通过`DllImport`特性调用DLL中的函数: ```csharp [DllImport("SampleDll")] public static extern int Add(int a, int b); ``` 需确保DLL的平台兼容性(如x86/x64)[^1]。 4. **远程线程注入加载** 通过`CreateRemoteThread`在目标进程中调用`LoadLibrary`: - 获取目标进程句柄(`OpenProcess`)。 - 在目标进程中分配内存写入DLL路径(`VirtualAllocEx` + `WriteProcessMemory`)。 - 创建远程线程执行`LoadLibrary`加载DLL[^4]。 #### 二、动态卸载DLL的方法 1. **使用Windows API `FreeLibrary()`** 配合`LoadLibrary`返回的句柄卸载: ```csharp [DllImport("kernel32.dll")] public static extern bool FreeLibrary(IntPtr hModule); FreeLibrary(dllHandle); // 传入LoadLibrary返回的句柄 ``` 2. **远程线程卸载DLL** 类似注入过程,但调用`FreeLibrary`: - 获取目标进程中已加载DLL的句柄。 - 通过`CreateRemoteThread`创建线程执行`FreeLibrary`[^3]。 3. **C#中通过AppDomain卸载** (仅限.NET Framework,.NET Core不支持): ```csharp AppDomain newDomain = AppDomain.CreateDomain("TempDomain"); newDomain.Load("Sample.dll"); AppDomain.Unload(newDomain); // 卸载整个域内的所有程序集 ``` 此方法会卸载整个应用程序域,可能影响其他资源。 #### 三、关键注意事项 1. **依赖关系** 动态加载DLL需满足依赖链完整,否则会触发`DllNotFoundException`。 2. **线程安全** 卸载DLL时需确保无代码正在执行其函数,否则可能导致崩溃。 3. **跨平台差异** Windows使用`LoadLibrary`/`FreeLibrary`,Linux需通过`dlopen`/`dlclose`实现类似功能。 4. **资源释放** 若DLL内部申请了内存或句柄,需在卸载前显式释放,避免资源泄漏。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值