[C++] WndProc in class

Pagina: 1
Acties:
  • 177 views sinds 30-01-2008
  • Reageer

  • Daro
  • Registratie: April 2003
  • Laatst online: 09-01 14:42
Hallo iedereen,

Ik wil in mijn eigen progje (proberen) alles in OOP te doen.
Probleem is, dat dat progje Win32Api code is, en dat je overal van die WndProc's moet toevoegen. Tot zover alles goed, maar als ik die WndProc in een of andere class wil steken, geeft die volgende error:

c:\documents and settings\david\mijn documenten\c++\c++ projecten\rekenoefeningen\beheer.cpp(30) : error C2664: 'DialogBoxParamA' : cannot convert parameter 4 from 'int (struct HWND__ *,unsigned int,unsigned int,long)' to 'int (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)'
None of the functions with this name in scope match the target type

Weet iemand een oplossing of moet ik die WndProc gewoon niet in een class steken.

Bedankt David

modbreak: ruimteverspillende greetz en bedankjes boven en onder posts zijn op GoT niet nodig, wij tweakers doen elkaar permanent de groeten (vrij naar de FAQ ;) )

[ Voor 15% gewijzigd door curry684 op 16-08-2003 22:01 ]


  • MisterData
  • Registratie: September 2001
  • Laatst online: 21-08 20:35
Klopt, dat kan niet. Wat wel werkt, is een WndProc maakt, die alle calls van alle instances van die class opvangt, en ze dan doorsluist naar de class. Voorbeeldje:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Window {
..bla.bla...

LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
// handle je msg's as usual
}
};

LRESULT GlobalWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
     Window* w = (Window*)GetWindowLong(hwnd,GWL_USERDATA);
    return w->WndProc(hwnd,msg,wParam,lParam);
}

//vervolgens moet je bij het aanmaken van je hwnd het volgende doen:

..CreateWindow(Ex) enzo... geef wel de GlobalWndProc op als lpfnWndProc!

SetWindowLong(hwnd,GWL_USERDATA,(long)this);


Zo werkt het bij mij in ieder geval wel. Wat je nu doet is een stukje eigen informatie aan die HWND toevoegen als 'userdata': in dit geval een pointer naar je Window-instantie :)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 21-08 18:33
Overigens kun je em wel in de class 'steken' ;) , maar dan zou je em static moeten maken.

Anywayz, een constructie zoals die van MisterData blijf je houden.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • madwizard
  • Registratie: Juli 2002
  • Laatst online: 26-10-2024

madwizard

Missionary to the word of ska

Het kan ook anders, WTL/ATL gebruikt een goed bedachte/beetje ranzige methode om wndprocs te koppelen aan een object.. De initialisatie is wat lastiger dan maar uiteindelijk is het misschien wat efficienter (geen GetWindowLong nodig), al denk ik niet dat je daar ook maar iets van merkt. Voordeel ervan is wel dat je GWL_USERDATA niet vervuild, zodat die vrij is om te gebruiken. Dat kan bij custom controls wel belangrijk zijn, anders moet je de gebruikers ervan verbieden GWL_USERDATA te gebruiken en dat is niet echt netjes. (USERDATA = user defined data, dus niet voor de implementatie)

Wat W/ATL doet is bij het aanmaken van het window, in een globale map een entry invoegen (hwnd -> object adres). Deze map is thread-safe dus kan door meerdere threads tegelijk gebruikt worden, dat maakt em ook wat trager (wel maar 1 tegelijk) maar de initialisatie van het window is dan ook de enige plek waar deze gebruikt wordt, zodra dat klaar is heb je er niks meer mee te maken.

De wndproc is bij het registreren van de class op een standaard static functie gezet, die wordt de eerste keer aangeroepen als het window aangemaakt wordt. Deze functie leest uit de globale map de entry voor z'n eigen window, het object adres dus. Dan schrijft ie in daarvoor gereserveerd geheugen in dat object een stukje assembler, dat als nieuwe wndproc gaat dienen. Dat stukje roept een andere static wndproc aan, maar vervangt daarbij de hWnd parameter door het object adres. Vervolgens wordt de wndproc van het window op het stukje assembler gezet.

Vanaf nu wordt dat aangeroepen, hWnd wordt object adres en de functie WndProc ziet er dan als zoiets uit (uit m'n hoofd maar exacte code staat ergens in de headers):
C++:
1
2
3
4
5
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    Window *w = reinterpret_cast<Window*>(hWnd);
    return w->WndProc(uMsg, wParam, lParam);
}


