← accueil

WinForms sous macOS

Exécuter une base de code Windows Forms sur Mac Apple Silicon et Intel — ce qui fonctionne, ce qui paraît différent, et comment livrer un .app.

Cette page est une traduction. En cas de divergence, la version anglaise fait foi. English

Pourquoi WinForms seul n’y arrive pas

System.Windows.Forms vit dans le runtime Windows Desktop et enveloppe user32.dll et GDI+. Il n’en existe aucune version pour macOS et il n’y en a jamais eu — un projet net10.0-windows ne compile même pas sur un Mac. Les contournements habituels ne tiennent pas non plus : le portage WinForms de Mono n’a jamais atteint le .NET moderne, System.Drawing.Common lève une PlatformNotSupportedException hors de Windows depuis .NET 7, et faire tourner l’application dans une VM Windows ou sous CrossOver signifie que vous livrez toujours une application Windows.

Ce que Majorsilence.Forms fait à la place

Il réimplémente l’API WinForms sur SkiaSharp et l’héberge dans une vraie NSWindow via Avalonia. Une simple compilation net10.0 — sans TFM -windows — produit une application qui se lance nativement sur les Mac Apple Silicon (arm64) et Intel (x64).

dotnet new install Majorsilence.Forms.Templates
dotnet new majorsilenceforms
dotnet run --project MajorsilenceFormsApp

Le modèle produit une bibliothèque d’interface partagée plus une tête de bureau ; le même code source compile sans modification sous Windows et Linux.

Ce qui fonctionne réellement sous macOS

Domaine Sous macOS
Fenêtrage et entrées NSWindow native via le backend Avalonia, avec les boutons « feux tricolores » standard de macOS et le déplacement/redimensionnement natifs
Apple Silicon arm64 natif — osx-arm64 et osx-x64 sont tous deux des cibles de publication normales
Rendu, Retina SkiaSharp, accéléré par le GPU et compatible HiDPI — le même code de dessin que sous Windows et Linux
Polices et texte Résolus via CoreText par SkiaSharp, avec un jeu de polices de secours embarqué pour tout ce qui manque
Code GDI+ / System.Drawing Remplacé par Majorsilence.Forms.Drawing — Bitmap, Font, Pen, Brush, Region, Drawing2D, Imaging, lecture EMF/WMF
Boîtes de dialogue courantes Boîtes de dialogue natives d’ouverture/enregistrement/dossier/couleur/police via le backend
Impression PrintDocument rend via Skia vers un PDF, à l’identique sur chaque OS
Contrôles WebView Vraie WKWebView, y compris le rendu PDF intégré
Son Joué via afplay
Stockage sécurisé et synthèse vocale Majorsilence.Forms.Essentials : SecureStorage écrit dans Keychain Services, Speech utilise la voix système say ; les deux exposent IsSupported et se dégradent en no-ops plutôt que de lever une exception
Thèmes et mode sombre Les thèmes sont des fichiers CSS (un sous-ensemble documenté) ; BuiltInTheme.Default suit l’apparence clair/sombre de l’OS, et Theme.SetBuiltInTheme/Theme.ApplyTheme basculent à l’exécution. L’exemple Theme Studio est un éditeur CSS en direct avec aperçu, et des binaires précompilés sont joints aux GitHub Releases
Gestes du trackpad Pinch, Swipe, LongPress et ScrollGesture avec inertie sont des événements de premier ordre ; ScrollableControl les applique déjà, donc Panel/ListBox/TreeView se déplacent sans aucune modification de l’application
Handle de fenêtre natif Un vrai pointeur NSWindow via WindowBase.PlatformHandle (les handles par contrôle valent IntPtr.Zero partout — voir Interopérabilité native)
Backend Uno Également pris en charge, et vérifié démarrant et rendant un formulaire complet sous macOS, si vous préférez héberger dans Uno
Backend GTK 4 Compile et s’exécute sous macOS avec le runtime GTK de Homebrew (brew install gtk4) — un backend pensé d’abord pour Linux, attendez-vous donc à une fenêtre GTK plutôt qu’AppKit ; utile surtout pour tester la tête GTK sur un Mac

Là où l’application ne fera pas très « Mac »

À connaître avant une revue de design, parce que ce sont des conséquences du modèle de compatibilité et non des bogues :

Livrer un .app

L’empaquetage est le même que pour toute application de bureau Avalonia ou .NET — le framework n’ajoute aucune étape supplémentaire :

dotnet publish -c Release -r osx-arm64 --self-contained

À partir de là, les corvées macOS habituelles s’appliquent : envelopper la sortie publiée dans une arborescence de bundle YourApp.app/Contents/MacOS avec un Info.plist et un .icns, puis la signer avec codesign, et la faire notariser par Apple si vous distribuez en dehors de l’App Store. Construisez un binaire universel en publiant osx-arm64 et osx-x64 et en les réunissant avec lipo.

Vérifié sous macOS

L’exemple Explorer et la galerie de contrôles complète tournent tous deux sous macOS, et la tête Uno de la galerie y a aussi été vérifiée :

L'exemple Explorer en cours d'exécution sous macOS

Ensuite