Komfortabler Dateimanager mit vielen Funktionen

GetMenuItemInfo und x64

By Sven on 22.08.2005 - 20:59 in Windows SDK

Das Platform SDK enthält alle (dokumentierten) Definitionen der Windows-API, die für die Entwicklung von Windows-Anwendungen nötig sind. Im Laufe der Zeit und der vielen Windows-Versionen kommen immer wieder ein paar neue Definitionen hinzu, häufig werden ältere Definitionen auch erweitert. Damit die Windows-Version, auf dem die Anwendung dann läuft, auch weiß, wie groß die Strukturdaten sind, die bei einem Aufruf einer API-Funktion übergeben werden, enthält eine Strukturdefinition in den meisten Fällen als ersten Wert die Größe der Struktur. So kann das Betriebssystem entscheiden, welche Daten in der Struktur gültig sind und von welchen Daten es zwecks Vermeidung eines Zugriffsfehlers die Finger lassen sollte.

In der Regel läuft das alles auch problemlos, eine recht heikle Funktion ist aber GetMenuItemInfo im Zusammenhang mit der MENUITEMINFO-Struktur:

typedef struct tagMENUITEMINFO
{
    UINT     cbSize;
    UINT     fMask;
    UINT     fType;         // used if MIIM_TYPE (4.0) or MIIM_FTYPE (>4.0)
    UINT     fState;        // used if MIIM_STATE
    UINT     wID;           // used if MIIM_ID
    HMENU    hSubMenu;      // used if MIIM_SUBMENU
    HBITMAP  hbmpChecked;   // used if MIIM_CHECKMARKS
    HBITMAP  hbmpUnchecked; // used if MIIM_CHECKMARKS
    ULONG_PTR dwItemData;   // used if MIIM_DATA
    LPSTR    dwTypeData;    // used if MIIM_TYPE (4.0) or MIIM_STRING (>4.0)
    UINT     cch;           // used if MIIM_TYPE (4.0) or MIIM_STRING (>4.0)
#if(WINVER >= 0x0500)
    HBITMAP  hbmpItem;      // used if MIIM_BITMAP
#endif /* WINVER >= 0x0500 */
}   MENUITEMINFO, FAR *LPMENUITEMINFO;

Diese Struktur wurde für Windows 2000 und Windows ME erweitert, wie man an

#if(WINVER >= 0x0500) 
    HBITMAP hbmpItem; // used if MIIM_BITMAP 
#endif /* WINVER >= 0x0500 */ 

sieht. Durch das Makro WINVER lässt sich steuern, ab welcher Windows-Version die Anwendung laufen soll. Dieses und einige weitere Makros sind über die ganzen API-Definitionen verstreut. Definiert man WINVER z.B. mit 0x0400, damit die Anwendung auch unter Windows 95 und NT 4 lauffähig ist, werden sämtliche zusätzliche Definitionen ausgeblendet. Dies hat aber den Nachteil, dass man neue Funktionen und Flags überhaupt nicht mehr benutzen kann.

Daher setze ich WINVER immer auf 0x0501 und importiere Funktionen, die erst ab einer bestimmten Windows-Version verfügbar sind, immer dynamisch. Somit laufen meine Anwendungen alle ab Windows 95/NT4, sofern zumindestens der Internet Explorer 5.5 installiert ist. Dieser enthält einige wichtige Systemerweiterungen, die von Entwicklerseite nicht einzeln redistributierbar sind.

Bei der Funktion GetMenuItemInfo gibt es aber einen kleinen Stolperstein. Übergibt man hier als Strukturgröße in cbSize die neue Größe, dann verweigern Windows 95 und NT4 mit der Fehlermeldung 87 (Ungültiger Parameter) die Arbeit. Beide Systeme verlangen explizit die alte und ihnen bekannte Strukturgröße ohne das zusätzliche hbmpItem.

Als Workaround hatte ich mir daher vor langer Zeit einmal ein paar Definitionen zusammengestellt, mit deren Hilfe dieses Problem umgangen wird:

