Watch, Follow, &
Connect with Us
Public Report
Report From:    [ Add a report in this area ]  
Report #:  60698   Status: Closed
Get/SetWindowLong prototypes bugged; they don't take LONG_PTRs
Project:   Build #:  11.0.2902.10471
Version:    11.2 Submitted By:   Jordan Russell
Report Type:  Basic functionality failure Date Reported:  4/9/2008 3:24:44 PM
Severity:    Serious / Highly visible problem Last Updated: 3/20/2012 2:24:39 AM
Platform:    All versions Internal Tracking #:   259068
Resolution: Deferred to Next Rel (Resolution Comments) Resolved in Build: :
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
Description
In Delphi.NET 2007's Windows unit, GetWindowLong and SetWindowLong were changed to take/return LONG_PTRs in an attempt to give them support for 64-bit values on 64-bit Windows.

This, however, wasn't the correct approach at all.

In 64-bit Windows, Microsoft actually did not make any alterations to the prototypes of GetWindowLong and SetWindowLong. They still take/return 32-bit LONGs (Longints) exactly as on 32-bit Windows. (Refer to WinUser.h and the Platform SDK docs.)

Rather than update the existing functions, Microsoft instead opted to introduce two new functions -- GetWindowLongPtr and SetWindowLongPtr -- that applications are to use when 64 bits are required.

It is these functions that Delphi.NET should be calling on 64-bit Windows.

Presently, by trying to pass 64-bit arguments (LONG_PTRs) to functions designed to accept only 32-bit arguments (LONGs), and by treating 32-bit results as if they were 64-bit results, it is relying on implementation details on the Windows side which could change at any moment.

For example: In the case of GetWindowLong, by declaring the result type as a LONG_PTR (64-bit), the assumption is being made that the function correctly initializes all 64 bits of RAX. Since GetWindowLong actually returns a LONG (32-bit), Windows could legally set only the lower 32 bits of RAX, leaving the upper 32 bits zero or garbage.

====== FIX ======

To eliminate this problem, while still retaining compatibility with existing code, GetWindowLong* and SetWindowLong* should be replaced with wrapper functions that call the proper Ptr versions when running on 64-bit Windows. (This is exactly what Microsoft has done in Windows Forms.)

The code is too long to paste here, so I've attached it. See WindowLongFix.pas.
Steps to Reproduce:
At very least, this breaks SetWindowLongA and SetWindowLongW, causing them to clip their arguments to 32 bits. (*All* of the Get/SetWindowLong prototypes are incorrect, though.)

Steps to reproduce (on Windows XP x64 Edition SP2):

1. Create a new Delphi.NET VCL Forms Application.

2. Go to Project Options, Compiler, and set Target Platform to "x64".

3. Drop a TMemo on the form, and set its Align property to alClient.

4. Create an OnCreate handler on the form, and paste the following code:

procedure TForm1.FormCreate(Sender: TObject);
const
  NewValue = $1122334455667788;
var
  ActualValue: LONG_PTR;
begin
  Assert(IntPtr.Size = 8);  // This test must be run on a 64-bit build!

  SetWindowLong(Handle, GWL_USERDATA, NewValue);
  ActualValue := GetWindowLong(Handle, GWL_USERDATA);
  Memo1.Lines.Add(Format('SetWindowLong($%.16x) -- GetWindowLong() = $%.16x',
    [NewValue, ActualValue]));

  SetWindowLongA(Handle, GWL_USERDATA, NewValue);
  ActualValue := GetWindowLongA(Handle, GWL_USERDATA);
  Memo1.Lines.Add(Format('SetWindowLongA($%.16x) -- GetWindowLongA() = $%.16x',
    [NewValue, ActualValue]));

  SetWindowLongW(Handle, GWL_USERDATA, NewValue);
  ActualValue := GetWindowLongW(Handle, GWL_USERDATA);
  Memo1.Lines.Add(Format('SetWindowLongW($%.16x) -- GetWindowLongW() = $%.16x',
    [NewValue, ActualValue]));
end;

Actual results:

SetWindowLong($1122334455667788) -- GetWindowLong() = $1122334455667788
SetWindowLongA($1122334455667788) -- GetWindowLongA() = $0000000055667788   <-- upper 32 bits were zeroed!
SetWindowLongW($1122334455667788) -- GetWindowLongW() = $0000000055667788   <-- upper 32 bits were zeroed!

Expected results:

SetWindowLong($1122334455667788) -- GetWindowLong() = $1122334455667788
SetWindowLongA($1122334455667788) -- GetWindowLongA() = $1122334455667788
SetWindowLongW($1122334455667788) -- GetWindowLongW() = $1122334455667788

(The Get/SetWindowLong versions in WindowLongFix.pas (see attachment) produce the expected results.)
Workarounds
None
Attachment
WindowLongFix.zip
Comments

None

Server Response from: ETNACODE01