Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to use a method's nested procedure as a winapi callback?

This is the simplified scenario, in 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;

The whole thing seems to work fine, but is it really correct and safe? I'd like to have it encapsulated this way because it's more readable and clean. My doubt is whether the nested procedure is a simple, normal procedure or not, so that it can have its own calling convention (stdcall) and safely reference outer method's local variables (pRequest).

Thank you.

like image 861
yankee Avatar asked Sep 01 '16 07:09

yankee


2 Answers

The implementation of local functions in 32 bit Delphi compilers for Windows means that such code does work as you intend. Provided that you don't refer to anything from the enclosing function. You can't refer to local variables including the Self reference. Your comment suggests that you wish to refer to pRequest, a local variable. You'll have to refrain from doing so for the reasons described above.

However, even when following these rules, it only works due to an implementation detail. It is explicitly stated as illegal in the documentation:

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

If ever you take your code to a different platform such as 64 bit Windows then it will fail. This issue is covered in more detail here: Why cannot take address to a nested local function in 64 bit Delphi?

My advice is that you do not use local functions in this way at all. Doing so just sets traps for yourself that you will fall into sometime in the future.

I would also advise using strongly typed declarations for the callback functions so that the compiler can check your callback has the correct signature. That requires redecoration of any Win32 API function because of Embarcadero's sloppy declarations using untyped pointers. You'll also want to give up using @ to obtain function pointers and so let the compiler work for you.

like image 164
David Heffernan Avatar answered Sep 23 '22 10:09

David Heffernan


The Method Pointers documentation advises against this practice:

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

The behaviour is undefined.

like image 27
fantaghirocco Avatar answered Sep 23 '22 10:09

fantaghirocco