Lazarus

Programming => Operating Systems => Windows => Topic started by: RAW on February 10, 2018, 07:57:47 am

Title: WM_DISPLAYCHANGE // recognize a different screen resolution
Post by: RAW on February 10, 2018, 07:57:47 am
Maybe this is useful for anybody....  :)

This is working for me...
EDIT: Instead of "ShowMessage" I use now "Color" in this example... As GetMem mentioned "ShowMessage" is a bad idea inside a WndProc.
Code: Pascal  [Select][+][-]
  1. UNIT Unit1;
  2. {$MODE OBJFPC}{$H+}{$J-}
  3.  
  4. Interface
  5.  USES
  6.   Windows, Classes, Forms, Dialogs;
  7.  
  8.  TYPE
  9.   TForm1 = Class(TForm)
  10.    Procedure FormCreate (Sender: TObject);
  11.    Procedure FormClick  (Sender: TObject);
  12.  
  13.     PRIVATE
  14.      oldProc: Windows.WndProc;
  15.   End;
  16.  
  17.  VAR
  18.   Form1: TForm1;
  19.  
  20. Implementation
  21. {$R *.LFM}
  22.  
  23.  
  24. Function WndCallback
  25.   (MyWND: HWND; uMSG: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; StdCall;
  26.  Begin
  27.   If (uMSG = WM_DISPLAYCHANGE)
  28.   Then Form1.Color:= RGB(0, 80, 255);
  29.  
  30.   Result:= CallWindowProc(Form1.oldProc, MyWND, uMSG, wParam, lParam);
  31.  End;
  32.  
  33.  
  34. Procedure TForm1.FormCreate(Sender: TObject);
  35.  Begin
  36.   oldProc:= Windows.WndProc(GetWindowLongPtr(Handle, GWL_WNDPROC));
  37.   SetWindowLongPtr(Handle, GWL_WNDPROC, PtrInt(@WndCallback));
  38.  End;
  39.  
  40.  
  41. Procedure TForm1.FormClick(Sender: TObject);
  42.  Begin
  43.   Color:= RGB(0, 187, 0);
  44.  End;
  45.  
  46. END.


// not working for me...
Code: Pascal  [Select][+][-]
  1. unit unit1;
  2. {$mode objfpc}{$H+}
  3.  
  4. interface
  5.  
  6. uses
  7.  Windows, Classes, SysUtils, Forms, Dialogs;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.   private
  12.    procedure WMDisplayChange(var msg: TMessage); message WM_DISPLAYCHANGE;
  13.   end;
  14.  
  15. var
  16.   Form1: TForm1;
  17.  
  18. implementation
  19.  
  20. {$R *.lfm}
  21.  
  22. procedure TForm1.WMDisplayChange(var msg: TMessage);
  23. begin
  24.  showmessage('Display Changed');
  25. end;
  26.  
  27. end.

WndProc (standard way) isn't working for me either...
Title: Re: WM_DISPLAYCHANGE (Is there any workaround ???)
Post by: GetMem on February 10, 2018, 08:39:39 am
@RAW
The ShowMessage inside WndCallback is wrong, I mean fundamentally wrong. You cannot block the message chain with a function like ShowMessage. I know you put it there only for testing purposes, still be careful with blocking functions.
Title: Re: WM_DISPLAYCHANGE // recognize a different screen resolution
Post by: RAW on February 10, 2018, 09:05:43 am
@GetMem
Yeah, I added some info lines regarding this... Thanks...  :)
Title: Re: WM_DISPLAYCHANGE // recognize a different screen resolution
Post by: taazz on February 10, 2018, 10:39:07 am

// not working for me...
Code: Pascal  [Select][+][-]
  1. unit unit1;
  2. {$mode objfpc}{$H+}
  3.  
  4. interface
  5.  
  6. uses
  7.  Windows, Classes, SysUtils, Forms, Dialogs;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.   private
  12.    procedure WMDisplayChange(var msg: TMessage); message WM_DISPLAYCHANGE;
  13.   end;
  14.  
  15. var
  16.   Form1: TForm1;
  17.  
  18. implementation
  19.  
  20. {$R *.lfm}
  21.  
  22. procedure TForm1.WMDisplayChange(var msg: TMessage);
  23. begin
  24.  showmessage('Display Changed');
  25. end;
  26.  
  27. end.

