Comment utiliser Windows Installer pour installer, et désinstaller proprement un dossier optionnel ?

L'un des aspects les plus importants quand on crée des MSI réside dans la désinstallation. Celle-ci doit respecter la maxime "Veuillez laisser cet endroit aussi propre que vous l'avez trouvé". Oui, cela fait penser à autre chose, et cela est volontaire.

Trop d'applications laissent des traces après leur passage. Cela peut être sous forme de fichiers, dossiers, ou clés dans la base de registre.

Avec Wix Toolset, les fichiers, dossiers, et clés de registre sont automatiquement supprimés lors de la désinstallation. Il y a cependant un cas problématique : les dossiers qui ne sont pas créés lors de l'installation. Windows Installer n'ayant pas connaissance de ceux-ci, il ne se permet pas de les supprimer. Si en plus, ces dossiers sont "optionnels", la gestion de leur présence, ou de leur absence lors de l'installation, et de la désinstallation se complique.

Avec Wix, il est possible de coder une action personnalisée pour l'installation, et une autre pour la désinstallation. Cette approche est un peu lourde pour ce que l'on a besoin de faire. Heureusement, il existe une solution plus simple. Il suffit de demander à Windows Installer de gérer un dossier vide comme étant l'un des composants de l'application.

Pour commencer, il faut déclarer le dossier. Pour mon exemple, je déclare un dossier MonDossierVide avec l'Id DOSSIER_VIDE.


<Fragment>
  <StandardDirectory Id="ProgramFiles6432Folder">
    <Directory Id="ManufacturerFolder" Name="!(bind.Property.Manufacturer)">
      ...
      <Directory Id="DOSSIER_VIDE" Name="MonDossierVide"/>
      ...
    </Directory>
  </StandardDirectory>
</Fragment>

Il faut ensuite déclarer un composant.


<DirectoryRef Id="DOSSIER_VIDE">
  <Component Id="ComposantDossierVide"
      Guid="{45D88591-3259-465D-AAAF-6248B7CCBD32}"
      Condition="UTILISER_DOSSIER_VIDE = 1">
      <CreateFolder />
  </Component>
</DirectoryRef>

Ce composant doit impérativement avoir :

  • une référence vers le dossier. Dans le cas présent, j'utilise un DirectoryRef pour cela, et je lui donne l'Id du dossier à créer. Exemple : DOSSIER_VIDE.
  • un Id défini. Exemple {45D88591-3259-465D-AAAF-6248B7CCBD32}.
  • un nœud CreateFolder.

Si en plus on ajoute une condition sur le composant, Windows Installer ne gérera le dossier que si la condition est respectée. Il ne créera le dossier que si la condition est vraie. Le dossier étant créé via un composant, il sera automatiquement supprimé lors de la désinstallation.

Bien évidemment, il faudra penser à référencer ComposantDossierVide dans liste des composants à installer.

Jérémy Jeanson

Pourquoi la fin du support de .net 7 est-elle un jalon important pour vos applications .net core, .net 6, ou 8 ?

En 2024, les développeurs .net commencent à être habitués aux acronymes STS/LTS associés aux différentes versions de .net. Mais tous ne saisissent pas forcément les implications de ceux-ci. Le plus souvent la réponse apportée peu se résume dans le tableau suivant :

DéfinitionAcronymeIdentificationDurée du support
Long Term SupportLTSVersion paire de .net3 ans
Standard Term SupportSTSVersion impaire de .net1 an et demi

Alors, oui certaines personnes peuvent confondre le premier S de STS avec Short, mais la vraie méprise n'est pas là.

Le statut "Maintenance support", cela vous parle ?

Il s'agit de la situation dans laquelle se trouve toute version de .net quand elle arrive dans ses 6 derniers mois de support. Durant cette période, vous ne recevrez que des correctifs de sécurités. Cela signifie que si un bug venait à être découvert durant cette période, il ne serait pas corrigé. Il vous serait juste recommandé de passer à une version supérieure.

Tout cela est expliqué sur la page ".NET and .NET Core Support Policy".

Maintenant, vous voyez peut-être où je souhaite en venir. La version 7 de .net arrive en fin de vie le 14 mai 2024, cela signifie que :

  • .net 7 est déjà limité à un support de type maintenance depuis novembre 2023.
  • .net 6 sera limité à un support de type maintenance en mai 2024.
  • .net 8 sera la seule version avec un support actif passé mai 2024.

Moralité

La fin de support de .net 7 est un jalon très important. Il est fortement recommandé de passer à .net 8 avant cette date. Voilà pourquoi les développeurs .net doivent migrer leurs applications dans les 6 mois qui suivent la publication d'une nouvelle version LTS. Considérer que vous avez davantage de temps pour le faire est une erreur. Que vous utilisiez une version LTS ou STS.

Jérémy Jeanson

