Параметр TPoint показывает правильные значения в Win32, но не в Win64 - PullRequest
0 голосов
/ 10 декабря 2018

enter image description here

Миграция Delphi-кода Win32 в Win64 с Delphi-кодом, особенно при использовании обработчиков событий или функции, принимающей параметр TPoint: в Win32 это показывало правильные значения для точкикоординаты x и y, но при чтении в Win64 значения x и y давали «ненужные» значения или некоторое время, совпадающее с передачей значения.

В моем случае TDM_Point (Msg.lParam) Msg.lParam действительно имеет значение {3997726} и после приведения к переменной TPoint P содержат {x = 30, y = 61} в бите win32 и в win64 Msg.lParam имеют значение {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 

Значение Msg устанавливается значением, возвращаемым посредством сообщения процесса

[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;      

, пожалуйста, найдите стек вызовов ниже.мы не подготовили ни одного сообщения.это готовится системой, когда мы нажимаем на элемент управления меткой формы.две верхние функции стека вызовов являются нашими функциями, в которых мы получаем значение msg из processmessage vcl.forms
msgruntimevalue

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Условное определение, которое вы используете, неверно : вы говорите, что Msg.lParam равно 3997726 в обоих случаях.Это может означать только одно: вы всегда получаете TSmallPoint, а никогда TPoint.

3997726 - это шестнадцатеричное $003D001E, которое показывает16-битные значения (два Smallint значения),

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

Очевидно, вы получаете TSmallPoint как Msg.lParam, даже в Win64 .Поэтому всегда приводите к TSmallPoint, никогда не приводите к TPoint (я не могу представить, что вы приведете к TPoint в Win32 , вы, вероятно, приведете к TDM_Point).

Поскольку Msg.lParam является 64-битным в Win64 , вы не можете напрямую привести его к TDM_Point [1] (или к TSmallPoint) - наверное, поэтомуВы сделали это TPoint в Win64 : чтобы соответствовать размеру 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

Итак, еще раз: полученное вами сообщение всегда содержит TSmallPoint.Не определяйте TDM_Point по-разному для разных платформ.

ОБНОВЛЕНИЕ

Добавленный скриншот показывает, что это сообщение WM_LBUTTONDOWN, , которое очень хорошо задокументировано :

lParam

Слово младшего разряда определяет x-координату курсора.Координата указана относительно верхнего левого угла клиентской области.

Старшее слово определяет y-координату курсора.Координата указана относительно верхнего левого угла клиентской области.


[1] Как подтвердил Дэвид Хеффернан .

0 голосов
/ 10 декабря 2018

Причина, по которой это происходит, заключается в том, что TSmallPoint является перекодированным, чьи поля X и Y расположены как Smallint (16-битное целое), в то время как TPoint использует его поля X и Y как FixedInt (32-битное целое число)

System.Types.TSmallPoint

System.Types.TPoint

Внутренние форматы данных (Delphi)

Таким образом, когда вы типизируете свои данные в структуру TPoint в своем 64-битном приложении, значение X для чтения фактически считывает блок памяти, который содержит значения X и Y, особенно если messege был создан в 32-битном приложении, которое использует структуру TSmallPoint.

Поэтому для решения вашей проблемы вам придется унифицировать структуру данных как в 32-битной, так и в 64-версии вашего приложения, чтобы иметь возможность отправлять сообщения между ними.

...