【发布时间】:2012-02-19 11:25:30
【问题描述】:
我正在尝试从我的 .NET 进程中卸载行为不端的第三方 DLL,因为它似乎导致了一个问题,该问题总是通过重新启动我的应用程序来解决。 我不想重新启动应用程序,而是删除并重新加载 DLL。
DLL 正在使用 LoadLibrary 加载并使用 FreeLibrary 删除(使用从 P/Invoke 网站获取的 DllImports)。
当我调用 LoadLibrary() 时,我看到 DLL 出现在 Process Explorer 的 DLL 列表中,当我调用 FreeLibrary() 时,我看到 DLL 从 DLL 列表中消失 - 正如预期的那样。
但是,一旦我调用了第三方库的Initialize() 函数,FreeLibrary() 就不再从列表中删除 DLL,即使我事先调用了相应的Deinit() 方法。
调用库中的另一个函数没有这个问题。不过,我必须Initialise()这个库才可以使用!
我尝试通过在自己的AppDomain 中创建 DLL 来隔离 DLL,然后在 DLL 被释放后卸载此域。
我没有收到来自Initialize() 或Deinit()、来自LoadLibrary() 或FreeLibrary() 或来自创建或卸载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;
}
最后,我的 ThirdPartyModule 组装类:
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++ 编译的
- 不幸的是,我的进程仅限于使用 .NET 2(因此没有 WCF 解决方案)。
- 我使用的是 WinXP Pro x64 SP2,但解决方案必须兼容 XP、Win7 x32/x64 等。
- DLL 用于与 USB 令牌通信
【问题讨论】:
-
能否在另一个进程中运行 DLL 并通过 WCF 与它通信。当你不想卸载它时,你可以简单地杀死整个进程。
-
即使在 .NET 2.0 中,您仍然可以在另一个 appDomain 中加载 dll 并使用可用的域间通信方法,然后在您想要卸载 dll 时终止第二个 appDomain。
-
Win32 对 AppDomains 一无所知,因此 AppDomains 不会帮助处理本机 dll。将面向库的代码放入一个单独的进程中,选择合适的 IPC 方法,然后就可以了。 IPC 不必是 WCF,您可以使用 JohnL 建议的 HttpListener 或共享内存或邮槽或管道或普通套接字。选择取决于您的程序与本机 dll 的交互方式、其输入/输出的组成等。
-
您可能想尝试循环调用 FreeLibrary(Module) 直到它失败。 Windows 保持 DLL 的加载计数,只有当它变为零时才会卸载 DLL。
-
@500 — 这只是在掩盖问题。
FreeLibrary在第一次运行时不会卸载该 dll,这是有原因的。像这样强制卸载可能会泄漏资源,甚至会为段错误设置进程(例如,如果 dll 挂钩了其他 dll 的函数)。当它自己在一个进程中时,至少内核会释放内核资源,而其他进程的内存不会被破坏。