Comment supprimer le pool récalcitrant d'une application inexistante dans IIS?

Voici une situation peu ragoutante que j'ai rencontrée il y a déjà quelque temps. IIS ne permettait pas la suppression d'un pool.

Heureusement, tout IIS est pilotable via PowerShell. Il suffit de connaitre les bonnes commandes.

L'application bloquant la suppression du pool n'était pas visible via la console. Dans un tel cas, il faut donc commencer par identifier les sites, et les applications que IIS ne semble pas vouloir afficher. Pour cela, il y a deux commandes à connaitre :


Get-Website
Get-WebApplication

Pour l'exemple, imaginons que je trouve une application app1 qui se trouvait dans le site web site1. Pour pouvoir supprimer cette application, il faut donc utiliser la commande :


Remove-WebApplication -Name app1 -Site site1 

Pour finir, le pool nommé pool1 dans mon exemple peut être supprimé via cette commande :


Remove-WebAppPool –Name pool1

Jérémy Jeanson

Comment réaliser des tests unitaires sur l'input d'un composant Blazor ?

L'une des choses que je préfère avec Blazor, consiste dans la possibilité de tester intégralement un composant via bUnit. Un bon exemple valant toujours mieux que de long discourt, voici un cas simple permettant de résoudre deux problématiques :

  • Le besoin de modifier un input.
  • Le besoin de consulter le contenu d'un input.

Pour commencer, on peut initialiser un contexte de test, et effectuer un rendu d'un composant :


// Création du context de test 
using var context = new TestContext();

// Création du composent et rendu
var obj = context.RenderComponent<MonComposent>();


Pour modifier une input dont l'Id serait inputId, on peut utiliser le code :


obj.Find("#inputId").Change("Ma valeur");


Pour consulter le contenu d'un input dont l'ID serait inputId, on peut utiliser le code :


var actual  = obj.Find("#inputId").GetAttribute("value");


Pour finir, voici un exemple complet :


[Theory] 
[InlineData("Foo")] 
public void Exemple(String value) 
{
  // Création du context de test 
  using var context = new TestContext(); 

  // Création du composent et rendu 
  var obj = context.RenderComponent<MonComposent>(); 

  // Modification
  obj.Find("#inputId").Change(value); 

  // … ajouter des interactions avec le composant ici 

  // Vérifier la valeur de l'input 
  var actual= obj.Find("#inputId").GetAttribute("value"); 
  actual.Should().BeEquivalentTo(value);  
} 

Conclusion

Voici du code simple et efficace. Tout ce que j'aime!

Jérémy Jeanson

Comment passer des paramètres à une action deferred de Windows Installer ?

Dans un précédent article, je présentais la démarche à suivre pour exécuter une action custom avec des privilèges élevés. Cette approche passe par l'exécution de l'action dans un contexte deferred. Celui-ci a un impact sur la manière d'accéder aux propriétés via l'action custom.

Habituellement, WiX toolset permet d'utiliser la syntaxe qui suit pour accéder à des paramètres lors de l'installation. Dans le cadre d'une action custom deferred, le code suivant n'est pas utilisable.


[CustomAction] 
public static ActionResult DoSomething(Session session) 
{ 
  var destination = session["INSTALLFOLDER"]; 
  // … 
}

Il doit être remplacé par :


[CustomAction] 
public static ActionResult DoSomething(Session session) 
{ 
  var destination = session.CustomActionData["INSTALLFOLDER"]; 
  // … 
}

CustomActionData est alimenté via la propriété Value de l'action custom. Le contenu accepte une syntaxe du style nom1=valuer1;nom2=valeur2;....

Malheureusement, cette propriété ne peut pas être affectée directement. Il faut ajout une seconde action custom pour cela. Dans l'exemple suivant, j'ai créé une action custom SetDoSomethingValue qui affecter les propriétés que j'utilise dans l'action custom DoSomething.


<!-- Actions personnalisées --> 
<Binary Id="CustomActions" 
    SourceFile="CustomActions.CA.dll"/> 

<CustomAction Id="DoSomething" 
    BinaryRef="CustomActions" 
    DllEntry="DoSomething" 
    Execute="deferred" 
    Return="check" 
    Impersonate="no"/> 

<CustomAction Id="SetDoSomethingValue" 
    Property="DoSomething" 
    Value="INSTALLFOLDER=[INSTALLFOLDER]"/>

Ben évidemment, l'action custom SetDoSomethingValue doit être ajoutée à la section InstallExecuteSequence, et s'exécuter avant l'action DoSomething.


<InstallExecuteSequence>
  <Custom Action="DoSomething" Before="InstallFinalize" Condition="NOT REMOVE"/> 
  <Custom Action="SetDoSomethingValue" Before="DoSomething" Condition="NOT REMOVE"/> 
</InstallExecuteSequence>

Jérémy Jeanson