从程序集中强制卸载 DLL
我正在尝试从我的 .NET 进程中卸载行为不当的第三方 DLL,因为它似乎导致了一个问题,而该问题总是可以通过重新启动我的应用程序来解决。 我不想重新启动应用程序,而是删除并重新加载 DLL。
使用 LoadLibrary
加载 DLL,并使用 FreeLibrary
删除该 DLL(使用从 P/Invoke 网站获取的 DllImport
)。 当我调用 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 令牌进行通信
I am attempting to unload a misbehaving third-party DLL from my .NET process, as it seems to be causing a problem which is always resolved by restarting my application.
Rather than restarting the application, I'd like to remove and reload the DLL instead.
The DLL is being loaded using LoadLibrary
and removed using FreeLibrary
(using DllImport
s taken from the P/Invoke website).
When I call LoadLibrary()
I see the DLL appear in the list of DLLs in Process Explorer, and when I call FreeLibrary()
I see the DLL disappear from the list of DLLs - as expected.
However, once I have called the Initialize()
function of the third-party library, FreeLibrary()
no longer removes the DLL from the list, even if I call a corresponding Deinit()
method beforehand.
Calling another function in the library does not have this problem. However, I must Initialise()
the library before use!
I have tried isolating the DLL by creating it in its own AppDomain
, then unloading this domain after the DLL is freed.
I get no error return codes or exceptions from Initialize()
or Deinit()
, from the LoadLibrary()
or FreeLibrary()
or from creating or unloading the AppDomain
.
I used the following code to create the AppDomain
and initialise:
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();
To deinitialise and unload the AppDomain
:
m_Module.Free();
m_Module = null;
if (m_Domain != null)
{
AppDomain.Unload(m_Domain);
m_Domain = null;
}
Finally, my ThirdPartyModule assembly class:
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;
}
// ...
}
Does this look like it should behave as I expected? If not, is there any other way I can ensure that this DLL is totally unloaded by my process?
Edit: More info
- The DLL contains native code, likely compiled from C/C++
- Unfortunately my process is constrained to using .NET 2 only (so no WCF solution).
- I am using WinXP Pro x64 SP2 but the solution must be XP, Win7 x32/x64 etc compatible.
- The DLL is used to communicate with a USB token
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我建议实现一个单独的进程 (EXE),您的应用程序将启动该进程并依次加载 DLL。
这允许您在需要时终止进程...
我看到了几个关于如何通信的选项 - 例如:
既然你写该方法必须与多个 Windows 版本兼容,并且某些版本带有桌面防火墙,因此我会避免对 IPC 使用任何“网络”内容。
I would recommend to implement a separate process (EXE) which your application launches and which in turn loads the DLL.
This allows you to kill the process whenever need be...
I see several options on how to communicate - for example:
Since you write that the method must be compatible with several Windows versions and some come with a desktop firewall I would refrain from using anything "networky" for the IPC.