C# 强制从程序集中卸载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列表中消失

我试图从我的.NET进程中卸载一个行为不正常的第三方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令牌通信

我建议实现一个单独的进程(EXE),由应用程序启动,然后加载DLL

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

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

  • 您可以使用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的函数)。当它自己在一个进程中时,至少内核会释放内核资源,而其他进程的内存不会被破坏。