Ausführen einer MFC-Anwendung auf einer Nicht-x86-Architektur, die nativ Winelib verwendet und ein Qt-Plug-in damit verbindet

Ich habe kürzlich eine alte Win32-MFC-MDI-Anwendung, die immer noch mit Microsoft Visual Studio 6.0 (Entwicklungsumgebung von 1998) entwickelt wurde, auf Linux portiert. Außerdem war es notwendig, dass unter Linux aus den Quellen ein natives Binary im ELF-Format zusammengestellt wurde. Mein Blick fiel auf das Wine-Projekt, mit dem Sie Windows-Binärdateien (PE-Format – Portable Executable) ausführen können. Eine der Komponenten von Wine – die Winelib-Bibliothek – ist eine Schicht (Wrapper) zwischen Win32-Aufrufen und Linux-Bibliotheksaufrufen. Mit Winelib können Sie Win32-Anwendungen aus dem Quellcode unter Linux erstellen. Winelib hat Dutzende von standardmäßigen Windows-Bibliotheks-Wrappern. Aber hier ist das Problem. Es gibt keinen Wrapper für MFC (Microsoft Foundation Classes). Infolgedessen musste eine Schicht zwischen MFC-Klassen und Win32-Aufrufen, die für die Kompilierung mit Winelib geeignet ist, von mir selbst vorbereitet werden. Anschließend wird der Weg zur Lösung des Problems beschrieben, sowie die Kompromisse, die bei der Umsetzung eingegangen werden mussten.

Ein wenig geschichtlicher Hintergrund. Winelib ist nicht der erste Versuch, Win32-Anwendungen unter *nix-Systemen zu kompilieren. Noch früher wurden für Solaris Unix mehrere Schichten erstellt, die die Standard-Windows-Bibliotheken implementieren, um sicherzustellen, dass ältere Versionen von Internet Explorer ausgeführt werden.

Daher war es notwendig, das Vorhandensein von MFC unter Winelib sicherzustellen. Es ist erwähnenswert, dass alle Ebenen der Winelib-Ebenenbibliotheken ähnliche Funktionen wie die Windows-Bibliotheken implementieren, aber von der Wine-Community von Grund auf neu geschrieben wurden. Die MFC-Quelle ist im installierten Visual Studio enthalten. Eine Internetsuche zum Erstellen von MFC mit Winelib ergab die folgenden Ergebnisse.

Im Allgemeinen ist klar, dass Sie bestimmte Makrodefinitionen für die MFC-Quellen festlegen müssen, um nicht einfach alles zu unterstützen. Nachdem ich diese Makros offengelegt hatte, begann ich, MFC unter Linux zu kompilieren. Ich habe MFC 4.2 von Visual Studio 6.0 übernommen, da die zu portierende Anwendung diese Entwicklungsumgebung und diese Version von MFC verwendet. Die Kompilierung wird übrigens von einem Wrapper über gcc namens winegcc durchgeführt. Und die Erstellung des Makefiles erfolgt durch das Winemaker-Perl-Skript.

Nach mehr als 30 Änderungen an verschiedenen Stellen im MFC 4.2-Quellcode wurde es schließlich kompiliert. Nachdem ich die in Visual Studio 6.0 erstellte minimale MFC-Anwendung hinzugefügt hatte, führte ich die resultierende Binärdatei aus. Und bekam direkt nach dem Start einen Absturz. Es stellte sich heraus, dass die Konstruktoren globaler Objekte im Fall von Winelib auf eine bestimmte Weise aufgerufen werden müssen.

Die so kompilierte abgestürzte Anwendung stammt aus den vollständigen MFC-Quellen, beschränkt auf wenige Makros. Theoretisch könnten bei einem Winelib-Build viele Stellen miteinander in Konflikt geraten. Dies erhöhte die Anzahl der Fehlerpunkte und die potenzielle Debugging-Zeit. Daher habe ich mich entschieden, auf eine ganz andere Art und Weise zu handeln – von einfach bis komplex. Nachdem ich ein Projekt für eine neue Win32-Anwendung (nicht MFC) unter MSVS 6.0 erstellt hatte, fügte ich eine einzelne mfc.cpp-Datei hinzu, in die ich begann, die Definitionen und die Implementierung dieser MFC-Funktionalität zu kopieren, um einen minimal funktionierenden Dialog zu erhalten MFC-Anwendung. Diese. Aus Sicht der Entwicklungsumgebung handelte es sich um eine Win32-Anwendung, in der die Mindestfunktionalität von MFC in einer separaten Datei implementiert war und die weder statisch noch dynamisch mit den binären MFC-Bibliotheken verknüpft war, die mit der Entwicklungsumgebung geliefert wurden.

Nachdem diese minimale Anwendung mit dem Kompilieren und Ausführen unter Windows begonnen hatte, kompilierte ich die resultierende mfc.cpp unter Linux mit winegcc und Winelib. Dieses Mal wurde die gesamte Anwendung + MFC-Implementierung in ein separates ladbares .dll.so-Modul verschoben, um die korrekte Initialisierung globaler Objektkonstruktoren sicherzustellen. Nachdem einige Ausnahmen in der MFC-Quelle mfc.cpp mit gdb abgefangen und gepatcht wurden, erschien unter Linux schließlich das MFC-Anwendungsfenster, kompiliert aus den Quellen in eine native ELF-Binärdatei.

