Search code examples
dllinno-setupdllimportpascalscript

Possible to call DLL function in uninstaller if DLL has dontcopy flag?


As the title says I need a function in my DLL which I need to call while uninstalling.

The DLL is included this way

#define myProgData "C:\ProgramData\Foo\Bar"

[Files]
Source: "mydll.dll"; Flags: dontcopy

I already use one function while installing and now I want to know if I can use the same DLL for uninstall or does the DLL have to be copied so the uninstaller can access it?

I've tried a simple call already but got the

Could not call proc - Exception

So I'm looking for the reason for this.

I've tried to copy the DLL out of the setup, the call works just great then.

Here's what the script looks like

[Files]
Source: "myDll.dll"; Flags: dontcopy;
(...)
[Code]    
function myUninstallFunction(foo: Integer): Boolean;
external 'myFunction@{#myProgData}myDll.dll stdcall uninstallonly';

which is used this way

function InitializeUninstall(): Boolean;
begin
    if myUninstallFunction(1) then
    begin
        MsgBox(ExpandConstant('{cm:uninstallFail}'), mbError, MB_OK);
        Result := false;     
    end;
    (...)
end;

I've tried to use another event procedure too e.g. CurUninstallStepChanged(CurUninstallStep: TUninstallStep);.


Solution

  • You cannot call a DLL embedded in installer from uninstaller, as at uninstall time, the uninstaller is not aware of installer location (and it may not exist anymore anyway).

    It's possible to embed the DLL into uninstaller instead, but it's way more work. See my answer to How keep uninstall files inside uninstaller?

    Other than that, there's no better solution than you have found already:

    • Install the DLL somewhere
    • And reference the path to the installed DLL in the uninstall code.
    • Use UnloadDLL to unload the loaded the DLL, so that you can delete it. (I believe that .NET assembyl DLLs cannot be unloaded. For those you might copy the DLL to a temp folder, before loading, so that you can delete the installed copy. See Load external DLL for uninstall process in Inno Setup)

    Some background information to explain the behavior you are experiencing:

    There's files: prefix available in the external declaration that makes the Inno Setup automatically extract the DLL when the function is needed and delete the extracted DLL automatically.

    See Pascal Scripting: Using DLLs:

    During Setup, a special 'files:' prefix may also be used to instruct Setup to automatically extract one or more DLLs from the [Files] section before loading the first DLL.

    Example:

    procedure MyDllFuncSetup(hWnd: Integer; lpText, lpCaption: AnsiString; uType: Cardinal);
    external 'MyDllFunc@files:MyDll.dll stdcall setuponly';
    

    But it does not work in the uninstaller.

    You can see that even the official example CodeDll.iss uses the files: for an installer function, but resorts to using the installed DLL ({app}\) for an uninstaller function:

    [Files]
    ...
    ; Install our DLL to {app} so we can access it at uninstall time
    ; Use "Flags: dontcopy" if you don't need uninstall time access
    Source: "MyDll.dll"; DestDir: "{app}"
    
    [Code]
    ...
    procedure MyDllFuncSetup(hWnd: Integer; lpText, lpCaption: AnsiString; uType: Cardinal);
    external 'MyDllFunc@files:MyDll.dll stdcall setuponly';
    
    procedure MyDllFuncUninst(hWnd: Integer; lpText, lpCaption: AnsiString; uType: Cardinal);
    external 'MyDllFunc@{app}\MyDll.dll stdcall uninstallonly';
    

    Even the Inno Setup source code confirms that (Projects\main.pas function CodeRunnerOnDllImport):

    if Pos('files:', DllName) = 1 then begin
      if IsUninstaller then begin
        { Uninstall doesn't do 'files:' }
        DllName := '';
        ForceDelayLoad := True;
    

    That explains the "Could not call proc - Exception", as Inno Setup would behave as if the DLL name is not even specified, but delayload flag is there. The DLL is not found by the uninstaller and you get the cryptic error message (as you get normally with delayload flag only).

    You can also check that using both files: and uninstallonly gets you

    "uninstallonly" cannot be used with "files:"