从程序集中强制卸载 DLL

发布于 2024-12-29 03:12:42 字数 2246 浏览 0 评论 0原文

我正在尝试从我的 .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 DllImports 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

段念尘 2025-01-05 03:12:42

我建议实现一个单独的进程 (EXE),您的应用程序将启动该进程并依次加载 DLL。

这允许您在需要时终止进程...

我看到了几个关于如何通信的选项 - 例如:

  • 您可以使用 COM(如果您将其实现为进程外 COM 服务器)
  • 您可以使用共享内存(非常高的性能,请参阅 进行演练,this 对于 .NET 2 包装器)

既然你写该方法必须与多个 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:

  • you could use COM (if you implement it as an out-of-process COM server)
  • you could use shared memory (very high performance, see this for a walkthrough and this for a .NET 2 wrapper)

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文