C# 强制从程序集中卸载DLL
我试图从我的.NET进程中卸载一个行为不正常的第三方DLL,因为它似乎导致了一个问题,而这个问题总是可以通过重新启动我的应用程序来解决。 我想删除并重新加载DLL,而不是重新启动应用程序 正在使用C# 强制从程序集中卸载DLL,c#,dll,process,appdomain,C#,Dll,Process,Appdomain,我试图从我的.NET进程中卸载一个行为不正常的第三方DLL,因为它似乎导致了一个问题,而这个问题总是可以通过重新启动我的应用程序来解决。 我想删除并重新加载DLL,而不是重新启动应用程序 正在使用LoadLibrary加载DLL,并使用freebrary删除DLL(使用从p/Invoke网站获取的DllImports)。 当我调用LoadLibrary()时,我看到DLL出现在Process Explorer中的DLL列表中,当我调用freellibrary()时,我看到DLL从DLL列表中消失
LoadLibrary
加载DLL,并使用freebrary
删除DLL(使用从p/Invoke网站获取的DllImport
s)。
当我调用LoadLibrary()
时,我看到DLL出现在Process Explorer中的DLL列表中,当我调用freellibrary()
时,我看到DLL从DLL列表中消失-正如预期的那样
但是,一旦调用了第三方库的Initialize()
函数,FreeLibrary()
就不再从列表中删除DLL,即使我事先调用了相应的Deinit()
方法。
调用库中的另一个函数没有此问题。但是,我必须在使用前初始化库()
我已尝试通过在自己的AppDomain
中创建DLL来隔离DLL,然后在释放DLL后卸载此域
我从Initialize()
或Deinit()
、从LoadLibrary()
或freebrary()
或创建或卸载AppDomain
中未获得错误返回代码或异常
我使用以下代码创建AppDomain
并初始化:
string pathToDll = Assembly.GetExecutingAssembly().CodeBase;
m_Domain = AppDomain.CreateDomain("MyAppDomain", null, new AppDomainSetup { PrivateBinPath = pathToDll });
m_Module = (ThirdPartyModule)m_Domain.CreateInstanceFromAndUnwrap(pathToDll, typeof(ThirdPartyModule).FullName);
m_Module.Init();
要取消初始化并卸载AppDomain
:
m_Module.Free();
m_Module = null;
if (m_Domain != null)
{
AppDomain.Unload(m_Domain);
m_Domain = null;
}
最后,我的第三个PartyModule程序集类:
internal class ThirdPartyModule : MarshalByRefObject
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool FreeLibrary(IntPtr hModule);
public IntPtr Module { get; set; }
public ThirdPartyModule()
{
Module = LoadLibrary("Misbehaving.dll");
}
public void Free()
{
FreeLibrary(Module);
Module = IntPtr.Zero;
}
// ...
}
这看起来应该像我期望的那样吗?如果没有,是否有其他方法可以确保我的进程完全卸载此DLL
编辑:更多信息
- DLL包含本机代码,可能是从C/C编译的++
- 不幸的是,我的流程仅限于使用.NET2(因此没有WCF解决方案)
- 我使用的是WinXP Pro x64 SP2,但解决方案必须与XP、Win7 x32/x64等兼容
- DLL用于与USB令牌通信
- 您可以使用COM(如果您将其实现为进程外COM服务器)
- 您可以使用共享内存(非常高的性能,有关演练和.NET2包装器,请参阅)
由于您写道,该方法必须与多个Windows版本兼容,有些版本还带有桌面防火墙,因此我不会对IPC使用任何“网络”功能。您能否在另一个进程中运行DLL,并通过WCF与之通信。如果不想卸载整个进程,您可以简单地将其终止。即使在.NET 2.0中,您也可以在另一个appDomain中加载dll,并使用可用的域间通信方法,然后在希望卸载dll时终止第二个appDomain。Win32对AppDomains一无所知,因此AppDomains对本机dll没有帮助。将面向库的代码放在一个单独的进程中,选择一个合适的IPC方法,就可以设置了。IPC不必是WCF,您可以按照JohnL的建议使用HttpListener,也可以使用共享内存、邮件槽、管道或普通套接字。选择取决于您的程序如何与本机dll交互,其输入/输出由什么组成等。您可能希望尝试在循环中调用FreeLibrary(模块),直到失败。Windows保留DLL的加载计数,只有当它变为零时才卸载DLL。@500-这只是掩盖了问题
FreeLibrary
不会在第一次运行时卸载该dll,这是有原因的。强制这样的卸载可能会泄漏资源,甚至为segfault设置进程(例如,如果dll连接了其他dll的函数)。当它自己在一个进程中时,至少内核会释放内核资源,而其他进程的内存不会被破坏。