#define MENUITEMINFO_SIZE_VERSION_400A  CDSIZEOF_STRUCT(MENUITEMINFOA, cch)
#define MENUITEMINFO_SIZE_VERSION_400W  CDSIZEOF_STRUCT(MENUITEMINFOW, cch)
#define MENUITEMINFO_SIZE_VERSION_500A  sizeof(MENUITEMINFOA)
#define MENUITEMINFO_SIZE_VERSION_500W  sizeof(MENUITEMINFOW)

#ifdef UNICODE
    #define MENUITEMINFO_SIZE_VERSION_400  MENUITEMINFO_SIZE_VERSION_400W
    #define MENUITEMINFO_SIZE_VERSION_500  MENUITEMINFO_SIZE_VERSION_500W
#else
    #define MENUITEMINFO_SIZE_VERSION_400  MENUITEMINFO_SIZE_VERSION_400A
    #define MENUITEMINFO_SIZE_VERSION_500  MENUITEMINFO_SIZE_VERSION_500A
#endif

#define MENUITEMINFO_SIZE		MENUITEMINFO_SIZE_VERSION_400

Da ich die zusätzliche Strukturvariable hbmpItem fast nie benötige, verwende ich für die Größe der Struktur in cbSize immer MENUITEMINFO_SIZE, somit funktioniert die Funktion problemlos auf allen Systemen ab Windows 95.

Beim Testen der x64-Version vom SpeedCommander musste ich aber leider feststellen, dass nun Windows XP x64 an dieser Stelle herumzickt. Alle Aufrufe von GetMenuItemInfo wurden wieder mit Fehler 87 zurückgegeben. Ändert man die Strukturgröße jedoch auf MENUITEMINFO_SIZE_VERSION_500, dann funktioniert es unter Windows XP x64 wieder wie erwartet. Eine kleine Erweiterung der obigen Definition schafft Abhilfe:

#define MENUITEMINFO_SIZE_VERSION_400A  CDSIZEOF_STRUCT(MENUITEMINFOA, cch)
#define MENUITEMINFO_SIZE_VERSION_400W  CDSIZEOF_STRUCT(MENUITEMINFOW, cch)
#define MENUITEMINFO_SIZE_VERSION_500A  sizeof(MENUITEMINFOA)
#define MENUITEMINFO_SIZE_VERSION_500W  sizeof(MENUITEMINFOW)

#ifdef UNICODE
    #define MENUITEMINFO_SIZE_VERSION_400  MENUITEMINFO_SIZE_VERSION_400W
    #define MENUITEMINFO_SIZE_VERSION_500  MENUITEMINFO_SIZE_VERSION_500W
#else
    #define MENUITEMINFO_SIZE_VERSION_400  MENUITEMINFO_SIZE_VERSION_400A
    #define MENUITEMINFO_SIZE_VERSION_500  MENUITEMINFO_SIZE_VERSION_500A
#endif

#ifdef _M_IX86
    #define MENUITEMINFO_SIZE		MENUITEMINFO_SIZE_VERSION_400
#else
    #define MENUITEMINFO_SIZE		MENUITEMINFO_SIZE_VERSION_500
#endif

Beim Kompilieren für 32bit-Systeme wird wie bisher immer die Größe der alten Struktur verwendet, beim Kompilieren für 64bit-Systeme dagegen die Größe der neuen Struktur.

Es gibt 2 Kommentare zu diesem Beitrag

Trackback URL | RSS-Feed für Kommentare

  1. Uwe sagt:

    Und ich dachte seit CodeJock brauchen wir uns darum nicht mehr zu kümmern?!?

  2. Sven sagt:

    Wenn man die Toolkit-Dll so nimmt, wie sie von Codejock angeboten wird, dann ist WINVER immer auf 0x400 gesetzt. Das würde unter x64 aber die oben beschriebenen Probleme aufzeigen. Da ich einige von den in der Dll enthaltenen Controls nicht benötige, habe ich mir eine eigene Fassung gestrickt. Diese ist nur etwas über 800kb groß anstatt 2.5 MB. Hier benutze ich wie in allen anderen Modulen dann auch 0x501 als WINVER.

Top