Sind HWND immer unique?



  • Hallo,

    kann man sich darauf verlassen, dass ein HWND innerhalb eines Programms immer unique ist, oder könnte in einem anderen Fenster bei einem Child Window ein HWND nochmals vorkommen?

    Ich frage, weil ich momentan eine std::unordered_map<HWND, Window*> speichere, um in der WindowProc die Events an die Controls weiterzuleiten. Es funktioniert aber nur, wenn jedes HWND unique ist. Sonst muss ich halt doch zu den identifier greifenn



  • Die Handles MÜSSEN verschieden sein. Wie sollten sonst die Funktionen unterscheiden können, welches Fenster gemeint ist? Zum Beispiel DestroyWindow. Nehme zwei gleiche HWND von verschiedenen Fenstern. Welches wird beim DestroyWindow zerstört?



  • Ich habe mir so etwas Ähnliches gedacht, wollte aber sicher gehen. Vielen Dank für die Bestätigung.

    LG



  • HWNDs sind Pointer auf eine Struktur.

    Die HWNDs können recycled werden. Wenn ein Window zerstört wird, kann ein anderes Window das gleiche HWND erhalten.

    Wer also Fenstermanipulationen vor hat, sollte HWNDs nicht zwischenspeichern.

    Weiter geht es hier: https://stackoverflow.com/questions/2344233/validate-hwnd-using-win32-api



  • Meine Window class löscht sich wieder aus der Table im dtor, d.h. wenn HWND recycled werden wäre für mich kein Problem. Wenn ein HWND wiederverwendet wird, dann ist auch das neue Window hinterlegt.



  • WinFreak not Winfried schrieb:

    HWNDs sind Pointer auf eine Struktur.

    Ich sag jetzt mal das ist falsch. Wenn du es aus guter Quelle hast, dann nenne diese gerne, ich lasse mich gern eines besseren belehren.
    Bis dahin behaupte ich mal dass HWNDs halt Handles sind. Also Indexe in einen Table, u.U. verziert mit ein paar Prüfbits/Generation-Counter-Bits irgendwo.


  • Mod



  • Stimmt das noch? Auf Windows 95 kann ich mich erinnern dass es ein 65K Limit gab und damit hatten wir massiv Probleme. Seit XP hatte ich keine solchen Probleme mehr. Daher hätte ich angenommen dass es dieses 65K Limit eben nicht mehr gibt, und dann kann der verlinkte Artikel nicht mehr aktuell sein.

    Daran dass ein HWND kein Pointer ist wird sich aber vermutlich nichts geändert haben.





  • hustbaer schrieb:

    Daran dass ein HWND kein Pointer ist wird sich aber vermutlich nichts geändert haben.

    ntdef.h definiert Handles so:

    typedef void *HANDLE;
    

    in windef.h finden wir:

    DECLARE_HANDLE            (HWND);
    

    und bezüglich DECLARE_HANDLE (in ntdef.h)

    #ifdef STRICT
    typedef void *HANDLE;
    #define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
    #else
    typedef PVOID HANDLE;
    #define DECLARE_HANDLE(name) typedef HANDLE name
    #endif
    


  • WinFreak not Winfried schrieb:

    ...

    Ja und?



  • dot schrieb:

    https://blogs.technet.microsoft.com/markrussinovich/2009/09/29/pushing-the-limits-of-windows-handles/

    Ich kann da nix zum Thema Window-Handles finden, und Kernel Handles sind gänzlich andere Tiere.

    EDIT: Doch, da steht's: https://blogs.technet.microsoft.com/markrussinovich/2010/02/24/pushing-the-limits-of-windows-user-and-gdi-objects-part-1/
    Sind wohl immer noch max. 64K USER Handles und davon max. 32K Fenster. Hm. Dann muss das Limit auf Win95 entweder noch niedriger gewesen sein oder ... irgendwas anderes 🙂
    Auf Win95 war bei 3-4 Visual Studio 6 Instanzen auf jeden Fall schluss, da haben dann schon Buttons gefehlt in vielen Dialogen. Auf XP war das nie ein Thema. Egal 🙂 /EDIT

    @WinFreak not Winfried
    Ja, ich kenne die Header-Files.

    Es ist formal ein Zeiger. Aber ein gültiger HWND Wert zeigt nicht auf irgend ein gültiges Objekt (ausser durch Zufall). Weil es eben nicht wirklich ein Zeiger ist. In Wirklichkeit sind es mehrere verschiedene Integerwerte die in einen 32 Bit Integer zusammengepackt werden und dann zu einem Zeiger gecastet.
    Einer dieser Integerwerte ist ein Index in einen Table in dem dann der wirkliche Zeiger auf das Objekt vermerkt ist.

    Und ganz nebenbei ist das die Definition von HANDLE und nicht die von HWND. HANDLE != HWND. Komplett andere Baustelle. KERNEL vs. USER. Ändert aber nix wesentliches, beides sind Handles und keine echten Zeiger, auch wenn sie als Zeiger getarnt daherkommen.

    Langer Rede kurzer Sinn: Die Aussage "HWNDs sind Pointer auf eine Struktur" ist nicht richtig. HWNDs sind zwar formal Zeiger, aber sie zeigen eben nicht auf eine Struktur.



  • hustbaer schrieb:

    Und ganz nebenbei ist das die Definition von HANDLE und nicht die von HWND.

    Man beachte:
    typedef void *HANDLE;
    und:
    #define DECLARE_HANDLE(name) typedef HANDLE name
    sowie:
    DECLARE_HANDLE (HWND);

    Alles in allem: HWND ist ein void*
    Einen Pointer nicht als Pointer zu benutzen ist recht unsinnig.
    Vermutlich werden HWNDs in Listen verwaltet und etwa so erzeugt: HWND hwnd = malloc (sizeof(WND)).

    Mal die CreateWindow-Funktion untersuchen. Ich habe die geleakten Sources von NT4. Werde nachher mal nachschauen. 🙂


  • Mod

    WinFreak not Winfried schrieb:

    ntdef.h definiert Handles so:

    typedef void *HANDLE;
    

    ...

    Und genau das ist kein Beweis! Das ist nur ein Trick damit die unterschiedlichen HANDLES intern nicht vertauscht werden können.
    Ein nette Sache der API Entwickler.

    In der Win16 API kam das erst mit der Einführung von Win16-STRICT. (Meine Güte ist das lange her <seufz />... und ja, damals habe ich auch schon programmiert 🕶)

    Damit hatte man endlich Fehlermeldungen wenn man ein HWND mit einem GDI-Handle verwechselte...

    Was intern für casts passieren weißt Du nicht. Theoretisch könnte ein HWND ein Pointer oder zumindest Pointer offset sein...



  • Hier ist die CreateWindow-Funktion
    https://github.com/killbug2004/NT_4.0_SourceCode/blob/master/nt4/private/ntos/w32/ntuser/kernel/createw.c

    Das HWND ist intern ein PWND.
    Die Zeile 165 sieht so aus:

    pwnd = HMAllocObject(ptiCurrent, pdesk, TYPE_WINDOW,
                sizeof(WND) + pcls->cbwndExtra);
    

    Das "Handle" zeigt also direkt auf einen Speicherblock.

    Mit der Bezeichnung "Handle" wird bloß verschleiert, dass es ein Pointer ist.
    HMAllocObject ist hier: http://read.pudn.com/downloads3/sourcecode/windows/248345/win2k/private/ntos/w32/ntuser/kernel/handtabl.c__.htm

    PVOID HMAllocObject(   
        PTHREADINFO ptiOwner,   
        PDESKTOP pdeskSrc,   
        BYTE bType,   
        DWORD size)
    

    🙂



  • Alter Schwede, du bist schon sehr von dir selbst überzeugt, hm?

    Winfreak again ... schrieb:

    Hier ist die CreateWindow-Funktion
    https://github.com/killbug2004/NT_4.0_SourceCode/blob/master/nt4/private/ntos/w32/ntuser/kernel/createw.c

    Das HWND ist intern ein PWND.

    Nein!
    Ein PWND ist ein PWND und eben kein HWND. Da gibt es Funktionen die HWND <-> PWND mappen. Und die machen nicht einfach bloss einen Cast.

    Winfreak again ... schrieb:

    Mit der Bezeichnung "Handle" wird bloß verschleiert, dass es ein Pointer ist.
    HMAllocObject ist hier: http://read.pudn.com/downloads3/sourcecode/windows/248345/win2k/private/ntos/w32/ntuser/kernel/handtabl.c__.htm

    Falsch.
    Vielleicht solltest du dir mal die Makros _HW, _HWCXX, _HWq etc. - die in dem von dir verlinkten File überall verwendet werden - ansehen.



  • hustbaer schrieb:

    Ein PWND ist ein PWND und eben kein HWND. Da gibt es Funktionen die HWND <-> PWND mappen. Und die machen nicht einfach bloss einen Cast.

    Ich habs gefunden. Während PWND noch ein echter Pointer auf eine WND Struct ist, ist ein Handle irgend ein DWORD, in dem nur ein Teil (HMINDEXBITS) ein Index zu sein scheint (in ein Array von HANDLEENTRYs).

    Hätte nicht gedacht, dass die solche Verrenkungen machen. 🙂
    Was wohl der Grund sein mag? Ich vermute, das Zeug ist historische gewachsen und es wurde immer mehr dazugebastelt. Dann kommt sowas wohl raus.



  • Winfreak noch immer ... schrieb:

    Ich habs gefunden. Während PWND noch ein echter Pointer auf eine WND Struct ist, ist ein Handle irgend ein DWORD, in dem nur ein Teil (HMINDEXBITS) ein Index zu sein scheint (in ein Array von HANDLEENTRYs).

    Also das was ich dir bereits geschrieben habe.

    Winfreak noch immer ... schrieb:

    Hätte nicht gedacht, dass die solche Verrenkungen machen. 🙂
    Was wohl der Grund sein mag? Ich vermute, das Zeug ist historische gewachsen und es wurde immer mehr dazugebastelt. Dann kommt sowas wohl raus.

    Nö, das macht voll viel Sinn.

    Handles sind prozessübergreifend und du musst sicherstellen können dass der eine Prozess nicht einen anderen Prozess crashen kann indem er irgendwo Mist baut.

    Vielleicht solltest du die Selbsteinschätzung deiner Kompetenz neu evaluieren. Du hast offenbar nicht viel Plan davon wie diese Dinge funktionieren, kommst aber trotzdem mit wilden (und völlig falschen) Vermutungen/Behauptungen daher...



  • hustbaer schrieb:

    Also das was ich dir bereits geschrieben habe.

    In der Tat. Deshalb habe ich die Sache auch weiter verfolgt.

    Winfreak noch immer ... schrieb:

    Nö, das macht voll viel Sinn.
    Handles sind prozessübergreifend

    Glaubst du denn, dass in dem HWND auch die Process-ID steckt? Das könnte man doch mal nachforschen.

    Winfreak noch immer ... schrieb:

    Nö, das macht voll viel Sinn.

    Glaube ich nicht. Ein Pointer, der in einem anderen Prozess gültig ist, sollte im hiesigen Prozess wie ein "wild pointer" behandelt werden. Dass das Window-Management Aufgaben des Kernels übernimmt, ist ein Designfehler.

    Windows ist schon eine tolle Sache. Man sollte seine Entwickler aber nicht für Götter halten. 🙂

    Winfreak noch immer ... schrieb:

    Vielleicht solltest du die Selbsteinschätzung deiner Kompetenz neu evaluieren. Du hast offenbar nicht viel Plan davon wie diese Dinge funktionieren, kommst aber trotzdem mit wilden (und völlig falschen) Vermutungen/Behauptungen daher...

    Ist kein Problem. Dazulernen geht immer. 😋



  • WinFreak online schrieb:

    Glaubst du denn, dass in dem HWND auch die Process-ID steckt? Das könnte man doch mal nachforschen.

    Handles sind prozessübergreifend. Du kannst dir aus Prozess A ein Handle von einem Fenster in Prozess B holen. Da kann keine PID drinstecken.


Log in to reply