Menüs ohne Resource Compiler

Hier können Artikel, Tutorials, Bücherrezensionen, Dokumente aller Art, Texturen, Sprites, Sounds, Musik und Modelle zur Verfügung gestellt bzw. verlinkt werden.
Forumsregeln
Möglichst sinnvolle Präfixe oder die Themensymbole nutzen.
Antworten
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Menüs ohne Resource Compiler

Beitrag von Krishty »

Wer nicht Microsofts Resource Compiler benutzen möchte, um vordefinierte Menüs zu erzeugen, kann das durch LoadMenuIndirect() machen. Leider scheint die Funktion so gut wie garnicht dokumentiert zu sein, darum habe ich mir die nötigen Details aus diesem OldNewThing-Artikel zusammengekratzt und schreibe meine Ergebnisse hier:

    static auto const menu =
        EMPTY_MENUITEMTEMPLATEHEADER
        NEXT_POPUP(L"first")
            NEXT_STRING(0123, L"1st sub 0")
            NEXT_STRING(0124, L"1st sub 1")
            NEXT_SEPERATOR
            LAST_POPUP(L"1st sub 2")
                LAST_STRING(0125, L"sub sub 1")
        NEXT_POPUP(L"second")
            NEXT_STRING(0126, L"2nd sub 0")
            LAST_STRING(0127, L"2nd sub 1")
        LAST_STRING(0128, L"last")
        ;

    SetMenu(hwnd, LoadMenuIndirectW((MENUTEMPLATEW*)menu));


erzeugt dieses Menü:
menu.png
menu.png (1.81 KiB) 5842 mal betrachtet
Die dazu nötigen Präprozessordefinitionen:

Code: Alles auswählen

#define MAKE_UTF16_LITERAL(x) L###x

// Expresses an empty 'MENUITEMTEMPLATEHEADER' instance as a UTF-16 literal.
#define EMPTY_MENUITEMTEMPLATEHEADER L"\x0000\x0000"

// Expresses a string entry of the given hexadecimal ID (without the '0x' prefix) and the given name (with the 'L' prefix) as a
//	UTF-16 literal.
#define NEXT_STRING(hexID_noPefix, title_UTF16Prefixd) L"\x0000" MAKE_UTF16_LITERAL(\x##hexID_noPefix) title_UTF16Prefixd L"\0"
#define LAST_STRING(hexID_noPefix, title_UTF16Prefixd) L"\x0080" MAKE_UTF16_LITERAL(\x##hexID_noPefix) title_UTF16Prefixd L"\0"

// Expresses a popup entry of the given name (with the 'L' prefix!) as a UTF-16 literal.
//
//		"The other type of menu item template is the pop-up submenu.
//
//				struct POPUPMENUITEM16 {
//					WORD wFlags;	// menu item flags (MFT_*, MFS_*)
//					CHAR szText[];	// null terminated ANSI string
//				};
//
//		 The pop-up item template doesn't have an ID, the MF_POPUP flag must be set in the flags (naturally), the MFS_HILITE
//		 flag must not be set, and it is immediately followed by... another menu resource, minus the resource header, which
//		 describes the pop-up submenu itself. (This is the recursive part.)"
//		(http://blogs.msdn.com/b/oldnewthing/archive/2008/07/09/8711897.aspx)
#define NEXT_POPUP(title_UTF16Prefixd) L"\x0010" title_UTF16Prefixd L"\0"
#define LAST_POPUP(title_UTF16Prefixd) L"\x0090" title_UTF16Prefixd L"\0"

// Expresses a seperator as a UTF-16 literal.
//
//		"The next item of the pop-up menu is a separator. If you have been following the rules strictly, you would generate the
//		 separator like this:
//
//				001D  00 08     // wFlags = MFT_SEPARATOR
//				001F  00 00     // wID = 0
//				0021  00        // ""
//
//		 However, it turns out that there is an alternate form for separators, namely to pass all zeroes:
//			
//				001D  00 00     // wFlags = 0
//				001F  00 00     // wID = 0
//				0021  00        // ""
//
//		 The existence of this alternate form is actually an artifact of history, which we'll look at next time. But for now,
//		 just realize that you can express a separator in two different ways, either the official way with MFT_SEPARATOR or the
//		 alternate way with wFlags = 0. Either works just fine."
//		(http://blogs.msdn.com/b/oldnewthing/archive/2008/07/09/8711897.aspx)
#define NEXT_SEPERATOR L"\x0000\x0000\x0000"
#define LAST_SEPERATOR L"\x0080\x0000\x0000"
Ich habe checked/unchecked (Häkchen vor Einträgen) der Einfachheit wegen weggelassen. Weil die Daten als String vorliegen, werden sie möglicherweise durch String Pooling gefaltet (was gut ist).

