gpt4 book ai didi

delphi - 为什么此代码在本地声明 TMemoryStream 时失败,但在全局声明时有效?

转载 作者:行者123 更新时间:2023-12-03 14:35:57 26 4
gpt4 key购买 nike

以下函数采用 Richedit 中的选定文本控制,写入 TMemoryStream在回调函数内,然后以纯文本字符串形式返回原始 rtf 代码。

var
MS: TMemoryStream; // declared globally and works.

implementation

function GetSelectedRTFCode(RichEdit: TRichedit): string;

function RichEditCallBack(dwCookie: Longint; pbBuff: PByte;
CB: Longint; var pCB: Pointer): Longint; stdcall;
begin
MS.WriteBuffer(pbBuff^, CB);
Result := CB;
end;

var
EditStream: TEditStream;
SL: TStringList;
begin
MS := TMemoryStream.Create;
try
EditStream.dwCookie := SF_RTF or SFF_SELECTION;
EditStream.dwError := 0;
EditStream.pfnCallback := @RichEditCallBack;
Richedit.Perform(EM_StreamOut, SF_RTF or SFF_SELECTION, DWord(@EditStream));
MS.Seek(0, soBeginning);

SL := TStringList.Create;
try
SL.LoadFromStream(MS);
Result := SL.Text;
finally
SL.Free;
end;
finally
MS.Free;
end;
end;

以上内容按预期工作,没有任何错误。

但是,我尝试尽可能避免全局声明变量,并将它们保留在需要它的过程或函数的本地,但由于某种原因声明 MS: TMemoryStream;里面GetSelectedRTFCode函数因特权指令和访问冲突错误而失败。

考虑到这一点,下面唯一的变化是 MS: TMemoryStream;本地声明失败:

function GetSelectedRTFCode(RichEdit: TRichedit): string;
var
MS: TMemoryStream; // declare here instead of globally but fails.

function RichEditCallBack(dwCookie: Longint; pbBuff: PByte;
CB: Longint; var pCB: Pointer): Longint; stdcall;
begin
MS.WriteBuffer(pbBuff^, CB);
Result := CB;
end;

var
EditStream: TEditStream;
SL: TStringList;
begin
MS := TMemoryStream.Create;
try
EditStream.dwCookie := SF_RTF or SFF_SELECTION;
EditStream.dwError := 0;
EditStream.pfnCallback := @RichEditCallBack;
Richedit.Perform(EM_StreamOut, SF_RTF or SFF_SELECTION, DWord(@EditStream));
MS.Seek(0, soBeginning);

SL := TStringList.Create;
try
SL.LoadFromStream(MS);
Result := SL.Text;
finally
SL.Free;
end;
finally
MS.Free;
end;
end;

为什么全局声明内存流变量可以工作,但本地声明却失败?

最佳答案

问题在于您使用嵌套函数作为回调是错误的。只要嵌套函数不引用周围函数的任何局部变量,以这种方式使用嵌套函数实现的机会就适用于 32 位编译器。

但是,一旦嵌套函数引用任何此类局部变量,则必须传递额外的隐藏参数,以便嵌套函数可以访问周围的函数堆栈帧。对于 64 位编译器,总是会传递一个隐藏的额外参数。

您会在网络上找到很多示例,其中人们演示了如何将嵌套函数作为回调传递。但所有这些例子都打破了 documented语言规则:

Nested procedures and functions (routines declared within other routines) cannot be used as procedural values, nor can predefined procedures and functions.

您必须做的是停止使用嵌套函数进行回调。您需要将回调函数声明为具有全局范围。通过 EDITSTREAM 结构体的 dwCookie 成员传递内存流。

// 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

And note also that your code declares the type of the final parameter incorrectly. It's a reference parameter of type Longint. Again, the compiler can report this, and does report this, unless you have used @ to obtain the function address.

This second error leads on to the implementation of the callback. It is incorrect. The return value indicates success. A value of zero is used to indicate success, any other value indicates failure. The number of bytes written must be returned via the final parameter. Your callback should look like this:

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;

关于delphi - 为什么此代码在本地声明 TMemoryStream 时失败,但在全局声明时有效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29098939/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com