使用方法的嵌套过程作为winapi回调是否安全?

yan*_*kee 11 delphi callback delphi-7

这是Delphi 7中的简化方案:

procedure TMyClass.InternalGetData;
var
  pRequest: HINTERNET;

    /// nested Callback 
    procedure HTTPOpenRequestCallback(hInet: HINTERNET; Context: PDWORD; Status: DWORD; pInformation: Pointer; InfoLength: DWORD); stdcall;
    begin
      // [...]  make something with pRequest
    end;

begin
  pRequest := HTTPOpenRequest(...);
  // [...]   
  if (InternetSetStatusCallback(pRequest, @HTTPOpenRequestCallback) = PFNInternetStatusCallback(INTERNET_INVALID_STATUS_CALLBACK)) then
    raise Exception.Create('InternetSetStatusCallback failed');
  // [...]
end;
Run Code Online (Sandbox Code Playgroud)

整件事似乎工作正常,但它是否真的正确和安全?我希望以这种方式封装它,因为它更具可读性和清洁性.我怀疑嵌套过程是否是一个简单的,正常的过程,因此它可以有自己的调用约定(stdcall)并安全地引用外部方法的局部变量(pRequest).

谢谢.

Dav*_*nan 13

在Windows的32位Delphi编译器中实现本地函数意味着这样的代码可以按照您的意图工作.前提是您没有引用封闭功能中的任何内容.您不能引用包括Self引用的局部变量.您的评论建议您希望引用pRequest一个局部变量.出于上述原因,您必须避免这样做.

但是,即使遵循这些规则,它也只能由于实现细节而起作用.在文档中明确指出它是非法的:

嵌套过程和函数(在其他例程中声明的例程)不能用作过程值.

如果您将代码带到不同的平台(例如64位Windows),那么它将失败.这里详细介绍了这个问题:为什么不能将地址转换为64位Delphi中的嵌套本地函数?

我的建议是你根本不以这种方式使用本地功能.这样做只会为自己设置陷阱,以便将来某个时候陷入困境.

我还建议对回调函数使用强类型声明,以便编译器可以检查您的回调是否具有正确的签名.这需要重新装修任何Win32 API函数,因为Embarcadero使用无类型指针的草率声明.你也想放弃使用@来获取函数指针,让编译器为你工作.


fan*_*cco 6

方法指针文件建议反对这种做法:

嵌套过程和函数(在其他例程中声明的例程)不能用作过程值,也不能用作预定义的过程和函数.

行为未定义.

  • 不,这就是文档的意思,@ Lieven.它使用术语*过程类型*来覆盖普通子例程,方法指针和方法引用的指针.值是类型的实例化,因此过程值是过程类型的实例化.这包括Yankee的案子. (3认同)