Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unload a .NET DLL from an unmanaged process

Tags:

I'm extending my Inno-Setup script with code that I can best implement in C# in a managed DLL. I already know how to export methods from a managed DLL as functions for use in an unmanaged process. It can be done by IL weaving, and there are tools to automate this:

  • NetDllExport (written by me)
  • UnmanagedExports

So after exporting, I can call my functions from Pascal script in an Inno-Setup installer. But then there's one issue: The DLL can't seem to be unloaded anymore. Using Inno-Setup's UnloadDLL(...) has no effect and the file remains locked until the installer exits. Because of this, the setup waits for 2 seconds and then fails to delete my DLL file from the temp directory (or install directory). In fact, it really stays there until somebody cleans up the drive.

I know that managed assemblies cannot be unloaded from an AppDomain anymore, unless the entire AppDomain is shut down (the process exits). But what does it mean to the unmanaged host process?

Is there a better way to allow Inno-Setup to unload or delete my DLL file after loading and using it?

like image 555
ygoe Avatar asked Feb 07 '15 22:02

ygoe


People also ask

Can you unload a DLL?

Short answer: No, it is impossible. Win32 doesn't provide an API to unload a DLL of another process. If a library is freed unexpectedly, the process will crash. This leads to a serious security hole as it breaks process protection mechanism.

How do I know if a DLL is managed or unmanaged?

To determine whether a DLL (or EXE) is managed or unmanaged, use dumpbin.exe with the /dependents switch. If you see mscoree. dll in the output, then the assembly is a managed assembly.


2 Answers

As suggested in other answers, you can launch a separate process at the end of the installation that will take care of the cleanup, after the installation processes finishes.

A simple solution is creating an ad-hoc batch file that loops until the DLL file can be deleted and then also deletes the (now empty) temporary folder and itself.

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; 
like image 106
Martin Prikryl Avatar answered Sep 28 '22 13:09

Martin Prikryl


You could add a batch script (in the form of running cmd -c) to be executed at the end of setup that waits for the file to be deletable and deletes it. (just make sure to set the inno option to not wait for the cmd process to complete)

You could also make your installed program detect and delete it on first execution.

like image 28
Sean K Avatar answered Sep 28 '22 12:09

Sean K