Das Fenster zeigte die russische Sprache nicht an, aber dies wurde behoben, indem dem System das Gebietsschema ru_RU.cp1251 hinzugefügt und die Anwendung mit dem Präfix „LC_ALL=ru_RU.cp1251“ aufgerufen wurde. Die ursprüngliche tragbare Anwendung ist Multibyte Win-1251, dh. nicht Unicode. Daher ist meine mfc.cpp nur auf MB Win1251 zugeschnitten (obwohl MFC 4.2 im Allgemeinen Unicode unterstützt, ich dies aber nicht getestet habe).

Außerdem habe ich in Windows anstelle einer minimalen Dialoganwendung die vollständige Quelle der portablen MDI-Anwendung mit mfc.cpp mit der Assembly verbunden. Ich musste viele MFC-Implementierungen aus dem Quellcode zu mfc.cpp hinzufügen. Und führen Sie auch einige Problemumgehungen durch. Wo immer es Unterschiede zu den ursprünglichen MFC-Quellen gibt, gibt es Frames _AFX_REDUCED_SOURCEPRINT, _AFX_PLATFORM_LINUX, _AFX_TARGET_ARM32.

Im Folgenden finden Sie eine Liste von Problemumgehungen in Bezug auf die ursprüngliche MFC 4.2-Quelle von Visual Studio 6.0, die erforderlich waren, um MFC unter einem kompilierten Build von MFC unter Windows (_AFX_REDUCED_SOURCEPRINT), unter Linux mit Winelib (_AFX_PLATFORM_LINUX) und auf Nicht-x86-Architekturen auszuführen (_AFX_TARGET_ARM32). Vielleicht können alle oder die meisten dieser Problemumgehungen korrekter und korrekter implementiert werden, als sie implementiert werden, aber im Rahmen der zu lösenden Aufgabe war es nicht möglich und notwendig, Zeit für diese Studien aufzuwenden. Die endgültige portierte Anwendung arbeitet in Bezug auf ihre unmittelbare Funktionalität stabil.

  • Rendert oder zerstört Tootip unter bestimmten Bedingungen nicht.

  • Funktioniert nicht mit Shortcuts pro App.

  • In einigen Fällen werden Befehlszeilenparameter nicht analysiert.

  • Verwendet die alte Methode zum Verschieben/Umbenennen von Dateien.

  • In einigen Fällen ist die automatische Zeichenfolgenkonvertierung deaktiviert, wodurch das Projekt an Multibyte ANSI gebunden ist.

  • Einige Änderungsereignisse für verbundene Drucker werden nicht verfolgt.

  • In einem der beim Beenden der Anwendung aufgerufenen Destruktoren wird kein Speicher freigegeben.

  • Der Prozess wird zwangsweise geschlossen, nachdem das letzte CFrameWnd geschlossen wurde.

  • Erkennung falscher Parameter beim Arbeiten mit Dialogressourcen in CWnd::ExecuteDlgInit() eingeführt.

  • Unter bestimmten Bedingungen werden zusätzliche leere globale __argc und __argv hinzugefügt.

Die ursprüngliche portable Anwendung hatte auch ein Qt-Plugin. Daher enthält das angehängte Archiv auch ein kleines Qt-Bibliotheksprojekt, das mit MFC eine Verbindung zur Winelib-Anwendung herstellt. Beachten Sie, dass das angehängte Archiv eine Demo eines leeren MDI-Projekts enthält, das von MSVS 2010 auf meine mfc.cpp herunterportiert wurde.

Archiv: https://disk.yandex.ru/d/fUHhfWKqkZbojA

PS: Die geposteten Materialien sind keineswegs als Allheilmittel oder der einzig richtige Weg positioniert. Sie können nur ein Ausgangspunkt sein, wenn Sie das Problem der Portierung einer MFC-Anwendung auf Linux lösen müssen. Das Projekt löste viele Fallstricke im Zusammenhang mit dem Kompilieren und Verlinken. Aber zB das angehängte Makefile ist nicht fehlerfrei (zB kompiliert es jedes Mal das gesamte Projekt neu, auch wenn sich nicht alle Quelldateien geändert haben). Es ist nur dann sinnvoll, ein Projekt zu verwenden, um Ihr MFC-Programm auf Linux zu portieren, wenn Sie bereits über genügend Erfahrung beim Erstellen von Projekten unter Linux verfügen.

ЗЗЫ: Die obige mfc.cpp enthält nicht die gesamte MFC 4.2. Wenn Ihre Anwendung einige andere MFC-Klassen und -Methoden verwendet und beim Verknüpfen von Verschwörungen bei GetRuntimeClass(), GetMessageMap(), messageMap, classC, dann müssen Sie nach IMPLEMENT_DYNAMIC(), IMPLEMENT_DYNCREATE(), IMPLEMENT_FIXED_ALLOC(), BEGIN_MESSAGE_MAP(), by suchen Durchführen einer Volltextsuche nach den erforderlichen Klassen in den MFC-Quellen von Visual Studio.

Similar Posts

Leave a Reply

Your email address will not be published.