Author Topic: Form scaled no border  (Read 777 times)

Hobbit00

  • New Member
  • *
  • Posts: 22
Re: Form scaled no border
« Reply #15 on: August 28, 2026, 03:51:39 pm »
Here it is ....

@Thaddy, you know better FPC, accomodate some functions (for example the OnWNCPaint function ... should be a function of class).

N.B.:
Quote
HookNC := TNCPaintHook.Create(Handle, @OnWNCPaint);
should be moved on "FormCreate" event, but with Delphi in the past I had some issue to create something that use the "handle" ... like if the handle is not valid. But it cannot stay on "OnShow" of course (untill a flag was tested to execute or not the create).

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. //{$mode objfpc}{$H+}
  4. {$mode Delphi}
  5.  
  6. interface
  7.  
  8. uses
  9.   Windows, Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  10.   uNCPaintHook;
  11.  
  12. type
  13.  
  14.   { TForm1 }
  15.  
  16.   TForm1 = class(TForm)
  17.     Button1: TButton;
  18.     procedure FormCreate(Sender: TObject);
  19.     procedure FormDestroy(Sender: TObject);
  20.     procedure FormShow(Sender: TObject);
  21.   private
  22.     HookNC: TNCPaintHook;
  23.   public
  24.  
  25.   end;
  26.  
  27. function OnWNCPaint(var MSG: TMsg): boolean;
  28.  
  29. var
  30.   Form1: TForm1;
  31.  
  32. implementation
  33.  
  34. {$R *.lfm}
  35.  
  36. function OnWNCPaint(var MSG: TMsg): boolean;
  37. var
  38.   DC: HDC;
  39.   R: TRect;
  40.   Brush: HBRUSH;
  41. begin
  42.   DC := GetWindowDC(Form1.Handle);
  43.   try
  44.     GetWindowRect(Form1.Handle, R);
  45.     OffsetRect(R, -R.Left, -R.Top); // window-relative coords
  46.     Brush := CreateSolidBrush(ColorToRGB(Form1.Color));
  47.     FillRect(DC, R, Brush);
  48.     DeleteObject(Brush);
  49.   finally
  50.     ReleaseDC(Form1.Handle, DC);
  51.   end;
  52.   Result := True;
  53. end;
  54.  
  55. { TForm1 }
  56.  
  57. procedure TForm1.FormCreate(Sender: TObject);
  58. begin
  59. end;
  60.  
  61. procedure TForm1.FormDestroy(Sender: TObject);
  62. begin
  63.   HookNC.Detach;
  64. end;
  65.  
  66. procedure TForm1.FormShow(Sender: TObject);
  67. begin
  68.   HookNC := TNCPaintHook.Create(Handle, @OnWNCPaint);
  69.   HookNC.Attach;
  70. end;
  71.  
  72. end.

