从非托管进程卸载.NET DLL

我正在使用我最好在托管DLL中的C#中实现的代码扩展我的Inno-Setup脚本。 我已经知道如何从托管DLL导出方法作为在非托管进程中使用的函数。 它可以通过IL编织来完成,并且有一些工具可以实现自动化:

  • NetDllExport (由我编写)
  • UnmanagedExports

因此,在导出后,我可以在Inno-Setup安装程序中从Pascal脚本调用我的函数。 但是有一个问题:DLL似乎不再被卸载了。 使用Inno-Setup的UnloadDLL(...)无效,文件将保持锁定状态,直到安装程序退出。 因此,安装程序等待2秒,然后无法从临时目录(或安装目录)中删除我的DLL文件。 事实上,它确实存在,直到有人清理驱动器。

我知道无法再从AppDomain卸载托管程序集,除非整个AppDomain关闭(进程退出)。 但是对于非托管主机进程意味着什么呢?

是否有更好的方法允许Inno-Setup在加载和使用后卸载或删除我的DLL文件?

正如在其他答案中所建议的那样,您可以在安装结束时启动一个单独的进程,该进程将在安装过程完成后进行清理。

一个简单的解决方案是创建一个特殊的批处理文件,该文件循环直到可以删除DLL文件,然后还删除(现在为空)临时文件夹及其自身。

 procedure DeinitializeSetup(); var FilePath: string; BatchPath: string; S: TArrayOfString; ResultCode: Integer; begin FilePath := ExpandConstant('{tmp}\MyAssembly.dll'); if not FileExists(FilePath) then begin Log(Format('File %s does not exist', [FilePath])); end else begin BatchPath := ExpandConstant('{%TEMP}\') + 'delete_' + ExtractFileName(ExpandConstant('{tmp}')) + '.bat'; SetArrayLength(S, 7); S[0] := ':loop'; S[1] := 'del "' + FilePath + '"'; S[2] := 'if not exist "' + FilePath + '" goto end'; S[3] := 'goto loop'; S[4] := ':end'; S[5] := 'rd "' + ExpandConstant('{tmp}') + '"'; S[6] := 'del "' + BatchPath + '"'; if not SaveStringsToFile(BatchPath, S, False) then begin Log(Format('Error creating batch file %s to delete %s', [BatchPath, FilePath])); end else if not Exec(BatchPath, '', '', SW_HIDE, ewNoWait, ResultCode) then begin Log(Format('Error executing batch file %s to delete %s', [BatchPath, FilePath])); end else begin Log(Format('Executed batch file %s to delete %s', [BatchPath, FilePath])); end; end; end; 

您可以添加一个批处理脚本(以运行cmd -c的forms),在设置结束时执行,等待文件可删除并删除它。 (只需确保将inno选项设置为不等待cmd进程完成)

您还可以在首次执行时检测并删除已安装的程序。

正如本代码项目文章中所述: https : //www.codeproject.com/kb/threads/howtodeletecurrentprocess.aspx

使用参数调用cmd,如下所示。

  Process.Start("cmd.exe", "/C ping 1.1.1.1 -n 1 -w 3000 > Nul & Del " + Application.ExecutablePath); 

但基本上就像@Sean建议的那样,请确保您不要等待cmd.exe在脚本中退出。

虽然不完全是您的问题的答案,但是您不能在下次重新启动计算机时标记要删除的DLL吗?

做你想做的事的简单方法是通过AppDomain。 您可以卸载AppDomain,而不是初始AppDomain。 因此,解决方案是创建一个新的AppDomain,在其中加载托管DLL,然后卸载AppDomain。

  AppDomain ad = AppDomain.CreateDomain("Isolate DLL"); Assembly a = ad.Load(new AssemblyName("MyManagedDll")); object d = a.CreateInstance("MyManagedDll.MyManagedClass"); Type t = d.GetType(); double result = (double)t.InvokeMember("Calculate", BindingFlags.InvokeMethod, null, d, new object[] { 1.0, 2.0 }); AppDomain.Unload(ad); 

这是DLL代码的样子……

 namespace MyManagedDll { public class MyManagedClass { public double Calculate(double a, double b) { return a + b; } } }