Nur mit Visual C++ 2012 getestet. Auf einigen Compilern wollt ihr vielleicht C++11s u""-Präfix statt L"" benutzen.

Viel Spaß!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Menüs ohne Resource Compiler

Beitrag von Krishty »

Weil ich gerade eine kleine Denkpause mache und eh reflektieren muss, erkläre ich hier nochmal im Detail, wie das Ganze funktioniert. Wer sehen will, was C++ alles kann, wird das sicher lieben:

Aaaalso: LoadMenuIndirect() erwartet einen Blob von Daten. Dieser beginnt mit einer MENUITEMTEMPLATEHEADER-Instanz:

    typedef struct {
        WORD versionNumber;
        WORD offset;
    } MENUITEMTEMPLATEHEADER, *PMENUITEMTEMPLATEHEADER;


Als möglichst einfaches Beispiel lege ich nun ein Menü mit nur einem Pop-Up (Datei) an: Klickt man drauf, öffnet sich das Untermenü, in dem man Beenden anklicken kann.

Die Dokumentation sagt nun, es würde ein Array aus MENUITEMTEMPLATE-Instanzen folgen:

    typedef struct {
        WORD mtOption;
        WORD mtID;
        WCHAR mtString[1];
    } MENUITEMTEMPLATE, *PMENUITEMTEMPLATE;


Das ist Unsinn. Aus dem OldNewThing-Link oben wissen wir nämlich: Die Datenstruktur gilt nur für tatsächliche Menüeinträge. Legt man ein Pop-Up an, kommt diese Struktur zum Einsatz:

    struct PopupTemplate {
        WORD mtOption;
        WCHAR mtString[1];
    };


Die ID fehlt, weil sie für Pop-Ups wohl nicht von Belang ist.

Ein weiteres undokumentiertes Detail ist: LoadMenuIndirect() erwartet nirgends eine Größenangabe. Woher weiß die Funktion also, wie viele Einträge unsere Menüs haben? Nullterminierung? Nein. Man muss dem jeweils letzten Eintrag jedes Menüs und Untermenüs das Flag 0x0080 (wohl auch bekannt als MF_END) übergeben.

Wiedemauchsei – für unser Menü sähe die Datenstruktur also so aus (und das ist tatsächlich, was Visual C++’ Dialog-Editor als Ressource in die Exe schreibt, wenn die Ressourcen kompiliert werden):

    struct Menu {
        MENUITEMTEMPLATEHEADER header;
        PopupTemplate file;
        wchar_t name0[5]; // "Datei"
        MENUITEMTEMPLATE exit;
        wchar_t name1[7]; // "Beenden"
    } const myMenu = {
        { 0, 0 }, // header.version / header.offset; immer 0
        { MF_POPUP | 0x0080, 'D' }, { 'a', 't', 'e', 'i', 0 }, //
Datei-Popup; gleichzeitig das letzte im Menü
        { 0x0080, 0x1234, 'B' }, { 'e', 'e', 'n', 'd', 'e', 'n', 0 } // Beenden-Eintrag mit der ID 0x1234; letzter Eintrag des Untermenüs
    };

    auto menuHandle = LoadMenuIndirectW((MENUTEMPLATEW*)myMenu);


Beachtet, dass ich wchar_t-Arrays zwischenschieben musste, damit die Namen der Elemente darin gespeichert werden können. Außerdem, dass ich die Nullterminierung selber vornehmen musste. Jeder sollte sofort sehen, dass das unwartbarer Quatsch ist.

