Можно ли использовать метод вложенной процедуры как обратный вызов winapi?

Это упрощенный сценарий в 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).

Спасибо.

Ответ 1

Реализация локальных функций в 32-битных компиляторах Delphi для Windows означает, что такой код работает так, как вы планируете. При условии, что вы не ссылаетесь ни на что из функции включения. Вы не можете ссылаться на локальные переменные, включая ссылку Self. Ваш комментарий предполагает, что вы хотите обратиться к pRequest, локальной переменной. Вам придется воздержаться от этого по причинам, описанным выше.

Однако даже при соблюдении этих правил он работает только из-за детализации реализации. Он явно указан как незаконный в документации:

Вложенные процедуры и функции (процедуры, объявленные в других подпрограммах) не могут использоваться в качестве процедурных значений.

Если вы когда-нибудь возьмете свой код на другой платформе, такой как 64-битная Windows, тогда он потерпит неудачу. Эта проблема более подробно рассматривается здесь: Почему не удается получить адрес для вложенной локальной функции в 64-битном Delphi?

Мой совет заключается в том, что вы вообще не используете локальные функции. Выполнение этого просто устанавливает ловушки для себя, что вы попадете в какое-то время в будущем.

Я бы также посоветовал использовать строго типизированные объявления для функций обратного вызова, чтобы компилятор мог проверить, что ваш обратный вызов имеет правильную подпись. Это требует исправления любой функции Win32 API из-за неактивных объявлений Embarcadero с использованием нетипизированных указателей. Вы также захотите отказаться от использования @, чтобы получить указатели на функции, и пусть компилятор работает на вас.

Ответ 2

Документация Указатели метода указывает на эту практику:

Вложенные процедуры и функции (процедуры, объявленные в других подпрограммах) не могут использоваться как процедурные значения, а также не могут предопределяться процедуры и функции.

Поведение undefined.