Windows handles, handle, C, C++, Windows types, Windows

Windows Handles demystifiziert

HANDLE, HWND, ...

Dies ist eine neue (aktualisierte) Version aus dem Abschnitt „HANDLE, HWND, ...” des alten Artikels „Windows Datentypen”. Dieser Artikel wurde in Unterkapitel aufgeteilt. Dies ist vor allem eine Anpassung (die Verzeichnisse der Headerfiles haben sich bei neueren Versionen von Visual Studio geändert) an das derzeit (2024-01-08 Mon) aktuelle Visual Studio 2020 von Microsoft. Den alten Artikel (war glaube ich für Visual Studio 2015 oder gar 2012) findet die Softwareentwicklerin hier.

Laut msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx (Achtung(!): Unbedingt die automatische Übersetzung nach Deutsch (oder andere Sprachen) ausschalten!) ist z. B. HWND folgendermaß definiert:

typedef void *PVOID;
typedef PVOID HANDLE;
typedef HANDLE HWND;

=> HWND entspricht void*

(Leider stimmt das nur, wenn nicht STRICT definiert ist (was anscheinend mittlerweile der Normalfall ist). Bei STRICT werden z. B. Handles (mit Ausnahme des Basistyps für Handles [HANDLE]) anders definiert, so dass der Compiler unterschiedliche Handles erkennen kann, siehe unten die Definition von HWND)

D. h. man kann ein HANDLE an ein HWND zuweisen und umgekehrt und der Compiler hat keine Chance, zu erkennen, dass man hier unterschiedliche Handles aneinander zuweist. Dies kann zu merkwürdigen Fehlern führen (wenn man z. B. ein File-Handle an ein Windows-Handle zuweist - der Compiler gibt keine Fehlermeldung/Warnung aus und übersetzt das.

Daher werden, wenn STRICT definiert ist (was anscheinend mittlerweile der Normalfall ist) Handles mittlerweile in „winnt.h” (bei mir im Verzeichnis „C:\Program Files\Microsoft Visual Studio\2022\Community\SDK\ScopeCppSDK\vc15\SDK\include\um\”) mittels Preprozessor-Makro definiert:

typedef void *PVOID;
...
#ifdef STRICT
typedef void *HANDLE; #if 0 && (_MSC_VER > 1000)
#define DECLARE_HANDLE(name) struct name##__; typedef struct name##__ *name
#else
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
#endif
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name

HANDLE ist also nach wie vor als Pointer auf void (void*) definiert, andere Handles wie z. B. HWND werden jetzt aber folgendermaßen definiert (siehe in „windef.h”, bei mir in „C:\Program Files\Microsoft Visual Studio\2022\Community\SDK\ScopeCppSDK\vc15\SDK\include\shared\”):

DECLARE_HANDLE(HWND)

D. h. HWND entspricht:

typedef struct HWND__ *HWND

Ein HWND ist jetzt also ein Pointer auf struct HWND__ ein anderes Handle ist z. B. HHOOK. Dies ist ebenfalls mit diesem Makro definiert und folglich ein Pointer auf struct HHOOK__. Dies hat den Vorteil, dass der Compiler jetzt zwischen HWND und HHOOK unterscheiden kann und bei Zuweisungen ohne Cast einen Fehler ausgeben kann.