Nun waren die Windows-3.1-Entwickler (oder wer auch immer diese Datenstrukturen entworfen hat) nicht blöd. Wenn ihr genau hinschaut, seht ihr, dass jedes Attribut in jeder der beteiligten Datenstrukturen 16 Bits groß ist – von den Flags über die IDs bis zu den Strings. Wir könnten das Ganze also als einfaches Array von 16-Bit-Werten ausdrücken:

    WORD cpnst myMenu[] = {
        0, 0,
        MF_POPUP | 0x0080,
        'D', 'a', 't', 'e', 'i', 0,
        0x0080, 0x1234, 'B' , 'e', 'e', 'n', 'd', 'e', 'n', 0
    };


Bereits hübscher, oder? Jetzt der springende Punkt: Wide-Character-Strings sind unter Windows ebenfalls 16 Bits pro Buchstabe groß. Und wir speichern dort Strings. Warum also nicht direkt ALLES als String speichern?

Kurze Erinnerung:
  • Wollen wir einen bestimmten Wert in einen Buchstaben schreiben ohne das Zeichen herauszusuchen, der durch diesen Wert repräsentiert wird, benutzen wir den Escape-Code \x89AB (mit vier hexadezimalen Ziffern).
  • Wollen wir den Wert 0 schreiben, können wir also \x0000 einsetzen. \0 ist aber aus den Zeiten, bevor C++ Unicode konnte, übrig, und bequemer.
  • Man kann durch den Präprozessor Strings beliebig oft verketten indem man sie schlicht und einfach hintereinander schreibt. "Hallo " "Welt" würde also automatisch zu "Hallo Welt" kompiliert.
Also packen wir doch einfach alle Flags und Werte mit den Buchstaben zusammen in einen String von 16-Bit-Buchstaben:

    auto const myMenu =
        L"\0\0"
        L"\x0090Datei\0"
        L"\x0080\x1234Beenden\0";


Immernoch hässlich. Aber mit der richtigen Formatierung sehen wir, wie wir das automatisieren können:

    auto const myMenu =
        L"\0\0"
        L"\x0090" L"Datei" L"\0"
        L"\x0080" L"\x1234" L"Beenden" L"\0";