Code: Pascal  [Select][+][-]
  1. unit uNCPaintHook;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows, LCLIntf, LCLType, Classes, Forms;
  9.  
  10.  
  11.   type _OnMsgHook = function(var Msg: TMsg): Boolean;
  12.  
  13.   TNCPaintHook = class
  14.   private
  15.     FTarget: HWND;
  16.     FAttached: Boolean;
  17.     // Callback : True = handled (exit), False = continue to original
  18.     FOnNCPaint: function(var Msg: TMsg): Boolean;
  19.   public
  20.     constructor Create(ATarget: HWND; AOnNCPaint: _OnMsgHook);
  21.     destructor Destroy; override;
  22.     procedure Attach;
  23.     procedure Detach;
  24.  
  25.     class var FOriginalProc: Windows.WNDPROC;
  26.     class var FHookMap: TNCPaintHook;
  27.   end;
  28.  
  29.   function HookedWndProc(hwnd1: HWND; Msg: UINT; wParam: WPARAM;
  30.     lParam: LPARAM): LRESULT; stdcall;
  31.  
  32.  
  33. implementation
  34.  
  35. { TNCPaintHook }
  36.  
  37. function HookedWndProc(hwnd1: HWND; Msg: UINT; wParam: WPARAM;
  38.   lParam: LPARAM): LRESULT; stdcall;
  39. var
  40.   Hook: TNCPaintHook;
  41.   WinMsg: TMsg;
  42.   Handled: Boolean;
  43. begin
  44.   Hook := TNCPaintHook.FHookMap;
  45.   if (Hook <> nil) and (Hook.FAttached) and (hwnd1 = Hook.FTarget) then
  46.   begin
  47.     if Msg = WM_NCPaint then
  48.     begin
  49.       WinMsg := Default(tagMsg);
  50.       WinMsg.Hwnd   := hwnd1;
  51.       WinMsg.Message := Msg;
  52.       WinMsg.WParam  := wParam;
  53.       WinMsg.LParam  := lParam;
  54.  
  55.       Handled := Hook.FOnNCPaint(WinMsg);
  56.       if Handled then
  57.       begin
  58.         Result := 0;  // Any value here
  59.         Exit;
  60.       end;
  61.     end;
  62.   end;
  63.   // Fallthrough:  original WndProc (LCL)
  64.   if (Hook <> nil) and (Hook.FOriginalProc <> nil) then
  65.     Result := CallWindowProc(TNCPaintHook.FOriginalProc, hwnd1, Msg, wParam, lParam)
  66.   else
  67.     Result := DefWindowProc(hwnd1, Msg, wParam, lParam);
  68. end;
  69.  
  70. constructor TNCPaintHook.Create(ATarget: HWND;
  71.   AOnNCPaint: _OnMsgHook);
  72. begin
  73.   inherited Create;
  74.   TNCPaintHook.FHookMap := nil;
  75.   FTarget     := ATarget;
  76.   FOnNCPaint  := AOnNCPaint;
  77.   FAttached   := False;
  78.   FOriginalProc := nil;
  79. end;
  80.  
  81. destructor TNCPaintHook.Destroy;
  82. begin
  83.   if FAttached then Detach;
  84.   inherited;
  85. end;
  86.  
  87. procedure TNCPaintHook.Attach;
  88. begin
  89.   if FAttached then Exit;
  90.   TNCPaintHook.FHookMap := Self;
  91.   FOriginalProc := Windows.WNDPROC(
  92.     GetWindowLongPtr(FTarget, GWLP_WNDPROC));
  93.   SetWindowLongPtr(FTarget, GWLP_WNDPROC,
  94.     LONG_PTR(@HookedWndProc));
  95.   FAttached := True;
  96. end;
  97.  
  98. procedure TNCPaintHook.Detach;
  99. begin
  100.   if not FAttached then Exit;
  101.   SetWindowLongPtr(FTarget, GWLP_WNDPROC,
  102.     LONG_PTR(FOriginalProc));
  103.   FAttached := False;
  104.   TNCPaintHook.FHookMap := nil;
  105. end;
  106.  
  107. end.

P.S.: Why this ? Because LCL predate WM_NCPAINT ...
« Last Edit: August 28, 2026, 04:30:53 pm by Hobbit00 »

Stavel

  • New member
  • *
  • Posts: 7
Re: Form scaled no border
« Reply #16 on: August 28, 2026, 07:22:43 pm »
Thank you for your interest in this thread, but Lazarus is reporting a compilation error for me.
Code: Pascal  [Select][+][-]
  1. HookNC := TNCPaintHook.Create(Handle, @OnWNCPaint);
unit1.pas(61,52) Error: Incompatible type for arg no. 2: Got "<address of function(var MSG):Boolean;Register>", expected "<procedure variable type of function(var tagMSG):Boolean;Register>"

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: Form scaled no border
« Reply #17 on: August 28, 2026, 07:39:37 pm »
The first unit is mode delphi, the second mode objfpc. Just remove the @
Any "programmer" that knows only one programming language is not a programmer

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: Form scaled no border
« Reply #18 on: August 28, 2026, 07:47:10 pm »
P.S.: Why this ? Because LCL predate WM_NCPAINT ...
So you mean it has never been updated since 27 years? (28, because D7 works)
There are two real bugs:
- first, the TWMNCActivate  message record is simply missing, I assume for the same 27 years....(introduced win2000)
- second, painting on the non-client area should be possible without hastle.
Anyway, Martin's tiny corrections made the original code working code. But that is not good enough.
Any "programmer" that knows only one programming language is not a programmer

Hobbit00

  • New Member
  • *
  • Posts: 22
Re: Form scaled no border
« Reply #19 on: August 28, 2026, 09:16:12 pm »
P.S.: Why this ? Because LCL predate WM_NCPAINT ...
So you mean it has never been updated since 27 years? (28, because D7 works)
There are two real bugs:
- first, the TWMNCActivate  message record is simply missing, I assume for the same 27 years....(introduced win2000)
- second, painting on the non-client area should be possible without hastle.
Anyway, Martin's tiny corrections made the original code working code. But that is not good enough.
I don't understand ... simply LCL do not propagate the message WM_NCPaint. Before "LCL management" the message exist, after no more.
If that is the situation since 27 .. 37 or more years ... it's so.
There are other lot of messages that Lazarus locks ... from what I read on the forum.

But all this isn't all that strange, because Lazarus is heavily "dependent" on multiple platforms, and for this reason I think many things aren't as well developed in detail for a specific platform.
The important thing is that we can overcome the obstacle with standard tools; then, if someone decides it's important, we'll resolve the situation in the most standard way.

