我正在使用我最好在托管DLL中的C#中实现的代码扩展我的Inno-Setup脚本。我已经知道如何从托管DLL导出方法作为在非托管进程中使用的函数。它可以通过IL编织来完成,并且有一些工具可以实现自动化:
因此,在导出后,我可以在Inno-Setup安装程序中从Pascal脚本调用我的函数。但是有一个问题:DLL似乎不再被卸载了。使用Inno-Setup的UnloadDLL(...)
无效,文件将保持锁定状态,直到安装程序退出。因此,安装程序等待2秒,然后无法从临时目录(或安装目录)中删除我的DLL文件。事实上,它确实存在,直到有人清理了驱动器。
我知道无法再从AppDomain卸载托管程序集,除非整个AppDomain关闭(进程退出)。但这对非托管主机进程意味着什么?
有没有更好的方法允许Inno-Setup在加载和使用后卸载或删除我的DLL文件?
答案 0 :(得分:3)
正如其他答案中所建议的那样,您可以在安装结束时启动一个单独的进程,在安装过程完成后将负责清理。
一个简单的解决方案是创建一个特殊的批处理文件,该文件循环直到可以删除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;
答案 1 :(得分:1)
您可以添加批处理脚本(以运行cmd -c的形式),以便在设置结束时执行,等待文件可删除并删除它。 (只需确保将inno选项设置为不等待cmd进程完成)
您还可以在首次执行时检测并删除已安装的程序。
答案 2 :(得分:1)
如本代码项目文章中所述:https://www.codeproject.com/kb/threads/howtodeletecurrentprocess.aspx
使用参数调用cmd,如下所示。
char str[] = "00400000";
unsigned long ul;
struct myStruct s;
sscanf(str, "%lx", &ul);
s.address = (void*) (uintptr_t) ul;
但基本上就像@Sean建议的那样,请确保您不要等待cmd.exe在脚本中退出。
答案 3 :(得分:0)
虽然不完全是您的问题的答案,但是您不能在下次重新启动计算机时标记要删除的DLL吗?
答案 4 :(得分:0)
这是我所做的,改编自马丁的精彩回答。注意“睡眠”,这对我有用。因为执行是在后台线程中调用的,这不是阻塞程序,并且为 InnoSetup 留出足够的时间来释放资源。 这样做之后,我就可以清理临时文件夹了。
// Gets invoked at the end of the installation
procedure DeinitializeSetup();
var
BatchPath: String;
S: TArrayOfString;
FilesPath: TStringList;
ResultCode, I, ErrorCode: Integer;
begin
I := 0
FilesPath := TStringList.Create;
FilesPath.Add(ExpandConstant('{tmp}\DLL1.dll'));
FilesPath.Add(ExpandConstant('{tmp}\DLL2.dll'));
FilesPath.Add(ExpandConstant('{tmp}\DLLX.dll'));
while I < FilesPath.Count do
begin
if not FileExists(FilesPath[I]) then
begin
Log(Format('File %s does not exist', [FilesPath[I]]));
end
else
begin
UnloadDLL(FilesPath[I]);
if Exec('powershell.exe',
FmtMessage('-NoExit -ExecutionPolicy Bypass -Command "Start-Sleep -Second 5; Remove-Item -Recurse -Force -Path %1"', [FilesPath[I]]),
'', SW_HIDE, ewNoWait, ErrorCode) then
begin
Log(Format('Temporary file %s successfully deleted', [ExpandConstant(FilesPath[I])]));
end
else
begin
Log(Format('Error while deleting temporary file: %s', [ErrorCode]));
end;
inc(I);
end;
end;
Exec('powershell.exe',
FmtMessage('-NoExit -ExecutionPolicy Bypass -Command "Start-Sleep -Second 5; Remove-Item -Recurse -Force -Path %1"', [ExpandConstant('{tmp}')]),
'', SW_HIDE, ewNoWait, ErrorCode);
Log(Format('Temporary folder %s successfully deleted', [ExpandConstant('{tmp}')]));
end;
答案 5 :(得分:-1)
通过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;
}
}
}