Как решить проблему с 32-битными результатами функции SendMessage в 64-битной среде Delphi
При переходе с 32-битных приложений на 64-битные в среде разработки Delphi, разработчики могут столкнуться с различными проблемами, одной из которых является некорректное интерпретирование результатов функций Windows API, таких как SendMessage. Эта функция может возвращать 32-битные значения в 64-битной среде, что приводит к неправильной интерпретации результатов, например, значение -1 может быть представлено как 0xFFFFFFFF, что в 64-битной системе интерпретируется как очень большое положительное число.
Описание проблемы
Разработчик столкнулся с проблемой при конвертации Delphi Windows программы в 64-битную версию. Используя функцию SendMessage для отправки сообщения EM_LINEINDEX компоненту, ожидалось, что в случае, если параметр WPARAM больше количества строк текста в компоненте, возвращаемое значение будет -1. Однако, в 64-битной среде, вместо -1 возвращалось значение 0xFFFFFFFF, что приводило к сбою сравнения.
Пример кода, демонстрирующий проблему:
procedure TForm1.Button1Click(Sender: TObject);
var
x: Int64;
begin
x := SendMessage(Memo1.Handle, EM_LINEINDEX, WPARAM(5), 0);
ShowMessage(Format('%d %X', [x, x]));
end;
В результате выполнения этого кода в строке отображается значение 4294967295 FFFFFFFF. При просмотре ассемблерного кода видно, что функция возвращает 00000000FFFFFFF в регистре rax. Если тип переменной x изменить на Integer, то проблема исчезает, однако при включенной проверке диапазонов ({$R+}) возникает исключение, связанное с переполнением диапазона.
Подтвержденный ответ
Возвращаемое значение, вероятно, является 32-битным внутри компонентов Edit/RichEdit. Разработчикам, вероятно, придется принять это как особенность и обрабатывать возвращаемое значение как 32-битное целое число. Это не должно стать проблемой, если не планируется хранение более 4 ГБ текста в этих компонентах.
Альтернативный ответ
Один из пользователей предложил написать собственную функцию Edit_LineIndex(), которая будет обрезать результат SendMessage() до типа Integer, и отключить проверку диапазонов и переполнения для этой функции.
Решение проблемы
Для решения проблемы можно использовать следующий подход:
Определить функцию обертку для SendMessage, которая будет принимать 64-битные параметры, но возвращать 32-битное значение после обрезки:
function SafeSendMessage(hWnd: HWND; Msg: UINT; wParam, lParam: LongInt): LongInt;
begin
Result := SendMessage(hWnd, Msg, WPARAM(wParam), LPARAM(lParam)) and $FFFFFFFF;
end;
Использовать эту функцию вместо стандартного SendMessage в вашем коде:
procedure TForm1.Button1Click(Sender: TObject);
var
x: LongInt;
begin
x := SafeSendMessage(Memo1.Handle, EM_LINEINDEX, 5, 0);
ShowMessage(Format('%d', [x]));
end;
Отключить проверку диапазонов только для этой функции, чтобы избежать исключений при конвертации типов:
{$R-}
procedure TForm1.Button1Click(Sender: TObject);
begin
// Код с использованием SafeSendMessage
end;
{$R+}
Следуя этим шагам, вы сможете корректно обрабатывать 32-битные результаты функций Windows API в 64-битной среде Delphi.
Разработчик сталкивается с некорректным представлением результатов 32-битных функций Windows API в 64-битной среде Delphi, что приводит к ошибкам в логике программы.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.