0

I'm reading an old Delphi book on Windows API. This is an example from it.

unit Unit1;

interface

uses
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
    System.Classes, Vcl.Graphics,
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
    TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
    private
        { Private declarations }
    public
        { Public declarations }
    end;

var
    Form1: TForm1;

implementation

{$R *.dfm}

{ Register the Window Class }
function RegisterClass: Boolean;
var
    WindowClass: TWndClass;
begin
    { setup our new window class }
    WindowClass.Style := CS_HREDRAW or CS_VREDRAW; { set the class styles }
    WindowClass.lpfnWndProc := @DefWindowProc;     { point to the default window procedure }
    WindowClass.cbClsExtra := 0;                   { no extra class memory }
    WindowClass.cbWndExtra := 0;                   { no extra window memory }
    WindowClass.hInstance := hInstance;            { the application instance }
    WindowClass.hIcon := 0;                        { no icon specified }
    WindowClass.hCursor := 0;                      { no cursor specified }
    WindowClass.hbrBackground := COLOR_WINDOW;     { use a predefined color }
    WindowClass.lpszMenuName := nil;               { no menu }
    WindowClass.lpszClassName := 'TestClass';      { the registered class name }

    { now that we have our class set up, register it with the system }
    Result := Winapi.Windows.RegisterClass(WindowClass) <> 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
    hWindow: HWND;
begin
    { Step 1: Register our new window class }
    if not RegisterClass then
    begin
        ShowMessage('RegisterClass failed');
        Exit;
    end;

    { Step 2: Create a window based on our new class }
    hWindow := CreateWindowEx(0, { no extended styles }
        'TestClass',             { the registered class name }
        'New Window',            { the title bar text }
        WS_OVERLAPPEDWINDOW,     { a normal window style }
        CW_USEDEFAULT,           { default horizontal position }
        CW_USEDEFAULT,           { default vertical position }
        CW_USEDEFAULT,           { default width }
        CW_USEDEFAULT,           { default height }
        0,                       { no owner window }
        0,                       { no menu }
        hInstance,               { the application instance }
        nil                      { no additional information }
        );

    { Step 3: If our window was created successfully, display it }
    if hWindow <> 0 then
    begin
        ShowWindow(hWindow, SW_SHOWNORMAL);
        UpdateWindow(hWindow);
    end
    else
    begin
        ShowMessage('CreateWindow failed');
        Exit;
    end;
end;

end.

The UI is very simple. I just drag a button and drop it on the form. However, when I run the program with Embarcadero® Delphi 11.0 Version 28.0.42600.6491 and click the button on the form, I got Project Project1.exe raised exception class ERangeError with message 'Range check error'.. Following that, I got this error.

enter image description here

Could anyone help to take a look where is the problem?

Btw, I just tried Delphi 7 and the program works. When I click the button, an empty window pops. enter image description here

user130268
  • 1,341
  • 2
  • 12
  • 20
  • According to the error message it looks like the Delphi IDE (`bds.exe`) is trying to autorecover `System.SysUtils.pas`. Have you perhaps had unsaved changes in it (the IDE should complain if you tried to save it anyway) then have the IDE crash on you? In such a case I would probably just try to reinstall Delphi. – fritten Dec 15 '21 at 20:22
  • I tried the code on my installation (Delphi 10.4 Version 27.0.40680.4203) and it works fine. – fritten Dec 15 '21 at 20:24

1 Answers1

1

Value of CW_USEDEFAULT constant does not align very well with declaration of CreateWindowEx function. Add following code to the top of Button1Click, near the var section:

const
  CW_USEDEFAULT = -MaxInt - 1;
Torbins
  • 2,111
  • 14
  • 16
  • 1
    Can't you simply cast `Winapi.Windows.CW_USEDEFAULT` to `Integer`? `CreateWindowEx(..., Integer(CW_USEDEFAULT), ...);` Otherwise, just turn off range checking around the `CreateWindowEx()` call using `{$R-}` – Remy Lebeau Dec 15 '21 at 18:00
  • In Delphi 10.4 Version 27.0.40680.4203 it's declared as `const CW_USEDEFAULT = DWORD($80000000);` (and simply generates W1012 if used when calling `CreateWindowEx`). Both `Integer` and `DWORD` are 32 bits, so even if Delphi's `CreateWindowEx` interface uses Integers in place of DWORDs it is still binary compatible with the exported DLL function. – fritten Dec 15 '21 at 19:53
  • Thank you, it works. @RemyLebeau's suggestion works too. – user130268 Dec 15 '21 at 21:38
  • @fritten "*it's declared as `const CW_USEDEFAULT = DWORD($80000000);`*" - yes, and because of that, a range check error occurs when passing that `DWORD` to an `Integer` parameter and range checking is enabled, since the value is higher than `MaxInt` – Remy Lebeau Dec 15 '21 at 21:47
  • @RemyLebeau: Yes, you're absolutely right. I don't see how it has anything to do with the question however. – fritten Dec 15 '21 at 22:55
  • Nevermind. I overlooked that he mentioned it. I was hung up on that error... :P – fritten Dec 15 '21 at 22:58