Wat ik me wel afvraag is waarom ze de hWnd parameter zo misbruiken, ze hadden ook gewoon een extra parameter mee kunnen geven, had je geen cast nodig.

Dit hele gebeuren werkt omdat elk window nu z'n eigen stukje assembler heeft, en dus een eigen *unieke* wndproc. Zo weet je binnen die wndproc altijd welk object je bent. Het is wat lastig te programmeren maar als je het eenmaal hebt werkt het gewoon en kun je het overal gebruiken.
MisterData schreef op 16 August 2003 @ 09:24:
Klopt, dat kan niet. Wat wel werkt, is een WndProc maakt, die alle calls van alle instances van die class opvangt, en ze dan doorsluist naar de class. Voorbeeldje:

[-----8<-----]

Zo werkt het bij mij in ieder geval wel. Wat je nu doet is een stukje eigen informatie aan die HWND toevoegen als 'userdata': in dit geval een pointer naar je Window-instantie :)
Pas hierbij wel op dat zodra CreateWindowEx retourneert, GlobalWndProc al een aantal keren is aangeroepen (of 1 keer, weet ik niet zeker, maar iig wel minimaal 1 keer). Als je daarna pas SetWindowLong gebruikt is er dus nog een moment dat GWL_USERDATA 0 is in GlobalWindowProc. Daar moet je wel even op checken.

[ Voor 23% gewijzigd door madwizard op 16-08-2003 11:47 ]

www.madwizard.org


  • Daro
  • Registratie: April 2003
  • Laatst online: 09-01 14:42
Mmmm, moeilijk dingen allemaal:)
Bedankt iedereen, ik zal een uitproberen, als er een probleem is, hoor je wel van mij:)

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

madwizard schreef op 16 August 2003 @ 11:39:
Pas hierbij wel op dat zodra CreateWindowEx retourneert, GlobalWndProc al een aantal keren is aangeroepen (of 1 keer, weet ik niet zeker, maar iig wel minimaal 1 keer). Als je daarna pas SetWindowLong gebruikt is er dus nog een moment dat GWL_USERDATA 0 is in GlobalWindowProc. Daar moet je wel even op checken.
Dat is natuurlijk ook stom als je dat probeert. De classpointer zet je in de WM_CREATE of WM_NCCREATE (whichever came first, don't remember). Zo doen VCL en mijn eigen window framework het ook.

Professionele website nodig?


  • MisterData
  • Registratie: September 2001
  • Laatst online: 21-08 20:35
madwizard schreef op 16 August 2003 @ 11:39:
Pas hierbij wel op dat zodra CreateWindowEx retourneert, GlobalWndProc al een aantal keren is aangeroepen (of 1 keer, weet ik niet zeker, maar iig wel minimaal 1 keer). Als je daarna pas SetWindowLong gebruikt is er dus nog een moment dat GWL_USERDATA 0 is in GlobalWindowProc. Daar moet je wel even op checken.
Gewoon op null-pointer checken bij het doen van GetWindowLong in de WndProc... en dan eventueel WM_CREATE afhandelen als die pointer null is (zodat het window goed geconstruct wordt) of gewoon DefWindowProc aanroepen....

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

MisterData schreef op 16 augustus 2003 @ 21:16:
[...]
Gewoon op null-pointer checken bij het doen van GetWindowLong in de WndProc... en dan eventueel WM_CREATE afhandelen als die pointer null is (zodat het window goed geconstruct wordt) of gewoon DefWindowProc aanroepen....
WM_CREATE en WM_NCCREATE zijn de documented en gegarandeerd de eerste messages die tijdens CreateWindow verstuurd worden. Je zou moeten opzoeken welke van de 2 eerst, maar tis een fixed order. En daar kun en mag je gebruik van maken zodat heel die check overbodig is.

Professionele website nodig?


Verwijderd

Je kan ook es kijken naar de CAuxThunk en CAuxStdThunk templates uit atlaux (deze 2 zijn redelijk losstaant en ook zonder atl prima te gebruiken)

  • Daro
  • Registratie: April 2003
  • Laatst online: 09-01 14:42
Eeh, lees mijn ondertitel eens aub...
Ik denk niet dat ik nog mee ben...

  • MisterData
  • Registratie: September 2001
  • Laatst online: 21-08 20:35
Volgens mij kon je zelfs ergens de HWND struct groter maken (ergens in een struct wat veranderen) dus dan heb je die userdata-plek zowieso niet meer nodig :)
Pagina: 1