[C++/win32] Data met windows associeren

Pagina: 1
Acties:

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 04:06

.oisyn

Moderator Devschuur®

Demotivational Speaker

Topicstarter
Ik ben een eigen window libje aan het schrijven, waarbij je dus gewoon simpelweg de klasse Window kan subclassen en daarin je messagehandlers zetten. Maar dan moet ik dus in de windowproc de juiste instantie van een klasse bij de window handle zien te vinden, zodat ik windowproc van de klasse kan aanroepen.

Nou zijn daarvoor meerdere oplossingen... Je zou natuurlijk de GWL_USERDATA entry kunnen gebruiken om een pointer naar de instantie in te zetten, maar dan kan de gebruiker die niet meer gebruiken (aan de andere kant, waarom zou ie die willen gebruiken als ie z'n zooi in z'n klasse kan zetten)

De andere oplossing is om een opzoektabelletje te maken waarin je de juiste instantie bij de juiste HWND kan vinden, maar ik denk eerlijk gezegd dat dit niet snel genoeg is om dat te doen bij elke window message die je krijgt... hoewel het natuurlijk ook maar 'slechts' gaat om een GUI app.

Hoe doen ze het trouwens bij MFC/WTL/<insert random window lib>? (ik heb even gekeken in de MFC en WTL code, maar die is mij te obfuscated om uit te gaan pluizen :))

Iemand hier ideetjes/opmerkingen over?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

*haastig*/niet goed doorgelezen dus mocht ik er weer es helemaal naast zitten laat ff weten :)

Heb je hier niets aan?

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

curry684

left part of the evil twins

Dit zijn de 3 core-functies van VCL op dat gebied:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
procedure TWinControl.CreateParams(var Params: TCreateParams);
begin
  FillChar(Params, SizeOf(Params), 0);
  with Params do
  begin
    Caption := FText;
    Style := WS_CHILD or WS_CLIPSIBLINGS;
    AddBiDiModeExStyle(ExStyle);
    if csAcceptsControls in ControlStyle then
    begin
    Style := Style or WS_CLIPCHILDREN;
    ExStyle := ExStyle or WS_EX_CONTROLPARENT;
    end;
    if not (csDesigning in ComponentState) and not Enabled then
    Style := Style or WS_DISABLED;
    if FTabStop then Style := Style or WS_TABSTOP;
    X := FLeft;
    Y := FTop;
    Width := FWidth;
    Height := FHeight;
    if Parent <> nil then
    WndParent := Parent.GetHandle else
    WndParent := FParentWindow;
    WindowClass.style := CS_VREDRAW + CS_HREDRAW + CS_DBLCLKS;
    WindowClass.lpfnWndProc := @DefWindowProc;
    WindowClass.hCursor := LoadCursor(0, IDC_ARROW);
    WindowClass.hbrBackground := 0;
    WindowClass.hInstance := HInstance;
    StrPCopy(WinClassName, ClassName);
  end;
end;



procedure TWinControl.CreateWnd;
var
  Params: TCreateParams;
  TempClass: TWndClass;
  ClassRegistered: Boolean;
begin
  CreateParams(Params);
  with Params do
  begin
    if (WndParent = 0) and (Style and WS_CHILD <> 0) then
    if (Owner <> nil) and (csReading in Owner.ComponentState) and
      (Owner is TWinControl) then
      WndParent := TWinControl(Owner).Handle
    else
      raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);
    FDefWndProc := WindowClass.lpfnWndProc;
    ClassRegistered := GetClassInfo(WindowClass.hInstance, WinClassName, TempClass);
    if not ClassRegistered or (TempClass.lpfnWndProc <> @InitWndProc) then
    begin
    if ClassRegistered then Windows.UnregisterClass(WinClassName,
      WindowClass.hInstance);
    WindowClass.lpfnWndProc := @InitWndProc;
    WindowClass.lpszClassName := WinClassName;
    if Windows.RegisterClass(WindowClass) = 0 then RaiseLastWin32Error;
    end;
    CreationControl := Self;
    CreateWindowHandle(Params);
    if FHandle = 0 then RaiseLastWin32Error;
  end;
  StrDispose(FText);
  FText := nil;
  UpdateBounds;
  Perform(WM_SETFONT, FFont.Handle, 1);
  if AutoSize then AdjustSize;
