Moderniser un logiciel ancien : de Delphi, VB6 et MFC vers .NET, étape par étape

Laborarbeitsplatz mit Pipette, Proben und Messsoftware auf dem Laptop

Architecture logicielle et UML

Le logiciel fonctionne depuis vingt ans, les clients comptent sur lui – et le seul qui connaisse vraiment le code Delphi part à la retraite l'année prochaine. Tout réécrire ? Cela prend des années et n'apporte de bénéfice qu'à la fin. Il existe une meilleure voie : pièce par pièce, sans arrêter l'exploitation.

Pourquoi maintenant – et pourquoi pas tout d'un coup

Pour Visual Basic 6, la situation est claire. Microsoft ne prend plus en charge l'environnement de développement depuis le 8 avril 2008. Selon Microsoft, le runtime fonctionne aussi sous Windows 11, mais il n'est pris en charge qu'en cas de régressions graves et de failles de sécurité critiques. Microsoft recommande expressément de remplacer les applications VB6. Delphi et MFC, en revanche, continuent d'être maintenus. Là, la pression vient d'ailleurs : moins de développeurs qui comprennent le code, une interface vieillissante, des tests manquants, des bibliothèques sans mises à jour.

Personne ne démolit un pont sur lequel la circulation passe chaque jour pour en construire un nouveau. On construit à côté et on transfère voie par voie. Martin Fowler a nommé ce modèle d'après le figuier étrangleur, qui enveloppe lentement un arbre : le nouveau code pousse autour de l'ancien jusqu'à ce que celui-ci puisse être arrêté. Chaque étape est mise en production et apporte immédiatement un bénéfice.

Cinq étapes

  1. Inventaire : recenser les modules, la base de données, les interfaces et les formulaires. Quelles parties changent souvent, lesquelles causent le plus d'erreurs ?
  2. Consigner le comportement : exécuter l'ancien logiciel avec des entrées réelles et enregistrer les résultats. Ces « golden masters » sont la référence pour chaque nouvelle ligne.
  3. Construire un pont : l'ancien et le nouveau doivent communiquer tant que les deux existent.
  4. Migrer module par module : commencer par la partie qui change le plus souvent ou qui fait le plus mal. Livrer chaque étape.
  5. Arrêter : ce n'est que lorsque plus rien n'appelle l'ancienne partie qu'elle disparaît.

Des ponts entre l'ancien et le nouveau

  • Delphi : l'ancien noyau de calcul est rendu accessible sous forme de DLL avec des exports stdcall, et .NET l'appelle par P/Invoke. Autre possibilité : communiquer via COM.
  • VB6 : les nouvelles parties sont créées en .NET et mises à disposition comme serveur COM. Avec EnableComHosting, le SDK génère une .comhost.dll, enregistrée avec regsvr32. Deux pièges : VB6 est en 32 bits, mais l'enveloppe COM est par défaut en 64 bits. Et .NET ne génère plus de bibliothèque de types. Pour une liaison précoce dans VB6, vous l'écrivez vous-même en IDL ou vous liez tardivement via CreateObject.
  • MFC : C++/CLI relie code natif et code .NET dans le même processus. Les nouvelles boîtes de dialogue sont créées en WPF ou WinForms et intégrées dans les fenêtres MFC.

Voici le pont Delphi en miniature. L'ancien noyau :

library Messkern;



// Ancien noyau de calcul, exposé sous forme de DLL avec stdcall

function Mittelwert(Werte: PDouble; Anzahl: Integer): Double; stdcall;

var

  I: Integer;

  Summe: Double;

begin

  Summe := 0;

  for I := 0 to Anzahl - 1 do

  begin

    Summe := Summe + Werte^;

    Inc(Werte);

  end;

  if Anzahl > 0 then

    Result := Summe / Anzahl

  else

    Result := 0;

end;



exports

  Mittelwert;



begin

end.

Et l'appel depuis C#, directement avec la comparaison avec la nouvelle implémentation :

using System;

using System.Linq;

using System.Runtime.InteropServices;



double[] messreihe = { 10.2, 10.4, 10.1, 10.3 };

double alt = Messkern.Mittelwert(messreihe, messreihe.Length);

double neu = messreihe.Average();

// Golden master : le nouveau code doit fournir ce que fournissait l'ancien

Console.WriteLine(Math.Abs(alt - neu) < 1e-9 ? "gleich" : $"Abweichung: {alt} / {neu}");



internal static class Messkern

{

    // Si la DLL est en 32 bits, le processus .NET doit aussi tourner en x86

    [DllImport("Messkern.dll", CallingConvention = CallingConvention.StdCall)]

    internal static extern double Mittelwert(double[] werte, int anzahl);

}

Pièges liés aux types de données

  • Virgule flottante : sous Win32, le type Extended de Delphi occupe dix octets, un double .NET huit. Les résultats diffèrent donc dans les dernières décimales. Comparez avec une tolérance.
  • Date : le TDateTime de Delphi et le Date de VB6 comptent les jours depuis le 30/12/1899 sous forme de nombre à virgule flottante. DateTime.FromOADate les convertit.
  • Monnaie : dans les deux langages, Currency est un entier 64 bits avec quatre décimales fixes. En .NET, il devient decimal, et non double.
  • Textes : un string Delphi ne doit jamais franchir une frontière de DLL. Utilisez PWideChar ou WideString. Les anciennes versions de Delphi travaillent avec des textes ANSI, le plus souvent en page de codes 1252.

Retour d'expérience

Un fabricant d'appareils de mesure utilisait depuis des décennies un logiciel Delphi développé en interne. Au départ, il y a eu du conseil et de la planification : nous avons mis en regard les objectifs et les possibilités des nouvelles technologies, et des prototypes ont montré à quoi les solutions pourraient ressembler. Nous avons ensuite formé sur place toute l'équipe du client. Une équipe projet de dix personnes porte le logiciel de façon agile vers C# et WPF, avec MariaDB et Entity Framework. Le nouveau logiciel est déjà utilisé en interne et convainc aussi bien les techniciens de mesure que le service commercial. Pour une surcouche de génie civil en C++, MFC et ObjectARX, un calcul de rentabilité a montré au préalable à partir de quand le portage serait rentabilisé.

Erreurs typiques

  • Tout refaire, tout d'un coup : aucun bénéfice pendant des années, et à la fin il manque la moitié.
  • Réécrire de mémoire plutôt que d'après le comportement mesuré.
  • Corriger en silence d'anciens bugs sur lesquels comptent les utilisateurs ou les systèmes en aval.
  • Mélanger 32 et 64 bits. Une DLL 32 bits dans un processus 64 bits se termine par une BadImageFormatException.
  • Faire passer des textes à travers la frontière sans règle claire. Le résultat : des plantages ou des caractères accentués corrompus.
  • Reproduire l'interface pixel par pixel au lieu d'améliorer les déroulements.
  • Ne pas embarquer sa propre équipe. Qui ne comprend pas le nouveau code se retrouve vite à maintenir à nouveau un logiciel hérité.

Comment nous vous aidons

Sources : Microsoft, « Support Statement for Visual Basic 6.0 on Windows » ; Microsoft Learn, « Exposing .NET Core components to COM » ; Martin Fowler, « Strangler Fig Application » ; Embarcadero DocWiki, « System.Extended » ; études de cas « Modernisation d'un logiciel d'appareil de mesure : de Delphi à C# » et « Modernisation d'une surcouche AutoCAD pour le génie civil », études de cas Cadida.

CONTACT

Sélectionnez votre langue

Premier entretienprendre rendez-vousPremier entretienprendre rendez-vous