Я пытаюсь вставить rtf внутри richedit (в частности, rtf, сохраненный в базе данных, а затем вставленный в текущий элемент управления richedit в точке выбора).
Код ниже работает в 32 битах. Но не смог заставить его работать в 64 битах, даже читая разные ответы в гугле на других языках.
Мне кажется, у меня где-то не тот размер некоторых типов.
Спасибо уже за помощь.
Версия Delphi 10.4 (но я не думаю, что это актуально)
type
TEditStreamCallBack = function(dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): DWORD;
stdcall;
TEditStream = record
dwCookie: Longint;
dwError: Longint;
pfnCallback: TEditStreamCallBack;
end;
TpRichEditUtils = class
public
class procedure RTFInsertStream(const ARichEdit : TRichEdit; const AStream: TStream);
end;
implementation
uses
Winapi.RichEdit, SysUtils;
function EditStreamReader(dwCookie: DWORD; pBuff: Pointer; cb: LongInt; pcb: PLongInt): DWORD; stdcall;
begin
Result := $0000; // assume no error
try
pcb^ := TStream(dwCookie).Read(pBuff^, cb); // read data from stream
except
Result := $FFFF; // indicates error to calling routine
end;
end;
class procedure TpRichEditUtils.RTFInsertStream(const ARichEdit : TRichEdit; const AStream: TStream);
var
EditStream: TEditStream; // callback used to read inserted RTF
begin
ARichEdit.Lines.BeginUpdate;
try
// Make sure rich edit is large enough to take inserted code
ARichEdit.MaxLength := ARichEdit.MaxLength + AStream.Size;
// Stream in the RTF via EM_STREAMIN message
EditStream.dwCookie := DWORD(AStream);
EditStream.dwError := $0000;
EditStream.pfnCallback := @EditStreamReader;
ARichEdit.Perform(
EM_STREAMIN,
SFF_SELECTION or SF_RTF or SFF_PLAINRTF, LPARAM(@EditStream)
);
// Report any errors as a bug
if EditStream.dwError <> $0000 then
raise Exception.Create('RTFInsertStream: Error inserting stream');
finally
ARichEdit.Lines.EndUpdate;
end;
end;
OStream := TStringStream.Create(ODmRemarqueStandard.REMSTD_TEXTE);
try
TpRichEditUtils.RTFInsertStream(TcxRichInnerEdit(EdtConditionGenerale.InnerControl), OStream);
finally
FreeAndNil(OStream)
end;
Я попытался изменить dwCookie: Longint на DWORD_PTR. и пробовал другие вещи, но всегда нарушение доступа.
Правильный тип возврата для обратного вызова — DWORD
, а не Longint
. И действительно, dwCookie
должно быть DWORD_PTR
, а не longint
(а dwError
должно быть DWORD
). Одна вещь, которой не хватает в ручном объявлении TEditStreamCalback
, это stdcall
в 32-битной версии.
Вы должны посмотреть исходный код для TRichEdit
в Vcl.ComCtrls.pas
, чтобы увидеть, как он реализует EM_STREAMIN
.
@SilverWarior Я не знал, что они уже были в исходном коде Delphi -_-;
@Remy Спасибо за решение
@SilverWarior Спасибо за помощь.
@MichaelDesboeufs Честно говоря, я тоже не знал об этом до вчерашнего дня, так как сам никогда не пользовался Winapi.RichEdit
устройством. По крайней мере, не добавляя его в раздел использования самостоятельно. Черт возьми, я даже не знал, что он существует, пока не увидел ссылку на него в вашем коде. Но как только я посеял это в вашем коде, я открыл свой Delphi и пошел проверить исходный код модуля, чтобы увидеть, как это реализовано. Именно тогда я заметил несоответствие между вашей декларацией и той, что в блоке Winapi.RichEdit
. ...
... Так что, если вы не используете версию Delphi Community Edition, у вас также должен быть доступ к исходному коду большинства модулей, поставляемых с Delphi. Вы можете многому научиться, проверив их исходный код.
@SilverWarior Конечно, хорошо учиться у других. Я читаю их код время от времени. Иногда не так просто читать и интерпретировать код других людей. От моего незнания или неправильной интерпретации того, что он должен делать.
Ваши объявления TEditStreamCallBack
и EditStreamReader
не совпадают.
Кроме того, вы обрабатываете файл cookie как 32-битное целое число как в 32-битной, так и в 64-битной сборке, что неверно. Это должно быть 32-битное целое число в 32-битной сборке и 64-битное целое число в 64-битной сборке. API Win32 объявляет файл cookie как DWORD_PTR
именно для этой цели.
TEditStreamCallBack
и TEditStream
уже объявлены в модуле Winapi.RichEdit
, вам не нужно объявлять их вручную в собственном коде.
Попробуйте это вместо этого:
uses
Winapi.RichEdit, SysUtils;
function EditStreamReader(dwCookie: DWORD_PTR; pbBuff: PByte; cb: Longint; var pcb: Longint): Longint; stdcall;
begin
Result := 0; // assume no error
try
pcb := TStream(dwCookie).Read(pBuff^, cb); // read data from stream
except
Result := -1; // indicates error to calling routine
end;
end;
class procedure TpRichEditUtils.RTFInsertStream(const ARichEdit : TRichEdit; const AStream: TStream);
var
EditStream: TEditStream; // callback used to read inserted RTF
begin
ARichEdit.Lines.BeginUpdate;
try
// Make sure rich edit is large enough to take inserted code
ARichEdit.MaxLength := ARichEdit.MaxLength + AStream.Size;
// Stream in the RTF via EM_STREAMIN message
EditStream.dwCookie := DWORD_PTR(AStream);
EditStream.dwError := 0;
EditStream.pfnCallback := @EditStreamReader;
ARichEdit.Perform(
EM_STREAMIN,
SFF_SELECTION or SF_RTF or SFF_PLAINRTF,
LPARAM(@EditStream)
);
// Report any errors as a bug
if EditStream.dwError <> 0 then
raise Exception.Create('RTFInsertStream: Error inserting stream');
finally
ARichEdit.Lines.EndUpdate;
end;
end;
Большое спасибо. Не видел, чтобы типы уже были определены в WinApi.RichEdit. Это решило мою проблему. Еще раз спасибо за отличный ответ.
Определения вашей и
TEditStreamCalback
функции иTEditStream
записи отличаются от тех, которые уже объявлены вWinapi.RichEdit
модуле. ТамTEditStreamCalback
тип результата определяется какLongint
, но вы объявляете его какDWORD
, а полеTEditStream
dwCookie
определяется какDWORD_PTR
, а вы определяете его какLongint
. В любом случае, почему вы даже переопределяете эти типы, если они уже определены вWinapi.RichEdit
, который вы включаете в раздел использования реализации?