dJonzo
dJonzo

Reputation: 21

Inno Setup: UnloadDLL does not work on uninstall

I'm working on an Inno Setup script and during uninstall I call a custom DLL to do some Revert operation. Unfortunately, after uninstall is completed the DLL and it's dependencies were not removed, despite the fact I called UnloadDLL and DeleteFile (which returns False). Why does UnloadDLL fail? Is there a possibility to load the DLL dynamic with LoadLibrary? I have seen some functions regarding this, but they are all deprecated. The DLL is built with Visual Studio with C interface.

Here's the code:

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;

Upvotes: 1

Views: 599

Answers (2)

dJonzo
dJonzo

Reputation: 21

Not sure what is the real problem, but unload the DLL manually with Windows API works:

function GetModuleHandle(moduleName: String): LongWord;
external '[email protected] stdcall';

function FreeLibrary(module: LongWord): Integer;
external '[email protected] stdcall';

var
  lib: LongWord; 
  res: integer;

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

Upvotes: 1

Razique
Razique

Reputation: 131

I would like to add a modest contribution given how many hairs I have lost myself trying to remove a DLL upon software removal. I tried a few things and ended up doing the following:

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;

A few things to notice:

  • The UnloadDLL didn't work for me either, despite trying everything I could. The DLL would simply not get removed by InnoSetup
  • The first Exec invokes a PowerShell shell. Notice the -NoExit. Without that, the job would simply exit without running.
  • The StartJob creates a detached background process that runs after the uninstaller quits.
  • The first instruction executes the following PowerShell script:
# 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
      }
    }
  }

That script iterates over the list of DLLs and removes all the DLLs in the folder. The second Exec deletes the parent folder. I could have put that instruction into the Powershell script, but I suspect that I would have ended up with the same problem, that is, PowerShell needing that file, thus never ending. Regarding that second Exec, the other difference is that the job is invoked via a -Command option. Without that, I would encounter the following error:

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

This is far from perfect, but this is the best I could come up with after almost one week(!) of trying bunch of different things.

Good luck to all the Inno fellows!

Upvotes: 1

Related Questions