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