【问题标题】:Force unloading of DLL from assembly强制从程序集中卸载 DLL
【发布时间】: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 的函数)。当它自己在一个进程中时,至少内核会释放内核资源,而其他进程的内存不会被破坏。

标签: c# dll process appdomain


【解决方案1】:

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

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

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

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

由于您写道该方法必须与多个 Windows 版本兼容,并且有些版本带有桌面防火墙,因此我会避免对 IPC 使用任何“网络化”的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-06
    • 1970-01-01
    • 1970-01-01
    • 2010-12-14
    • 2012-11-06
    • 1970-01-01
    相关资源
    最近更新 更多