Recent

Author Topic: [SOLVED] windows API WNDPROC  (Read 10342 times)

KemBill

  • Jr. Member
  • **
  • Posts: 74
[SOLVED] windows API WNDPROC
« on: December 03, 2017, 05:31:29 pm »
I like to reinvent the wheel  :)

I am building a window from scratch with only api.

WNDPROC is defined like this (so you cannot bind a window proc of an object instance)
Code: Pascal  [Select][+][-]
  1. WNDPROC = function (_para1:HWND; _para2:UINT; _para3:WPARAM; _para4:LPARAM):LRESULT;stdcall;
  2.  

how would you do if you wanted to use some objects variables and/or functions in the WNDPROC (since you are not in the same namespace) without having global variables (a kind of depency injection) ?

Code: Pascal  [Select][+][-]
  1.  
  2. type
  3.   THello = class(TObject)
  4.     procedure Something;
  5.   end;
  6.  
  7. function WNDPROC(hWnd:HWND; Msg:UINT; WPARAM:WPARAM; LPARAM:LPARAM):LRESULT;stdcall;
  8. begin
  9.   case (Msg) of
  10.     //THello does not exist here !!
  11.     // cannot bind THello.WNDPROC because of wndproc definition
  12.   end;
  13. end;
  14.  
« Last Edit: December 04, 2017, 10:51:48 am by KemBill »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11452
  • FPC developer.
Re: windows API WNDPROC
« Reply #1 on: December 03, 2017, 06:07:48 pm »
Usually one of the params of the wndproc is a parameter that you can register earlier. Usually you pass SELF there.

balazsszekely

  • Guest
Re: windows API WNDPROC
« Reply #2 on: December 03, 2017, 06:29:39 pm »
Class function combined with static should work, like this:
Code: Pascal  [Select][+][-]
  1. type
  2.   THello = class(TObject)
  3.   private
  4.     class function WNDPROC(hWnd:HWND; Msg:UINT; WPARAM:WPARAM; LPARAM:LPARAM):LRESULT; stdcall; static;
  5.     procedure Something;
  6.   end;
  7.  
  8. implementation
  9.  
  10. class function THello.WNDPROC(hWnd: HWND; Msg: UINT; WPARAM: WPARAM;
  11.   LPARAM: LPARAM): LRESULT; stdcall;
  12. begin
  13.  
  14. end;

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: windows API WNDPROC
« Reply #3 on: December 03, 2017, 06:46:41 pm »
Class function combined with static should work, like this:

I tried, doesn't compile, because it's still a procedure of object

Since it's static, it doesn't allow me to share variables with the object instance?
« Last Edit: December 03, 2017, 06:50:27 pm by KemBill »

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: windows API WNDPROC
« Reply #4 on: December 03, 2017, 07:38:20 pm »
I like to reinvent the wheel  :)
doubt it.
I am building a window from scratch with only api.

WNDPROC is defined like this (so you cannot bind a window proc of an object instance)
Code: Pascal  [Select][+][-]
  1. WNDPROC = function (_para1:HWND; _para2:UINT; _para3:WPARAM; _para4:LPARAM):LRESULT;stdcall;
  2.  

how would you do if you wanted to use some objects variables and/or functions in the WNDPROC (since you are not in the same namespace) without having global variables (a kind of depency injection) ?
to put it as simple as possible you have a generic wndproc that does nothing more than calling the the method for you eg.
Code: Pascal  [Select][+][-]
  1. function WNDPROC(hWnd:HWND; Msg:UINT; WPARAM:WPARAM; LPARAM:LPARAM):LRESULT;stdcall;
  2. var
  3.   vData:Pointer;
  4. begin
  5.    vData := Pointer(GetWindowLongPtr(hWnd, GWLP_USERDATA));
  6.    result := TWinControl(VData).WindowPorc(hWnd, Msg, WPARAM,LPARAM);
  7. end;
  8.  
Just make sure when you create the window to SetWindowlongPtr(hwnd, GWLP_USERDATA, self) or something along those lines.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

balazsszekely

  • Guest
Re: windows API WNDPROC
« Reply #5 on: December 03, 2017, 08:01:03 pm »
Quote
I tried, doesn't compile, because it's still a procedure of object
It compiles fine both with Lazarus 18RC5/Trunk. Please see attached project.

PS: I have to admit, I'm not sure anymore what are you after.

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: windows API WNDPROC
« Reply #6 on: December 03, 2017, 08:10:40 pm »
Quote
I tried, doesn't compile, because it's still a procedure of object
It compiles fine both with Lazarus 18RC5/Trunk. Please see attached project.

PS: I have to admit, I'm not sure anymore what are you after.

you are using forms... mode Delphi, will do the test


Just make sure when you create the window to SetWindowlongPtr(hwnd, GWLP_USERDATA, self) or something along those lines.

Thank you all, I'm back on the rails

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: windows API WNDPROC
« Reply #7 on: December 03, 2017, 10:24:15 pm »
Here it is :

