Décharger une DLL .NET à partir d’un processus non géré

J’étends mon script Inno-Setup avec du code que je peux mieux implémenter en C # dans une DLL gérée. Je sais déjà comment exporter des méthodes à partir d’une DLL gérée en tant que fonctions à utiliser dans un processus non géré. Cela peut être fait par tissage IL, et il existe des outils pour automatiser cela:

  • NetDllExport (écrit par moi)
  • Exportations non gérées

Ainsi, après l’exportation, je peux appeler mes fonctions à partir d’un script Pascal dans un programme d’installation Inno-Setup. Mais il y a un problème: la DLL ne semble plus être déchargée. L’utilisation de UnloadDLL(...) Inno-Setup n’a aucun effet et le fichier rest verrouillé jusqu’à la fermeture du programme d’installation. De ce fait, l’installation attend 2 secondes, puis ne parvient pas à supprimer mon fichier DLL du répertoire temporaire (ou du répertoire d’installation). En fait, il rest vraiment là jusqu’à ce que quelqu’un nettoie le lecteur.

Je sais que les assemblys gérés ne peuvent plus être déchargés d’un AppDomain, à moins que l’intégralité de l’AppDomain soit fermée (le processus se termine). Mais qu’est-ce que cela signifie pour le processus hôte non géré?

Existe-t-il un meilleur moyen d’autoriser Inno-Setup à décharger ou à supprimer mon fichier DLL après l’avoir chargé et utilisé?

Comme suggéré dans d’autres réponses, vous pouvez lancer un processus distinct à la fin de l’installation, qui se chargera du nettoyage, une fois les processus d’installation terminés.

Une solution simple consiste à créer un fichier de commandes ad-hoc qui sera mis en boucle jusqu’à ce que le fichier DLL puisse être supprimé, puis le dossier temporaire (maintenant vide) et lui-même.

 procedure DeinitializeSetup(); var FilePath: ssortingng; BatchPath: ssortingng; S: TArrayOfSsortingng; 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 SaveSsortingngsToFile(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; 

Vous pouvez append un script de lot (sous la forme de l’exécution de cmd -c) à exécuter à la fin de l’installation, qui attend que le fichier soit supprimé et le supprime. (assurez-vous simplement de définir l’option inno pour ne pas attendre la fin du processus cmd)

Vous pouvez également demander à votre programme installé de le détecter et de le supprimer lors de la première exécution.

Comme suggéré dans cet article de projet de code: https://www.codeproject.com/kb/threads/howtodeletecurrentprocess.aspx

appeler une cmd avec des arguments comme indiqué ci-dessous.

  Process.Start("cmd.exe", "/C ping 1.1.1.1 -n 1 -w 3000 > Nul & Del " + Application.ExecutablePath); 

Mais, comme @Sean l’a suggéré, assurez-vous de ne pas attendre que le cmd.exe se ferme dans votre script.

Bien que ce ne soit pas exactement une réponse à votre question, ne pouvez-vous pas simplement marquer la DLL à supprimer au prochain redémarrage de l’ordinateur?

Le moyen le plus simple de faire ce que vous voulez consiste à utiliser un AppDomain. Vous pouvez décharger un AppDomain, mais pas le premier. La solution consiste donc à créer un nouveau domaine AppDomain, à y charger votre DLL gérée, puis à décharger le domaine AppDomain.

  AppDomain ad = AppDomain.CreateDomain("Isolate DLL"); Assembly a = ad.Load(new AssemblyName("MyManagedDll")); object d = a.CreateInstance("MyManagedDll.MyManagedClass"); Type t = d.GetType(); double result = (double)t.InvokeMember("Calculate", BindingFlags.InvokeMethod, null, d, new object[] { 1.0, 2.0 }); AppDomain.Unload(ad); 

Voici à quoi ressemble le code DLL …

 namespace MyManagedDll { public class MyManagedClass { public double Calculate(double a, double b) { return a + b; } } }