P.S.: I know less than nothing about Lazarus / FPC so my apologies if I write sily things.

Xenno

  • Full Member
  • ***
  • Posts: 203
    • BS Programs
Re: Form scaled no border
« Reply #20 on: August 29, 2026, 05:53:00 am »
I would like the form to be frameless and scalable

An alternative:
  • Try different path: WM_NCCALCSIZE + WM_NCHITTEST
  • Check video (https://youtu.be/dO7QXtUWbZY) as a reference (Delphi)
  • It is surely doable with Lazarus as seen in attachment.
Lazarus 4.6, Windows 10, Website, YouTube channel
If I want to share, I give. If I want money, I sell. I don't set traps.

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: Form scaled no border
« Reply #21 on: August 29, 2026, 06:11:29 am »
  • Try different path: WM_NCCALCSIZE + WM_NCHITTEST
  • Check video (https://youtu.be/dO7QXtUWbZY) as a reference (Delphi)
  • It is surely doable with Lazarus as seen in attachment.
I have done that already in my zipped (2' reply) project. Doesn't work, but does work in Delphi.
Since it is straight Windows API it should work, so there is a bug.
OTOH Martin fixed OP's original code and that works.
« Last Edit: August 29, 2026, 06:13:50 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

Xenno

  • Full Member
  • ***
  • Posts: 203
    • BS Programs
Re: Form scaled no border
« Reply #22 on: August 29, 2026, 06:31:25 am »
WM_NCCalcSize is one of the message that excluded by Lazarus to be passed to form. Needs to hook the WndProc.
Lazarus 4.6, Windows 10, Website, YouTube channel
If I want to share, I give. If I want money, I sell. I don't set traps.

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: Form scaled no border
« Reply #23 on: August 29, 2026, 06:38:27 am »
Got it working. Get my DelphiExample.zip, convert to Lazarus
- Add the TWMNCActivate record, it is missing in the LCL.
- In the form1.Oncreate set Form1.BorderStyle := bsNone
Lazarus needs this specified, unlike Delphi: that is a bug, but hardly worth fixing.

Now it works, including moving etc.
My code in reply #7 then also works, as does the code in reply #6 from Hobbit00, but there you need to set
Code: Pascal  [Select][+][-]
  1.   Style := FOldStyle and not (
  2.     WS_CAPTION
  3.     or WS_SYSMENU
  4.     or WS_MINIMIZEBOX
  5.     or WS_MAXIMIZEBOX
  6.     or WS_THICKFRAME  // was commented out.
  7.   );
as well as borderstyle := bsNone.

« Last Edit: August 29, 2026, 06:47:26 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

dsiders

  • Hero Member
  • *****
  • Posts: 1696
Re: Form scaled no border
« Reply #24 on: August 29, 2026, 08:00:26 am »
- Add the TWMNCActivate record, it is missing in the LCL.

You keep saying that, but it's not really true. It just has a different name in LCL.
https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/lcl/lmessages.pp?ref_type=heads#L463

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: Form scaled no border
« Reply #25 on: August 29, 2026, 08:33:10 am »
No, that is lmessages. My code refers to Windows messages unit. It is part of the Windows API and belongs there. Would mean that I can not write Windows code without dependency on lcl (and I do that quite a lot).
That is the whole point.! Even Delphi7 has it declared.
Pointing to lmessages is a bit nonsense.
« Last Edit: August 29, 2026, 08:41:28 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

Stavel

  • New member
  • *
  • Posts: 7
Re: Form scaled no border
« Reply #26 on: September 01, 2026, 09:34:36 am »
which means it's not feasible

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: Form scaled no border
« Reply #27 on: September 01, 2026, 09:42:46 am »
which means it's not feasible
I don't know if it is feasible to back-port to 3.2.4, though, since I noticed the RC's are bumped and the branch is now 3.2.4 (which in itself is good news).
No it is feasible as I showed. There is a bug report to fix the missing message structure for Windows. @Marcov: did you see that yet?
On e.g. Linux/Qt6 the issue does not exist. Just Windows.
[edit]
@Marcov replied - and closed  >:(   (not really angry) - there is already the same structure, yes there is, but that structure is for the client area, not the non-client area. That can be fixed with an alias.
Then it compiles on both Delphi and Freepascal, independent of LCL, plain windows.
I have requested to re-open.

Usually I would not make a point of such things, but this is simply the API.
The correct fix is a one liner unless it affects declaration order.
« Last Edit: September 01, 2026, 12:06:07 pm by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

Stavel

  • New member
  • *
  • Posts: 7
Re: Form scaled no border
« Reply #28 on: September 09, 2026, 04:45:45 pm »
When can we expect the update?
Best regards

 

TinyPortal © 2005-2018