使用方法的嵌套过程作为 winapi 回调是否安全?
Is it safe to use a method's nested procedure as a winapi callback?
这是简化的场景,在 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;
整个过程似乎工作正常,但它真的正确且安全吗?
我希望以这种方式封装它,因为它更具可读性和清洁性。我怀疑嵌套过程是否是一个简单、正常的过程,以便它可以有自己的调用约定 (stdcall
) 并安全地引用外部方法的局部变量 (pRequest
).
谢谢。
Method Pointers 文档反对这种做法:
Nested procedures and functions (routines declared within other routines) cannot be used as procedural values, nor can predefined procedures and functions.
行为未定义。
Windows 的 32 位 Delphi 编译器中的局部函数实现意味着此类代码确实可以按您的预期工作。前提是您没有引用封闭函数中的任何内容。您不能引用局部变量,包括 Self
引用。您的评论表明您希望引用局部变量 pRequest
。由于上述原因,您必须避免这样做。
然而,即使遵循这些规则,它也只是由于实现细节而起作用。在documentation:
中明确声明为非法
Nested procedures and functions (routines declared within other routines) cannot be used as procedural values.
如果您将代码带到不同的平台,例如 64 位 Windows,那么它将失败。此问题在此处有更详细的介绍:Why cannot take address to a nested local function in 64 bit Delphi?
我的建议是根本不要以这种方式使用局部函数。这样做只会给自己设下陷阱,以后有的时候会掉进去。
我还建议对回调函数使用强类型声明,以便编译器可以检查您的回调是否具有正确的签名。由于 Embarcadero 使用无类型指针的草率声明,这需要重新修饰任何 Win32 API 函数。您还想放弃使用 @ 获取函数指针,让编译器为您工作。
这是简化的场景,在 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;
整个过程似乎工作正常,但它真的正确且安全吗?
我希望以这种方式封装它,因为它更具可读性和清洁性。我怀疑嵌套过程是否是一个简单、正常的过程,以便它可以有自己的调用约定 (stdcall
) 并安全地引用外部方法的局部变量 (pRequest
).
谢谢。
Method Pointers 文档反对这种做法:
Nested procedures and functions (routines declared within other routines) cannot be used as procedural values, nor can predefined procedures and functions.
行为未定义。
Windows 的 32 位 Delphi 编译器中的局部函数实现意味着此类代码确实可以按您的预期工作。前提是您没有引用封闭函数中的任何内容。您不能引用局部变量,包括 Self
引用。您的评论表明您希望引用局部变量 pRequest
。由于上述原因,您必须避免这样做。
然而,即使遵循这些规则,它也只是由于实现细节而起作用。在documentation:
中明确声明为非法Nested procedures and functions (routines declared within other routines) cannot be used as procedural values.
如果您将代码带到不同的平台,例如 64 位 Windows,那么它将失败。此问题在此处有更详细的介绍:Why cannot take address to a nested local function in 64 bit Delphi?
我的建议是根本不要以这种方式使用局部函数。这样做只会给自己设下陷阱,以后有的时候会掉进去。
我还建议对回调函数使用强类型声明,以便编译器可以检查您的回调是否具有正确的签名。由于 Embarcadero 使用无类型指针的草率声明,这需要重新修饰任何 Win32 API 函数。您还想放弃使用 @ 获取函数指针,让编译器为您工作。