Code: Pascal  [Select][+][-]
  1. unit hello;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows, Classes, SysUtils;
  9.  
  10. type
  11.  
  12.   { THello }
  13.  
  14.   THello = class(TObject)
  15.   private
  16.     someText: string;
  17.     function WindowProc(hWnd: HWND; Msg: UINT; WPARAM: WPARAM;
  18.       LPARAM: LPARAM): LRESULT; stdcall;
  19.   public
  20.     constructor Create;
  21.     destructor Destroy; override;
  22.     procedure Run;
  23.   end;
  24.  
  25. implementation
  26.  
  27. function dummy(hWnd: HWND; Msg: UINT; WPARAM: WPARAM; LPARAM: LPARAM): LRESULT; stdcall;
  28. var
  29.   vData: Pointer;
  30. begin
  31.   vData := Pointer(GetWindowLongPtr(hWnd, GWLP_USERDATA));
  32.   Result := THello(VData).WindowProc(hWnd, Msg, WPARAM, LPARAM);
  33. end;
  34.  
  35. { THello }
  36.  
  37. function THello.WindowProc(hWnd: HWND; Msg: UINT; WPARAM: WPARAM;
  38.   LPARAM: LPARAM): LRESULT; stdcall;
  39. begin
  40.   case (Msg) of
  41.     WM_CREATE:
  42.     begin
  43.       MessageBox(0, 'Hello world!', PChar(someText), MB_OK or MB_USERICON);
  44.     end;
  45.     WM_CLOSE:
  46.     begin
  47.       PostQuitMessage(0);
  48.       Result := 0;
  49.     end;
  50.     else
  51.       Result := DefWindowProc(hWnd, Msg, WParam, LParam);
  52.   end;
  53.  
  54. end;
  55.  
  56. constructor THello.Create;
  57. var
  58.   WindowClass: WndClass;
  59.   hWindow: HWnd;
  60. begin
  61.   someText := 'Hello';
  62.   WindowClass.Style := cs_hRedraw or cs_vRedraw;
  63.   WindowClass.lpfnWndProc := @dummy;
  64.   WindowClass.cbClsExtra := 0;
  65.   WindowClass.cbWndExtra := 0;
  66.   WindowClass.hInstance := system.MainInstance;
  67.   WindowClass.hIcon := LoadIcon(0, idi_Application);
  68.   WindowClass.hCursor := LoadCursor(0, idc_Arrow);
  69.   WindowClass.hbrBackground := GetStockObject(WHITE_BRUSH);
  70.   WindowClass.lpszMenuName := nil;
  71.   WindowClass.lpszClassName := PChar(someText);
  72.  
  73.   if Windows.RegisterClass(WindowClass) = 0 then
  74.   begin
  75.     halt(1);
  76.   end;
  77.  
  78.   hWindow := CreateWindow(PChar(someText), 'Hello world program',
  79.     ws_OverlappedWindow, cw_UseDefault, cw_UseDefault, cw_UseDefault,
  80.     cw_UseDefault, 0, 0, system.MainInstance, nil);
  81.  
  82.   if hWindow = 0 then
  83.   begin
  84.     halt(1);
  85.   end;
  86.   SetWindowlongPtr(hWindow, GWLP_USERDATA, LONG_PTR(self));
  87.   ShowWindow(hWindow, CmdShow);
  88.   ShowWindow(hWindow, SW_SHOW);
  89.   UpdateWindow(hWindow);
  90. end;
  91.  
  92. destructor THello.Destroy;
  93. begin
  94.   inherited Destroy;
  95. end;
  96.  
  97. procedure THello.Run;
  98. var
  99.   AMessage: Msg;
  100. begin
  101.   while GetMessage(@AMessage, 0, 0, 0) do
  102.   begin
  103.     TranslateMessage(AMessage);
  104.     DispatchMessage(AMessage);
  105.   end;
  106.   Halt(AMessage.wParam);
  107. end;
  108.  
  109. end.
  110.  

the THello.WindowProc is running, but I can't access the variable sometext inside

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: windows API WNDPROC
« Reply #8 on: December 03, 2017, 11:10:23 pm »
the THello.WindowProc is running, but I can't access the variable sometext inside
1) please define can't access, it raises an exception or it doesn't show the string assigned?
2) are you sure that the SetWindowLongPtr has been executed when you first receive the WM_CREATE message? As far as I can see you do not check if the userdata is 0 or not.

Quote
Before returning, CreateWindow sends a WM_CREATE message to the window procedure.
from https://msdn.microsoft.com/en-us/library/windows/desktop/ms632679(v=vs.85).aspx
in which case you have to change the dummy procedure to something along the lines of.

