【问题标题】:Inno Setup: UnloadDLL does not work on uninstallInno Setup:UnloadDLL 在卸载时不起作用
【发布时间】:2017-12-18 07:01:36
【问题描述】:

我正在编写一个 Inno Setup 脚本,在卸载过程中我调用了一个自定义 DLL 来执行一些 Revert 操作。不幸的是,卸载完成后,DLL 和它的依赖项并没有被删除,尽管我调用了UnloadDLL 和 DeleteFile(它返回False)。 为什么UnloadDLL 会失败? 是否有可能使用LoadLibrary 加载动态DLL?我已经看到了一些与此相关的功能,但它们都已被弃用。 DLL 是用带有 C 接口的 Visual Studio 构建的。

代码如下:

function Revert(param: String): cardinal;
external 'Revert@{app}\Revert.dll cdecl delayload uninstallonly';

procedure RevertAll();
var
    param: String;
    dataDirectory: String;
    temp: String;
    i: Integer;
begin
    dataDirectory := ExpandConstant('{commonappdata}\MyAppData');
    StringChangeEx(dataDirectory, '\', '\\', True);
    param := '{"dataDirectory": "' + dataDirectory + '", "registryPath" : "SOFTWARE\\MyReg\\Key"}';

    Revert(param);

    temp := ExpandConstant('{app}\Revert.dll');
    for i := 0 to 10 do
    begin
        UnloadDLL(temp);
        Sleep(500);

        if DeleteFile(temp) then
            break;
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
    if (CurUninstallStep = usUninstall) then
    begin
        RevertAll();
    end 
end;

【问题讨论】:

标签: dll inno-setup uninstallation delete-file


【解决方案1】:

考虑到我在删除软件时尝试删除 DLL 时已经失去了多少头发,我想添加一个适度的贡献。 我尝试了一些事情,最终做了以下事情:

case CurUninstallStep of
      usUninstall:
        begin
          Exec('powershell.exe', ExpandConstant('-NoExit -ExecutionPolicy Bypass Start-Job -FilePath {%TEMP}\mySoft\CleanUtils.ps1 -ArgumentList {%TEMP}\mySoft\'), '', SW_HIDE, ewNoWait, ErrorCode);
          Exec('powershell.exe', ExpandConstant('-NoExit -ExecutionPolicy Bypass -Command "Start-Job -ScriptBlock {{ Remove-Item -Recurse -Force {%TEMP}\mySoft"'), '', SW_HIDE, ewNoWait, ErrorCode);
        end;
    end;

需要注意的几点:

  • UnloadDLL 对我也不起作用,尽管我已尽我所能。 InnoSetup 根本不会删除 DLL
  • 第一个 Exec 调用 PowerShell shell。注意-NoExit。否则,作业将直接退出而不运行。
  • StartJob 创建一个分离的后台进程,在卸载程序退出后运行。
  • 第一条指令执行以下 PowerShell 脚本:
# Input directory
$temp_dir=$args[0]

Get-ChildItem "$temp_dir" -Force -Filter "*.dll" |
  Foreach-Object {
    $file = $_.FullName

    while (Test-Path($file) )
    {
      try
      {
          remove-item -Recurse -Force $file
      }
      catch
      {
          Start-Sleep -seconds 5
      }
    }
  }

该脚本遍历 DLL 列表并删除文件夹中的所有 DLL。 第二个Exec 删除父文件夹。我本可以将该指令放入 Powershell 脚本中,但我怀疑我最终会遇到同样的问题,即 PowerShell 需要该文件,因此永远不会结束。 关于第二个Exec,另一个区别是作业是通过-Command 选项调用的。没有它,我会遇到以下错误:

Start-Job : Cannot bind parameter 'ScriptBlock'. Cannot convert the "-encodedCommand" value of type "System.String" to type "System.Management.Automation.ScriptBlock".
At line:1 char:11
+ Start-Job -ScriptBlock -encodedCommand IABHAGUAdAAtAFAAcgBvAGMAZQBzAH ...
+           ~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Start-Job], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.StartJobCommand

这远非完美,但这是我在尝试了近一周(!)之后能想到的最好的结果。

祝所有 Inno 伙伴好运!

【讨论】:

【解决方案2】:

不确定真正的问题是什么,但可以使用 Windows API 手动卸载 DLL:

function GetModuleHandle(moduleName: String): LongWord;
external 'GetModuleHandleW@kernel32.dll stdcall';

function FreeLibrary(module: LongWord): Integer;
external 'FreeLibrary@kernel32.dll stdcall';

var
  lib: LongWord; 
  res: integer;

repeat
  lib := GetModuleHandle('Revert.dll');
  res := FreeLibrary(lib);
until res = 0;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-05
    • 1970-01-01
    • 2012-11-16
    • 1970-01-01
    • 2015-10-01
    • 2017-08-15
    • 1970-01-01
    • 2018-05-04
    相关资源
    最近更新 更多