end;



procedure TWinControl.CreateWindowHandle(const Params: TCreateParams);
begin
  with Params do
    FHandle := CreateWindowEx(ExStyle, WinClassName, Caption, Style,
    X, Y, Width, Height, WndParent, 0, WindowClass.hInstance, Param);
end;

Kijk maar of je er iets mee kunt... ze gebruiken in ieder geval geen GWL_USERDATA voor zover ik kan zien.

Professionele website nodig?


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
GetClassInfo(), met atomWindowType uit GetWindowInfo() ?
Daar kun je dus extra informatie achter plakken (In C++ termen: subclassen ).

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

De andere oplossing is om een opzoektabelletje te maken waarin je de juiste instantie bij de juiste HWND kan vinden, maar ik denk eerlijk gezegd dat dit niet snel genoeg is om dat te doen bij elke window message die je krijgt... hoewel het natuurlijk ook maar 'slechts' gaat om een GUI app.
Tenzij je te maken hebt met honderden windows zou ik me hier niet al te druk om maken. Eventueel kun je het zoeken nog versnellen door gebruik te maken van een hash of een of andere binaire boom ofzo...

Verwijderd

Op donderdag 02 mei 2002 01:19 schreef .oisyn het volgende:
Nou zijn daarvoor meerdere oplossingen... Je zou natuurlijk de GWL_USERDATA entry kunnen gebruiken om een pointer naar de instantie in te zetten, maar dan kan de gebruiker die niet meer gebruiken (aan de andere kant, waarom zou ie die willen gebruiken als ie z'n zooi in z'n klasse kan zetten)
Als je makkelijk wilt programmeren (in dit geval), dan kun je toch werken met de GWL_USERDATA????
Dan kun je werken met het "Controller - Model". Alle windows die je wilt maken zijn dan classes van dit Controller-object. De events kun je per class specificeren door de "On" methods te overriden... of natuurlijk zelf methods toe te voegen...
Okee, 't is niet de "snelste" manier, maar ja, wat maakt dat nog uit met de pc's van tegenwoordig??? Mijn mening is dat je beter makkelijk kunt proggen, zodat je weinig fouten maakt... (en je proggie waarschijnlijk "stabieler" zal zijn...)

Bijvoorbeeld:
code:
1
2
3
4
5
6
7
8
9
10
11
12
//Controller Type Declaratie..

type
 TController = class (TObject)
  Hwnd: THandle;
  constructor Create(X:integer; Y:integer; Width:integer; Height:integer; Brush: HBrush; Caption:string; Style:integer; StyleEx:integer; HwndParent:THandle);
  destructor Destroy; override;
  procedure Close;
  function OnClose: Boolean; virtual;
  function OnCreate: Boolean; virtual;
  function OnDestroy: Boolean; virtual;
 end;


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//WindowProc...

function WinProc (Hwnd:THandle; message:integer; wParam:WPARAM; lParam:LPARAM) : LRESULT; stdcall;
var Ctrl: TController;
begin
 Ctrl := TController ( GetWindowLong (Hwnd, GWL_USERDATA) );
 case message of
  WM_NCCREATE:begin
           with PCreateStruct (lParam)^ do Ctrl := TController(lpCreateParams);
           SetWindowLong (Hwnd, GWL_USERDATA, integer(Ctrl));
           Result := DefWindowProc(Hwnd, message, wParam, lParam);
          end;
  WM_CLOSE:begin
        if Ctrl.OnClose then Result := 0 else Result := DefWindowProc(Hwnd, message, wParam, lParam);
         end;
  WM_CREATE:begin
         Ctrl.Hwnd := Hwnd;
         if Ctrl.OnCreate then Result := 0 else Result := DefWindowProc(Hwnd, message, wParam, lParam);
        end;
  WM_DESTROY:begin
          if Ctrl.OnDestroy then Result := 0 else Result := DefWindowProc(Hwnd, message, wParam, lParam);
          Ctrl.Free;
         end;
  else Result := DefWindowProc(Hwnd, message, wParam, lParam);
 end;
end;


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//Implementatie van de TController methods...

constructor TController.Create(X:integer; Y:integer; Width:integer; Height:integer; Brush: HBrush; Caption:string;  Style:integer; StyleEx:integer; HwndParent:THandle);
var WndClass: TWndClassEx;
    s: string;
