首页 文章

TPoint参数在Win32中显示正确的值,但在Win64中不显示

提问于
浏览
0

使用Delphi代码将delphi Win32代码迁移到Win64,特别是在使用事件处理程序或采用TPoint参数的函数时:在Win32中,这显示了点的x和y坐标的正确值,但在Win64中读取x和y给出了“垃圾”值或者与 Value 相同的时间传递给它 .

在我的情况下,TDM_Point(Msg.lParam)Msg.lParam确实具有值{3997726}并且在转换为TPoint变量P之后在win32位中包含{x = 30,y = 61}并且在win64中具有值{3997726}在win32中相同,但在转换为TPoint变量后,P包含{x = 3997723,y = 0})

条件定义如下:

{$IFDEF WIN32}
    TDM_Point = TSmallPoint;
{$ELSE}
    TDM_Point = TPoint;
{$ENDIF}

示例代码如下:

Function process
begin
  If Form.Handle = Msg.hWnd Then
  begin
    Control := SearchControl ( Form, TDM_Point(Msg.lParam) );         
    //Msg is type of tagMSG 
  end
end


Function Form.SearchControl ( Parent : tWinControl; P : TDM_Point ) : tControl;
Var
  Index   : Integer;
  Control : tControl;
  Rect    : tRect;
  tmpPoint : TPoint;
Begin            
  //code            
end

消息值由Process Message的值返回设置

[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
procedure TApplication.HandleMessage;
var
  Msg: TMsg;
begin
  if not ProcessMessage(Msg) then Idle(Msg);
end;

值设置如下:

If Drawing Then
Begin
{$IFNDEF WIN32}
  { Map coordinates to parent of chosen control,
    or to form in no control is chosen }
  If EditControls.Count > 0 Then
     MapWindowPoints ( Msg.hWnd,
                       tControl(EditControls.Objects[0]).Parent.Handle,
                       Msg.lParam, 1 )
  Else
     MapWindowPoints ( Msg.hWnd, fEditForm.Handle,
                       Msg.lParam, 1 );
{$ELSE}
  x:=GetSystemMetrics(SM_CXFRAME);
  if abs((Msg.Pt.X-fEditForm.left-x) - TDM_Point(Msg.lParam).X)  > 0 then
    TDM_Point(Msg.lParam).X:=Msg.Pt.X-fEditForm.left-x;

  x:=GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
  if abs((Msg.Pt.Y-fEditForm.top-x) - TDM_Point(Msg.lParam).Y)  > 0 then
    TDM_Point(Msg.lParam).Y:=Msg.Pt.Y-fEditForm.top-x;
{$ENDIF}
  Start.X   := TDM_Point(Msg.lParam).X;
  Start.Y   := TDM_Point(Msg.lParam).Y;
  Last    := Start;
  SetSelection;
  SetClipRect;
End;

请在下面找到调用堆栈 . 我们没有准备任何消息 . 当我们点击表格的标签控制时,这是由系统准备的 . 调用堆栈的前两个函数是我们的函数,我们从vcl.forms的processmessage中获取msg值

2 回答

  • 0

    发生这种情况的原因是因为TSmallPoint是一个重新组合,其X和Y字段被称为Smallint(16位整数),而TPoint将其X和Y字段视为FixedInt(32位整数)

    System.Types.TSmallPoint

    System.Types.TPoint

    Internal Data Formats (Delphi)

    因此,当您在64位应用程序中将数据类型转换为TPoint结构时,读取X值实际上会读取包含X和Y值的内存块,尤其是在使用TSmallPoint结构的32位应用程序中创建了messege时 .

    因此,为了解决您的问题,您必须在应用程序的32位和64位版本上统一数据结构,以便能够在两者之间发送消息 .

  • 1

    The conditional define you use is wrong :你说 Msg.lParam 在这两种情况下都是 3997726 . 这只能意味着一件事:你 always 收到 TSmallPointnever a TPoint .

    3997726 是十六进制 $003D001E ,显示16位值(两个 Smallint 值),

    X = $001E (decimal value 30) 
    Y = $003D (decimal value 61).
    

    显然,即使在Win64中,您收到的 TSmallPointMsg.lParam . 所以总是强制转换为 TSmallPoint ,永远不会转换为 TPoint (我无法想象你在Win32中转换为 TPoint ,你可能会转换为 TDM_Point ) .

    由于 Msg.lParam 在Win64中为64位,因此无法直接将其强制转换为 TDM_Point [1](或 TSmallPoint ) - 我猜这就是为什么你在Win64中将其设为 TPoint :以匹配 Msg.lParam 的大小 .

    但是可以相互铸造不同尺寸的整体类型 . 因此,您可以使用中间强制转换为正确大小的整数类型(此处为 UInt32 ,这是一个32位无符号整数),例如就像是:

    type
      // No conditional define! Always TSmallPoint!
      TDM_Point = TSmallPoint; // 2 x 16 bit = 32 bit
    

    然后在你的代码中:

    begin
      X := TDM_Point(UInt32(Msg.lParam)).X; // or: TSmallPoint(UInt32(Msg.lParam)).X
      Y := TDM_Point(UInt32(Msg.lParam)).Y; // or: TSmallPoint(UInt32(Msg.lParam)).Y
    

    再说一次: the message you receive always seems to contain a TSmallPoint. Do not define TDM_Point differently for different platforms.

    更新

    您添加的屏幕截图显示消息为WM_LBUTTONDOWN,which is very well documented

    lParam低位字指定光标的x坐标 . 坐标相对于客户区的左上角 . 高位字指定光标的y坐标 . 坐标相对于客户区的左上角 .


    [1]正如David Heffernan所证实的那样 .

相关问题