Wir können also ein Makro bauen, das die Daten in der richtigen Reihenfolge anlegt. Ein kleines Problem dabei ist, dass LoadMenuIndirect() dieses blöde Flag erwartet, das den letzten Eintrag markiert :(

    #define MENU_HEADER L"\0\0"
    #define NEXT_POPUP(title) L"\x0010" title L"\0"
    #define LAST_POPUP(title) L"\x0090" title L"\0"
    #define NEXT_STRING(id, title) L"\x0000" id title L"\0"
    #define LAST_STRING(id, title) L"\x0080" id title L"\0"


Schreiben wir also nun ein Menü (ich füge jetzt noch mehr hinzu, weil es so schön einfach wird), wird …

    auto const myMenu = MENU_HEADER
        NEXT_POPUP(L"Datei")
            LAST_STRING(L"\x1234", L"Beenden")
        LAST_POPUP(L"Bearbeiten")
            NEXT_STRING(L"\x1235", L"Rückgängig")
            NEXT_STRING(L"\x1236", L"Wiederholen")
            NEXT_STRING(L"\x1237", L"Kopieren")
            LAST_STRING(L"\x1238", L"Einfügen")
        ;


… für uns vom Präprozessor übersetzt zu:

    auto const myMenu = L"\0\0"
        L"\x0010" L"Datei" L"\0"
            L"\x0000" L"\x1234" L"Beenden" L"\0"
        L"\x0090" L"Bearbeiten" L"\0"
            L"\x0000" L"\x1235" L"Rückgängig" L"\0"
            L"\x0000" L"\x1236" L"Wiederholen" L"\0"
            L"\x0000" L"\x1237" L"Kopieren" L"\0"
            L"\x0080" L"\x1238" L"Einfügen" L"\0"
        ;


… und so lässt sich das Ganze gleich viel leichter warten. Ein Dorn Im Auge ist uns aber, dass wir die IDs der einzelnen Menüelemente als String übergeben müssen – wer versteht schon L"\x1238" und mag die Redundanz? Darum eine weitere Erinnerung:
  • In einem Makro weist #param den Präprozessor an, den Parameter mit Anführungszeichen zu umschließen (String-isierung). Ein Makro

        #define STRINGIZE(param) #param

    sorgt also dafür, dass STRINGIZE(blah blah blubb) durch "blah blah blubb" ersetzt wird.
  • In einem Makro verbindet ## zwei Parameter:

        #define CONCATENATE(first, second) first##second

    sorgt also dafür, dass CONCATENATE(foo, bar) durch foobar ersetzt wird.
Mit diesem Wissen ändern wir die Makros zu:

    // Stringisierung und Verbindung kombiniert:
    #define MAKE_UTF16_LITERAL(x) L###x
    #define NEXT_STRING(id, title) L"\x0000" MAKE_UTF16_LITERAL(\x##id) title L"\0"
    // andere Makros analog


Die Ersetzung des Präprozessors ersetzt also …

    NEXT_STRING(1235, L"Rückgängig")

… durch …

    L"\x0000" MAKE_UTF16_LITERAL(\x##1235) L"Rückgängig" L"\0"

…; dann wird die Verbindung aufgelöst:

    L"\x0000" MAKE_UTF16_LITERAL(\x1235) L"Rückgängig" L"\0"

Die C-Präprozessorregeln besagen, dass der Präprozessor den neuen Text dann erneut parsen muss (damit man Makros schachteln kann). Das tut er und entdeckt den Aufruf an MAKE_UTF16_LITERAL(), und erzeugt:

    L"\x0000" L###\x1235 L"Rückgängig" L"\0"

Dann wird die Stringisierung durchgeführt …

    L"\x0000" L##"\x1235" L"Rückgängig" L"\0"

… und die Verbindung:

    L"\x0000" L"\x1235" L"Rückgängig" L"\0"

Beim erneuten Parsen wird der Präprozessor keine Makros mehr finden, und damit ist diese Zeile fertig. Und hat uns die gewollte Datenstruktur erzeugt.

Beim nächsten Mal zeige ich dann, wie man die numerischen IDs durch Makros ersetzt, damit man die Zahlen nicht überall neu schreiben muss.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Menüs ohne Resource Compiler

Beitrag von Krishty »

Sooo – nächste Version. Dieses Mal möchte ich Accelerators (also Tastenkombinationen, die Befehle auslösen; z.B. Strg+C für Kopieren) einbauen.

Die WinAPI ist derart ausgelegt, dass zwischen Befehlen durch Menüs und durch Tastenkombinationen nicht unterschieden werden muss: In beiden Fällen wird dem Fenster eine WM_COMMAND-Nachricht mit der 16-Byte-ID des Befehls geschickt. Außerdem zeigen die meisten Menüs die passenden Tastenkombinationen an – im Bearbeiten-Menü des Programms eurer Wahl steht üblicherweise nicht nur Kopieren, sondern abgerückt dahinter Strg+C. Beachtet, dass diese Bezeichnung mitübersetzt werden muss – in der englischen Version stünde dort Ctrl+C.

Das alles ist ein Wartbarkeitsalptraum: Man muss Menüs und Accelerators synchron halten, und die entsprechenden Texte übersetzen. Es wäre also ideal, wenn wir beides vom Compiler erzeugen lassen könnten statt uns selber drum zu kümmern.

Ich schmeiße hier ein paar Sachen der letzten Beiträge über den Haufen weil, naja, ich lerne ja noch.

Um eine Tastenkombination zu registrieren, muss eine ACCEL-Datenstruktur befüllt werden:

    struct ACCEL {
        BYTE fVirt;
        WORD key;
        WORD cmd;
    };


Dabei landet in fVirt z.B. das Flag FCONTROL, wenn die Strg-Taste gedrückt sein soll. Das FVIRTKEY-Flag sollte dort immer gesetzt sein – weglassen muss man es nur, wenn man z.B. zwischen Strg+c und Strg+C (mit Shift oder Capslock) unterscheiden will, also verwirrender Quatsch. Ich werde Strg, Alt, und Shift hier einfach Systemtasten nennen.

key hält dann den Virtual Key Code; und cmd die ID des Befehls, der ausgelöst werden soll. Hat z.B. bei uns Datei öffnen die ID 0x1234, und wollen wir es auf Strg+O (Systemtaste: Strg; Taste: O) legen, sähe die Instanz so aus:

    { FVIRTKEY | FCONTROL, VK_O, 0x1234 }

Einfach. Um eine Tastenkombination im Menü einzutragen, wird ein Tab an den Menüinhalt angehängt (das bewirkt Ausrichtung rechts), und dann die Tastenkombination ausgeschrieben:

    L"Öffnen\tStrg+O"





Dann mal ans Werk. Zuerst einmal definieren wir alle Befehle derart:

    #define OPEN_FILE     COMMAND(1234, CTRL, O)

Dabei ist COMMAND kein tatsächlich existierendes Makro. Wir werden aber den Präprozessor nutzen, um das zum Namen eines tatsächlichen Makros zu erweitern. Das erlaubt es dann, das Makro zu überladen. Eine Eigenart der WinAPI ist nämlich, dass nicht nur Befehle IDs haben, sondern auch solche Sachen wie die Trennstriche in Menüs (damit man sie adressieren kann, wenn man dynamisch Dinge in Menüs einfügt). Die deklarieren wir dann ähnlich:

    #define SOME_SEPERATOR     SEPERATOR(89AB)

Auch hier ist SEPERATOR kein real existierendes Makro, sondern nur ein Platzhalter, der den "Typ" angibt. So sieht nun das Makro aus, das für beide Makro-"Typen" überladen ist, und die ID als C++-Konstante ausspuckt:

    #define ID_OF(foo) CONCATENATE(INTERNAL_ID_OF_, foo)
    #define INTERNAL_ID_OF_COMMAND(id, syskey, key) 0x##id
    #define INTERNAL_ID_OF_SEPERATOR(id) 0x##id


Quelltextbeispiel:

    switch(commandID) {
        case ID_OF(OPEN_FILE):
            return open();
        case ID_OF(SOME_SEPERATOR):
            return 0;


Der Präprozessor wird das nun im ersten Schritt ID_OF auflösen. (Intuitiv wäre, dass er OPEN_FILE und SOME_SEPERATOR auflöst – tut er aber absichtlich nicht, damit man Makros andere Makros übergeben kann. Ich erkläre die Regeln hier nicht weiter, weil der Thread sonst explodiert …)

    switch(commandID) {
        case CONCATENATE(INTERNAL_ID_OF_, OPEN_FILE):
            return open();
        case CONCATENATE(INTERNAL_ID_OF_, SOME_SEPERATOR):
            return 0;


Nun werden OPEN_FILE und SOME_SEPERATOR aufgelöst:

        case CONCATENATE(INTERNAL_ID_OF_, COMMAND(1234, CTRL, O)):
            return open();
        case CONCATENATE(INTERNAL_ID_OF_, SEPERATOR(89AB)):
            return 0;


Jetzt das CONCATENATE()-Makro:

        case INTERNAL_ID_OF_##COMMAND(1234, CTRL, O):
            return open();
        case INTERNAL_ID_OF_##SEPERATOR(89AB):
            return 0;


Jetzt die String-Verbindung:

        case INTERNAL_ID_OF_COMMAND(1234, CTRL, O):
            return open();
        case INTERNAL_ID_OF_SEPERATOR(89AB):
            return 0;


Wir man sieht, werden jetzt entsprechend dem Makro-"Typ" unsere "Überladungen" eingesetzt:

        case 0x##1234:
            return open();
        case 0x##89AB:
            return 0;


Nur noch die Strings verbinden und fertig:

        case 0x1234:
            return open();
        case 0x89AB:
            return 0;


Von hier an sollte klar sein, wie die Tastenkombination eines Befehls in einen String umgewandelt werden kann, den wir in ein Menü schreiben können:

    #define SYSKEY_AS_UTF16(command) INTERNAL_CONCATENATE(INTERNAL_SYSKEY_AS_UTF16_, command)
    #define INTERNAL_SYSKEY_AS_UTF16_COMMAND(id, systemKey, key) INTERNAL_SYSKEY_UTF16_STRING_##systemKey
    #if 0x0407 == LOCALE // German (Germany)?
    #    define INTERNAL_SYSKEY_UTF16_STRING_CTRL     L"\tStrg+"
    #    define INTERNAL_SYSKEY_UTF16_STRING_ALT      L"\tAlt+"
    #    define INTERNAL_SYSKEY_UTF16_STRING_CTRL_ALT L"\tStrg+Alt+"
    #    define INTERNAL_SYSKEY_UTF16_STRING_SHIFT    L"\tUmschalt+"
    #    define INTERNAL_SYSKEY_UTF16_STRING_         L"\t" // Keine Systemtaste
    #else
    #    error Translate this!
    #endif


Ähnlich für die Tasten. Die Makros für 26 Buchstabentasten, 10 Zahlentasten, und die 12 F-Tasten erspare ich euch an dieser Stelle. Die gute Nachricht ist, dass die meisten davon wohl nicht in andere Sprachen übersetzt werden müssen.

Das gleiche muss noch für die Attribute der ACCEL-Struktur gemacht werden.

Jetzt ist klar, wie es geht, und mir wird langweilig. Nun läuft alles darauf hinaus, diese Makros …

    MENU_NEXT(OPEN_FILE, "&Öffnen …")

    ACCEL_FOR(OPEN_FILE)


… in diesen Quelltext zu verwandeln …

    L"\x0000" L"\x1234" L"&Öffnen …" L"\tStrg+" L"O"

    { FVIRTKEY | FCONTROL, VK_O, 0x1234 }


… damit diese Daten rauskommen:

    0000 1234 "&Öffnen …\tStrg+O"

    09 00 004F 89AB


Und dann muss man sich um den ganzen Scheiß nie mehr kümmern :) Ich vereinfache meine Version gerade; dann werde ich in den nächsten Tagen hier den kompletten Quelltext veröffentlichen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: Menüs ohne Resource Compiler

Beitrag von BeRsErKeR »

Also erstmal Hut ab dass du dir die Mühe machst. Allerdings frage ich mich ob es noch sinnvoll ist sowas händisch per WinAPI zu machen. Gibts es einen Grund dafür? Es gibt ja mit gtkmm, Qt und Co. genug Alternativen, die einem diesen ganzen Kram abnehmen und zudem noch plattformunabhängig sind. Für wirkliche GUI-Anwendungen würde ich heutzutage eh nicht mehr auf C++ zurückgreifen, sondern C# nehmen. C++ für Spiele und zeitkritische Tools ohne Klicki-Bunti, ja, aber für die GUI-Programmierung? Ich weiß nicht so recht.
Ohne Input kein Output.
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Menüs ohne Resource Compiler

Beitrag von Krishty »

Wenn man nicht auf Plattformunabhängigkeit angewiesen ist (finde ich scheiße), gibt es eigentlich kaum Gründe, die dagegen sprechen – man hat ein auf allen Windows-Versionen von 95 bis 8 lauffähiges Menü ohne Abhängigkeiten / Build-Skripte / ähnlichen Kram, das von jedem Benutzer sofort zu bedienen gewusst wird, das garantiert fehlerfrei läuft (weil 20 Jahre bewährt), das niemals mehr gewartet werden muss (weil Windows immer abwärtskompatibel bleibt), und das sich automatisch in Farbe und Form dem Thema des Anwenders anpasst.

Dass es nur 200 B Platz verbraucht und ich davon 1000 Stück erzeugen und anzeigen kann bevor GTK überhaupt komplett in den Arbeitsspeicher geladen ist, ist eine angenehme Nebenwirkung.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: Menüs ohne Resource Compiler

Beitrag von BeRsErKeR »

Krishty hat geschrieben:Wenn man nicht auf Plattformunabhängigkeit angewiesen ist (finde ich scheiße), gibt es eigentlich kaum Gründe, die dagegen sprechen – man hat ein auf allen Windows-Versionen von 95 bis 8 lauffähiges Menü ohne Abhängigkeiten / Build-Skripte / ähnlichen Kram, das von jedem Benutzer sofort zu bedienen gewusst wird, das garantiert fehlerfrei läuft (weil 20 Jahre bewährt), das niemals mehr gewartet werden muss (weil Windows immer abwärtskompatibel bleibt), und das sich automatisch in Farbe und Form dem Thema des Anwenders anpasst.
Das meinst du hoffentlich nicht ernst. Es gibt wohl kaum etwas das mehr Bugs hat und Sonderlösungen erfordert um überhaupt halbwegs zu funktionieren als die WinAPI. Und wozu muss heutzutage etwas neu produziertes noch unter Windows 95 oder 98 lauffähig sein? Das mit der Abwärtskompatibilität würde ich auch nicht unterschreiben. Ich kann mir nicht vorstellen, dass Microsoft auch in 10 Jahren noch alles abwärtskompatibel halten wird und kann. Falls doch wär es ein ziemliches Armutszeugnis. Farbe und Form kann sich auch gtkmm und Co anpassen und zwar auch den Einstellungen des Windows-Theme. Das ist nichts was der WinAPI eigen ist.
Krishty hat geschrieben:Dass es nur 200 B Platz verbraucht und ich davon 1000 Stück erzeugen und anzeigen kann bevor GTK überhaupt komplett in den Arbeitsspeicher geladen ist, ist eine angenehme Nebenwirkung.
Mal abgesehen davon dass man was falsch machen würde, wenn man 1000 Menüs bräuchte, wirst du heutzutage wohl selbst mit überdimensionalen GUI-Anwendungen keinerlei Probleme mehr mit Speicherplatz bekommen.

Wie angesprochen ist selbst C#, was sich nicht so sehr um Speicherplatz und Performance scherrt, für GUI-Anwendungen mehr als brauchbar. Was drunter liegt an Logik kannst du dann halt mit C++/CLI machen und du sparst dir sehr viel Zeit und Leid.
Ohne Input kein Output.
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Menüs ohne Resource Compiler

Beitrag von Krishty »

BeRsErKeR hat geschrieben:Es gibt wohl kaum etwas das mehr Bugs hat und Sonderlösungen erfordert um überhaupt halbwegs zu funktionieren als die WinAPI.
BeRsErKeR hat geschrieben:Ich kann mir nicht vorstellen, dass Microsoft auch in 10 Jahren noch alles abwärtskompatibel halten wird und kann. Falls doch wär es ein ziemliches Armutszeugnis.
Bist du betrunken?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: Menüs ohne Resource Compiler

Beitrag von BeRsErKeR »

Krishty hat geschrieben:Bist du betrunken?
Ab und zu.
Ohne Input kein Output.
Benutzeravatar
dot
Establishment
Beiträge: 1745
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Menüs ohne Resource Compiler

Beitrag von dot »

Was mich interessieren würde: Was genau ist der Vorteil davon, sich so ein Menu Template zu basteln? Wenn man es sowieso schon zu Fuß macht, dann kann man das Menü doch auch einfach gleich auf direktem Wege (CreateMenu() etc.) bauen!?
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Menüs ohne Resource Compiler

Beitrag von Krishty »

Worauf genau beziehst du dich? Auf Vorteile einer statischen Lösung gegenüber einer Laufzeitlösung, oder auf meine Makros für automatische Übersetzung und automatisch synchrone Tastenkürzel?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1745
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Menüs ohne Resource Compiler

Beitrag von dot »

Mir ist einfach unklar, wieso du den Weg über Menu Templates gehst, anstatt die Menüs direkt über die entsprechenden APIs zu erzeugen. Denn LoadMenuIndirect() tut ja am Ende auch nix anderes!?
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Menüs ohne Resource Compiler

Beitrag von Krishty »

90 % des Menüinhalts steht schon beim Kompilieren fest, also ist das für mich kein Laufzeitproblem. Außerdem kann man bei einer mitteleinfachen Makro-Auflistung des Menüinhalts weit weniger falsch machen als bei einem Batzen voneinander abhängiger WinAPI-Aufrufe. Es ist so gesehen die minimale Menge Quelltext und Zustand im Verhältnis zum Ergebnis :)
dot hat geschrieben:LoadMenuIndirect() tut ja am Ende auch nix anderes!?
Wenn das nicht der beste Grund ist, das aufzurufen statt es selber auszuschreiben, weiß ich auch nicht ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1745
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Menüs ohne Resource Compiler

Beitrag von dot »

Ok, das macht Sinn. :)
Antworten