begin
 inherited Create;
 s := ClassName;
 if GetClassInfoEx(hInstance, PChar(s), WndClass) = False then
  begin
   WndClass.cbSize := SizeOf (TWndClassEx);
   WndClass.style := CS_DBLCLKS or CS_HREDRAW or CS_OWNDC or CS_VREDRAW;
   WndClass.lpfnWndProc := @WinProc;
   WndClass.cbClsExtra := 0;
   WndClass.cbWndExtra := 0;
   WndClass.hInstance := hInstance;
   WndClass.hIcon := LoadIcon (hInstance, 'MAINICON');
   WndClass.hCursor := LoadCursor (0, IDC_ARROW);
   WndClass.hbrBackground := Brush;
   WndClass.lpszMenuName := nil;
   WndClass.lpszClassName := PChar(s);
   WndClass.hIconSm := LoadIcon (hInstance, 'SMALLICON');
   if RegisterClassEx (WndClass) = 0 then DisplayError ('Register Window Class Failed.');
  end;
 Hwnd := CreateWindowEx (StyleEx, PChar(s), PChar(Caption), Style, X, Y, Width, Height, HwndParent, 0, hInstance, Self);
 if Hwnd = 0 then DisplayError ('Create Window Failed.');
end;

destructor TController.Destroy;
begin
 inherited Destroy;
end;

procedure TController.Close;
begin
 SendMessage (Hwnd, WM_CLOSE, 0, 0);
end;

function TController.OnClose: Boolean;
begin
 Result := False;
end;

function TController.OnCreate: Boolean;
begin
 Result := False;
end;

function TController.OnDestroy: Boolean;
begin
 Result := False;
end;

:) TSI

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 04:06

.oisyn

Moderator Devschuur®

Demotivational Speaker

Topicstarter
Op donderdag 02 mei 2002 08:19 schreef Yarvieh het volgende:
*haastig*/niet goed doorgelezen dus mocht ik er weer es helemaal naast zitten laat ff weten :)

Heb je hier niets aan?
aah kijk eens aan, thanx! (MFC gebrukt dus een opzoektabelletje voor HWND -> class instance) :)
Op donderdag 02 mei 2002 10:07 schreef curry684 iets over VCL
bedankt, maar ik had liever de windowproc gezien :)
Op donderdag 02 mei 2002 10:50 schreef MSalters het volgende:
GetClassInfo(), met atomWindowType uit GetWindowInfo() ?
Daar kun je dus extra informatie achter plakken (In C++ termen: subclassen ).
ik geloof niet dat je helemaal begrijpt wat ik bedoel. Ik weet wel hoe ik op lowlevel win32-niveau een window kan subclassen, maar wat ik bedoelde was: hoe kan ik op de beste manier een instantie van een C++ klasse bij de juiste HWND vinden (en daar ben ik nu dus uit). En nee, gebruik maken van de extra window bytes is geen optie, aangezien bepaalde windows daar al gebruik van maken.
Op donderdag 02 mei 2002 13:41 schreef Qlone het volgende:

Tenzij je te maken hebt met honderden windows zou ik me hier niet al te druk om maken. Eventueel kun je het zoeken nog versnellen door gebruik te maken van een hash of een of andere binaire boom ofzo...
ja idd, hier zat ik net na het posten ook al aan te denken... het gui gedeelte van je app staat 99.9% van de tijd te idlen, dus die extra tijd (die ook nog niet eens zoveel is) zal ook wel niet veel uitmaken :)
Ik denk idd dat ik gewoon deze manier ga gebruiken, zodat GWL_USERINFO ook gewoon beschikbaar blijft.
Op donderdag 02 mei 2002 15:13 schreef TSI2002 het volgende:

Als je makkelijk wilt programmeren (in dit geval), dan kun je toch werken met de GWL_USERDATA?
ik weet hoe GWL_USERDATA werkt, het probleem dat ik hiermee had is dat ik deze entry vrij wilde houden, zodat een gebruiker daar zelf dingen in kan zetten (wat ook de bedoeling van die entry is)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Op donderdag 02 mei 2002 19:05 schreef .oisyn het volgende:
ik weet hoe GWL_USERDATA werkt, het probleem dat ik hiermee had is dat ik deze entry vrij wilde houden, zodat een gebruiker daar zelf dingen in kan zetten (wat ook de bedoeling van die entry is)
Maarre, kun je dan niet een Pointer geven naar een "zelf" gedefineerd record, met daarin de pointer naar het window-object????? Zodat de gebruik van de lib nog altijd user-defined data kan doorgeven???

