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函数内部的Priviliged指令和访问冲突错误失败。
所以考虑到这一点,唯一的变化是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^,soBeginning); SL := TStringList.Create; try SL.LoadFromStream(MS); Result := SL.Text; finally SL.Free; end; finally MS.Free; end; end;
为什么要全局声明内存流变量,但在本地声明时失败?
解决方法
然而,一旦嵌套函数引用任何这样的局部变量,则必须传递一个额外的隐藏参数,以便嵌套函数可以访问周围的函数栈帧。而对于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,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,1); end;