В дополнение к тому, что сказал Дэвид Хеффернан, у вас есть большая проблема для решения. Обратные вызовы статуса назначаются на основе HINTERNET
, но вы рассматриваете их как один глобальный обратный вызов, который не будет работать. Вы должны отслеживать каждый обработчик HINTERNET
, который передается в InternetSetStatusCallback()
, чтобы затем вызывать его соответствующий обратный вызов изнутри вашего обратного вызова на основе указанного HINTERNET
.
Вы также должны уметь удалять HINTERNET
ручки из списка отслеживания, когда они закрыты. Вы можете использовать для этого статус INTERNET_STATUS_HANDLE_CLOSING
, однако в документации говорится, что он активирован только для дескрипторов HINTERNET
, которые имеют ненулевое значение Context
. Таким образом, вам придется зацепить InternetCloseHandle()
для учета HINTERNET
ручек с нулем Context
.
Попробуйте что-то больше, как это:
unit HttpMonitor;
interface
uses
Windows, WinInet, System.Generics.Collections;
type
// The WinInet unit maps INTERNET_STATUS_CALLBACK to a mere TFarProc, so
// let's spell out its parameters so we can actually make calls to it
// when needed...
INTERNET_STATUS_CALLBACK_TYPE = procedure(hInet: HINTERNET; dwContext: DWORD_PTR; dwInternetStatus: DWORD; lpvStatusInformation: LPVOID; dwStatusInformationLength: DWORD); stdcall;
THttpMonitor = class
private
FCallbacks: TDictionary<HINTERNET, INTERNET_STATUS_CALLBACK_TYPE>;
FInternetCloseHandle: function(hInet: HINTERNET): BOOL; stdcall;
FInternetSetStatusCallback: function(hInet: HINTERNET; lpfnInternetCallback: INTERNET_STATUS_CALLBACK_TYPE): INTERNET_STATUS_CALLBACK_TYPE; stdcall;
public
class function InternetCloseHandle(hInet: HINTERNET): BOOL; stdcall; static;
class function InternetSetStatusCallback(hInet: HINTERNET; lpfnInternetCallback: INTERNET_STATUS_CALLBACK_TYPE): INTERNET_STATUS_CALLBACK_TYPE; stdcall; static;
class procedure InternetStatusCallback(hInet: HINTERNET; dwContext: DWORD_PTR; dwInternetStatus: DWORD; lpvStatusInformation: LPVOID; dwStatusInformationLength: DWORD); stdcall; static; static;
constructor Create;
destructor Destroy; override;
end;
var
HttpMonitor: THttpMonitor = nil;
implementation
class function THttpMonitor.InternetCloseHandle(hInet: HINTERNET): BOOL; stdcall;
begin
HttpMonitor.FCallbacks.Remove(hInet);
Result := FInternetCloseHandle(hInet);
end;
class procedure THttpMonitor.InternetStatusCallback(hInet: HINTERNET; dwContext: DWORD_PTR; dwInternetStatus: DWORD; lpvStatusInformation: LPVOID; dwStatusInformationLength: DWORD); stdcall;
var
Callback: INTERNET_STATUS_CALLBACK_TYPE;
begin
//...
if HttpMonitor.FCallbacks.TryGetValue(hInet, Callback) then
begin
if Assigned(Callback) then
Callback(hInet, dwContext, dwInternetStatus, lpvStatusInformation, dwStatusInformationLength);
end;
end;
class function THttpMonitor.InternetSetStatusCallback(hInet: HINTERNET; lpfnInternetCallback: INTERNET_STATUS_CALLBACK_TYPE): INTERNET_STATUS_CALLBACK_TYPE; stdcall;
begin
HttpMonitor.FCallbacks.TryGetValue(hInet, Result);
HttpMonitor.FCallbacks.AddOrSetValue(hInet, lpfnInternetCallback);
FInternetSetStatusCallback(hInet, @THttpMonitor.InternetStatusCallback);
end;
constructor THttpMonitor.Create;
begin
inherited;
FCallbacks := TDictionary<HINTERNET, INTERNET_STATUS_CALLBACK_TYPE>.Create;
@FInternetCloseHandle := InterceptCreate('wininet.dll', 'InternetCloseHandle', @THttpMonitor.InternetCloseHandle);
@FInternetSetStatusCallback := InterceptCreate('wininet.dll', 'InternetSetStatusCallback', @THttpMonitor.InternetSetStatusCallback);
end;
destructor THttpMonitor.Destroy;
var
item: TPair<HINTERNET, INTERNET_STATUS_CALLBACK_TYPE>;
begin
if Assigned(FInternetSetStatusCallback) then
begin
for item in FCallbacks do
FInternetSetStatusCallback(item.Key, nil);
InterceptRemove(FInternetSetStatusCallback);
end;
if Assigned(FInternetCloseHandle) then
InterceptRemove(FInternetCloseHandle);
FCallbacks.Free;
inherited;
end;
end.
uses
..., HttpMonitor;
procedure TForm1.FormCreate(Sender: TObject);
begin
HttpMonitor := THttpMonitor.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
HttpMonitor.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Webrowser1.Navigate('www.stackoverflow.com');
end;
С учетом сказанного, есть одна последняя проблема, решить, и я не есть решение для этого - как назначить callback к дескриптору HINTERNET
, который никогда не передается InternetSetStatusCallback()
, так что вы видите его? InternetStatusCallback()
имеет статус INTERNET_STATUS_HANDLE_CREATED
, но в документации указывается, что он активируется только InternetConnect()
. Существуют и другие функции WinInet, которые создают дескрипторы HINTERNET
. Таким образом, вам могут потребоваться дополнительные крючки для учета всех дескрипторов HINTERNET
, на которые вы заинтересованы.
Гораздо лучше в глубине ответить. С другой стороны, я знаю об этом. –
Однако. 'InternetSetStatusCallback' вызывается только один раз? –
Если это вызов только один раз, то есть только 1 дескриптор 'HINTERNET', который' TWebBrowser' заинтересован в получении статуса, хотя может быть несколько дескрипторов HINTERNET, участвующих в операции HTTP. Почему вы подключаете 'InternetSetStatusCallback()' для начала? Какой статус вы ищете, что вы не можете получить из собственных событий TWebBrowser? –