zoiets als:
code:
1
2
3
4
5
type
 TRecord = record
  Controller : TController;
  UserData : TUserData;
 end;

:) TSI

Verwijderd

Wat is er mis met de iets wat smerige manier die atl/wtl gebruikt?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 04:06

.oisyn

Moderator Devschuur®

Demotivational Speaker

Topicstarter
ouch, ik lees nu hoe ATL het doet (de link die Yarvieh gaf)

Ze alloceren voor elke window een eigen windowproc; een klein stukje assembly code die de HWND parameter van de windowproc wijzigt in de instantie van de klasse, en vervolgens de oude window proc aanroept.

Die oude window cast dan gewoon de HWND parameter naar de Window klasse, en roept vervolgens z'n virtual functie aan :)

is op zich wel een goeie methode... maar je zult nog altijd die associatieve array moeten gebruiken om de instanties bij de window handles te vinden bij bepaalde window messages... mmja, nu eerst even eten :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 04:06

.oisyn

Moderator Devschuur®

Demotivational Speaker

Topicstarter
Op donderdag 02 mei 2002 19:09 schreef TSI2002 het volgende:

[..]

Maarre, kun je dan niet een Pointer geven naar een "zelf" gedefineerd record, met daarin de pointer naar het window-object????? Zodat de gebruik van de lib nog altijd user-defined data kan doorgeven???

zoiets als:
code:
1
2
3
4
5
type
 TRecord = record
  Controller : TController;
  UserData : TUserData;
 end;

:) TSI
das waar, maar dan verplicht je dat alles wat in je app gebruikt wordt gebruik maakt van mijn lib... misschien is er wel gewoon een een of andere 3rd party lib die iets kwijt moet in die entry, en dan zit je met de gebakken peren

oh kijk trouwens even naar deze topic: [topic=309815] :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


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

curry684

left part of the evil twins

Op donderdag 02 mei 2002 19:05 schreef .oisyn het volgende:
bedankt, maar ik had liever de windowproc gezien :)
Ik had niet echt het gevoel dat je dit wilde zien:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function InitWndProc(HWindow: HWnd; Message, WParam,
  LParam: Longint): Longint;
begin
  CreationControl.FHandle := HWindow;
  SetWindowLong(HWindow, GWL_WNDPROC,
    Longint(CreationControl.FObjectInstance));
  if (GetWindowLong(HWindow, GWL_STYLE) and WS_CHILD <> 0) and
    (GetWindowLong(HWindow, GWL_ID) = 0) then
    SetWindowLong(HWindow, GWL_ID, HWindow);
  SetProp(HWindow, MakeIntAtom(ControlAtom), THandle(CreationControl));
  SetProp(HWindow, MakeIntAtom(WindowAtom), THandle(CreationControl));
  asm
      PUSH    LParam
      PUSH    WParam
      PUSH    Message
      PUSH    HWindow
      MOV     EAX,CreationControl
      MOV     CreationControl,0
      CALL    [EAX].TWinControl.FObjectInstance
      MOV     Result,EAX
  end;
end;

Vooral dat stoere stuk ASM in de Pascal :)

Overigens is dit alleen de InitWndProc, voor de rest wordt alles intern afgehandeld met Perform, Broadcast en gerepointerde closure WndProc methods... maar daarvoor zou je echt de hele code door moeten neuzen daar kan ik niet zo snel een korte samenvatting van geven (voor zover ik het zelf uberhaupt snap...).

Erg stoere feature van VCL is trouwens de 100% integrated support voor non-windowed controls, die dus zelf theoretisch geen messages kunnen processen, maar hierdoor wel transparency kunnen hebben en heel weinig overhead hebben.

Professionele website nodig?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 04:06

.oisyn

Moderator Devschuur®

Demotivational Speaker

Topicstarter
kijk, daar heb ik wat aan :)

zo te zien gebruikt VCL ook gewoon de GWL_WNDPROC entry van een window

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1