Code: Pascal  [Select][+][-]
  1. function dummy(hWnd: HWND; Msg: UINT; WPARAM: WPARAM; LPARAM: LPARAM): LRESULT; stdcall;
  2. var
  3.   vData: Pointer;
  4. begin
  5.   if msg = wm_create then begin
  6.     vData := LPARAM;
  7.     if Assigned(vData) and (vData is THello) then Result := THello(VData).WindowProc(hWnd, Msg, WPARAM, LPARAM)
  8.   end else begin
  9.     vData := Pointer(GetWindowLongPtr(hWnd, GWLP_USERDATA));
  10.     if assigned(vData) then Result := THello(VData).WindowProc(hWnd, Msg, WPARAM, LPARAM)
  11.     else Result := -1;????
  12.   end;
  13. end;
  14.  

And ofcourse change the call to createwindow to something along the lines of
Code: Pascal  [Select][+][-]
  1. ....
  2.   hWindow := CreateWindow(PChar(someText), 'Hello world program',
  3.     ws_OverlappedWindow, cw_UseDefault, cw_UseDefault, cw_UseDefault,
  4.     cw_UseDefault, 0, 0, system.MainInstance, self);
  5. ....
  6.  

Make sure you read the complete createwindow documentation before handling the wm_Create message. It is a bit of a special case.
« Last Edit: December 03, 2017, 11:11:55 pm by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: windows API WNDPROC
« Reply #9 on: December 03, 2017, 11:24:49 pm »
Make sure you read the complete createwindow documentation before handling the wm_Create message. It is a bit of a special case.

you're right, I made a small test by replacing WM_CREATE with WM_KEYUP and it works as expected :o

« Last Edit: December 03, 2017, 11:44:13 pm by KemBill »

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: windows API WNDPROC
« Reply #10 on: December 04, 2017, 08:08:42 am »
I think that creating proper trampoline WNDPROC would catch all messages, e.g. the WM_CREATE etc.

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: windows API WNDPROC
« Reply #11 on: December 04, 2017, 09:29:39 am »
here is the link explaining what taazz was saying :

https://msdn.microsoft.com/en-us/library/windows/desktop/ff381400(v=vs.85).aspx

the only strange thing, is , in C you are allowed to write
Code: Pascal  [Select][+][-]
  1.  wc.lpfnWndProc   = DERIVED_TYPE::WindowProc;

In pascal due to strong typing, I am not allowed because "function of object" <> "function"


KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: windows API WNDPROC
« Reply #12 on: December 04, 2017, 10:50:36 am »
According to MSDN, the "right way" is to handle WM_NCCREATE messages

first add "self"  to CreateWindow
Code: Pascal  [Select][+][-]
  1.   hWindow := CreateWindow(PChar(someText), 'Hello world program',
  2.     ws_OverlappedWindow, cw_UseDefault, cw_UseDefault, cw_UseDefault,
  3.     cw_UseDefault, 0, 0, system.MainInstance, self);  
  4.  

the dummy proc becomes

Code: Pascal  [Select][+][-]
  1. function dummy(hWnd: HWND; Msg: UINT; WPARAM: WPARAM; LPARAM: LPARAM): LRESULT; stdcall;
  2. var
  3.   hello: THello;
  4.   pCreate: PCREATESTRUCT;
  5. begin
  6.   if msg = WM_NCCREATE then
  7.   begin
  8.     pCreate := PCREATESTRUCT(LPARAM);
  9.     hello :=THello(pCreate^.lpCreateParams);
  10.     SetWindowlongPtr(hWnd, GWLP_USERDATA, LONG_PTR(hello));
  11.   end
  12.   else
  13.   begin
  14.     hello := THello(LONG_PTR(GetWindowLongPtr(hWnd, GWLP_USERDATA)));
  15.   end;
  16.   if hello <> nil then
  17.     Result := THello(hello).WindowProc(hWnd, Msg, WPARAM, LPARAM)
  18.   else
  19.     Result := DefWindowProc(hWnd, Msg, WParam, LParam);
  20. end;
  21.  

and after THello.WindowProc can handle wm_create messages

Code: Pascal  [Select][+][-]
  1.  function THello.WindowProc(hWnd: HWND; Msg: UINT; WPARAM: WPARAM;
  2.   LPARAM: LPARAM): LRESULT; stdcall;
  3.  
  4. begin
  5.  
  6.   case (Msg) of
  7.     WM_CREATE:
  8.     begin
  9.       MessageBox(0, 'Hello world!', PChar(sometext), MB_OK or MB_USERICON);
  10.     end;
  11.     WM_KEYUP:
  12.     begin
  13.       MessageBox(0, 'Hello world!', PChar(sometext), MB_OK or MB_USERICON);
  14.     end;
  15.     WM_CLOSE:
  16.     begin
  17.       PostQuitMessage(0);
  18.       Result := 0;
  19.     end;
  20.     else
  21.       Result := DefWindowProc(hWnd, Msg, WParam, LParam);
  22.   end;
  23.  
  24. end;
  25.  

 

TinyPortal © 2005-2018