Ответ 1
Проблема заключается в неправильном использовании вложенной функции в качестве обратного вызова. Вероятность реализации с использованием вложенной функции таким образом работает для 32-битного компилятора, если вложенная функция не относится к каким-либо локальным переменным окружающих функций.
Однако, как только вложенная функция ссылается на любые такие локальные переменные, необходимо передать дополнительный скрытый параметр, чтобы вложенная функция могла получить доступ к фреймам стека окружающих функций. А для 64-битного компилятора всегда передается скрытый дополнительный параметр.
В Интернете вы найдете множество примеров, где люди демонстрируют передачу вложенных функций в качестве обратных вызовов. Но все эти примеры нарушают документированные правила языка:
Вложенные процедуры и функции (процедуры, объявленные в других подпрограммах) не могут использоваться как процедурные значения, а также не могут предопределяться процедуры и функции.
Вам нужно прекратить использование вложенных функций для обратных вызовов. Вы должны объявить функцию обратного вызова глобальной областью. Передайте поток памяти через член dwCookie
структуры EDITSTREAM
.
// This compiles now, but the callback implementation is wrong, see below
function RichEditCallBack(dwCookie: DWORD_PTR; pbBuff: PByte;
CB: Longint; var pCB: Longint): Longint; stdcall;
var
MS: TMemoryStream;
begin
MS := TMemoryStream(dwCookie);
MS.WriteBuffer(pbBuff^, CB);
Result := CB;
end;
function GetSelectedRTFCode(RichEdit: TRichedit): string;
var
MS: TMemoryStream;
EditStream: TEditStream;
SL: TStringList;
begin
MS := TMemoryStream.Create;
try
EditStream.dwCookie := DWORD_PTR(MS);
EditStream.dwError := 0;
EditStream.pfnCallback := RichEditCallBack;
Richedit.Perform(EM_StreamOut, SF_RTF or SFF_SELECTION, LPARAM(@EditStream));
MS.Seek(0, soBeginning);
SL := TStringList.Create;
try
SL.LoadFromStream(MS);
Result := SL.Text;
finally
SL.Free;
end;
finally
MS.Free;
end;
end;
Обратите внимание, в частности, что я не использовал оператор @
для получения адреса функции обратного вызова. Использование оператора @
для функции приводит к подавлению проверки типов. Если бы вы не использовали оператор @
, тогда компилятор мог бы сообщить вам свои ошибки.
Компилятор сказал бы:
[dcc32 Error] E2094 Local procedure/function 'RichEditCallBack' assigned to procedure variable
Также обратите внимание, что ваш код неправильно объявляет тип окончательного параметра. Это ссылочный параметр типа Longint
. Опять же, компилятор может сообщить об этом и сообщить об этом, если вы не использовали @
для получения адреса функции.
Эта вторая ошибка приводит к реализации обратного вызова. Это неверно. Возвращаемое значение указывает на успех. Для обозначения успеха используется значение нуля, любое другое значение указывает на сбой. Количество записанных байтов должно быть возвращено через окончательный параметр. Ваш ответ должен выглядеть следующим образом:
function RichEditCallBack(dwCookie: DWORD_PTR; pbBuff: PByte;
CB: Longint; var CBWritten: Longint): Longint; stdcall;
var
MS: TMemoryStream;
begin
MS := TMemoryStream(dwCookie);
CBWritten := MS.Write(pbBuff^, CB);
Result := IfThen(CB = CBWritten, 0, 1);
end;