WndProc (standard way) isn't working for me either...
that is because the default handler filters unsupported messages out from the internal message loop. You should use the getwindowlong (look the default handler proc for info) to get the attached wincontrol instead of relying on a global variable that might or might not be valid.
Title: Re: WM_DISPLAYCHANGE // recognize a different screen resolution
Post by: RAW on February 11, 2018, 09:16:47 pm
@taazz
Thanks for the info...
What do you mean? Something like this:
Code: Pascal  [Select][+][-]
  1. Procedure TForm1.FormCreate(Sender: TObject);
  2.  Begin
  3.   oldProc:= Windows.WndProc(GetWindowLongPtr(Handle, GWL_WNDPROC));
  4.   SetWindowLongPtr(Handle, GWL_WNDPROC, PtrInt(@WndCallback));
  5.  End;
  6.  
... is working good too...  :)

But I heard it's better to use this:
Code: Pascal  [Select][+][-]
  1.  TYPE
  2.   TSubClassProc = Function
  3.    (hWnd: HWND; uMSG: UINT; wParam: WPARAM; lParam: LPARAM;
  4.     uIDSubclass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; StdCall;
  5.  ........................
  6.  
  7.  Function SetWindowSubclass
  8.   (hWnd: HWND; pfnSubclass: TSubClassProc; uIDSubclass: UINT_PTR;
  9.    dwRefData: DWORD_PTR): BOOL; StdCall; External 'ComCtl32.dll';
  10.  
  11.  Function RemoveWindowSubclass
  12.   (hWnd: HWND; pfnSubclass: TSubClassProc; uIDSubclass: UINT_PTR): BOOL;
  13.    StdCall; External 'ComCtl32.dll';
  14.  
  15.  Function DefSubclassProc
  16.   (hWnd: HWND; uMSG: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
  17.    StdCall; External 'ComCtl32.dll';
  18.  
I'm working on it...  :)
Title: Re: WM_DISPLAYCHANGE // recognize a different screen resolution
Post by: taazz on February 11, 2018, 09:40:52 pm
@taazz
Thanks for the info...
What do you mean? Something like this:
Code: Pascal  [Select][+][-]
  1. Procedure TForm1.FormCreate(Sender: TObject);
  2.  Begin
  3.   oldProc:= Windows.WndProc(GetWindowLongPtr(Handle, GWL_WNDPROC));
  4.   SetWindowLongPtr(Handle, GWL_WNDPROC, PtrInt(@WndCallback));
  5.  End;
  6.  
No I mean

Code: Pascal  [Select][+][-]
  1. UNIT Unit1;
  2. {$MODE OBJFPC}{$H+}{$J-}
  3.  
  4. Interface
  5.  USES
  6.   Windows, Classes, Forms, Dialogs;
  7.  
  8.  TYPE
  9.   TForm1 = Class(TForm)
  10.   Procedure FormCreate (Sender: TObject);
  11.    PRIVATE
  12.     PrevWndProc: Windows.WNDPROC;
  13.   End;
  14.  
  15.  VAR
  16.   Form1: TForm1;
  17.  
  18. Implementation
  19. {$R *.LFM}
  20.  
  21.  
  22. Function WndCallback (MyHWND: HWND; uMSG: UINT; wParam: WParam; lParam: LParam): LRESULT; StdCall;
  23. var
  24.   WindowInfo: PWin32WindowInfo;
  25. Begin
  26.   WindowInfo := GetWin32WindowInfo(Window); //<---- step one
  27.   If (uMSG = WM_DISPLAYCHANGE)
  28.   Then ShowMessage('Display Changed') // only for testing purposes...
  29.   Else Form1.Color:= RGB(0, 170, 0);  // don't use this in real world apps, take
  30.                                       // a look at the following post from GetMem
  31.  
  32.   if WindowInfo^.WinControl is TForm1 then //Eliminate form1 global variable for safer handling.
  33.   Result:= CallWindowProc(TForm1(WindowInfo^.WinControl).PrevWndProc, MyHWND, uMSG, WParam, LParam);
  34.  End;
  35.  
  36.  
  37. Procedure TForm1.FormCreate(Sender: TObject);
  38.  Begin
  39.   PrevWndProc:= Windows.WNDPROC
  40.   (SetWindowLongPtr(Handle, GWL_WNDPROC, PtrInt(@WndCallback)));
  41.  End;
  42.  
  43. END.

Take a closer look on WindowProc in the file win32callback.inc it has all the information you need.
TinyPortal © 2005-2018