Об альтернативе Application.ProcessMessages для TWebBrowser и разрыве стека выполнения
Использование Application.ProcessMessages может вызывать неожиданные проблемы. Особенно это актуально, когда используются оконные windows сообщения для разрыва стека выполнения. Но нашелся способ, которое позволяет не выбирать все сообщения из очереди. На первый взгляд, решение даже работает, однако буду рекомендовать никогда его не использовать и всегда использовать Application.ProcessMessage и об этом ниже.
Очевидно, что WebBrowser обрабатывает какие-то сообщения. Но поиск их казался гиблой идеей. В действительности оказалось, что ни так все плохо. С помощью Window Detective было обнаружено, что в момент загрузки происходит подозрительная посылка сообщений окну с именем класса ‘Internet Explorer_Hidden’. Решил проверить и выбрал из очереди оконных сообщений в момент загрузки документа сообщения предназначенные только этому окну. К моему удивлению — все заработало.
Сообщения получаемые окном IE |
TMyWebBrowser = class(TWebBrowser)
protected
procedure WBProcessMessage;
procedure InternalSetValue(const AValue: string);
public
procedure SetValue(const AValue: string); // Входная точка в примера
procedure WaitWB;
end;
function EnumWindowsToFindIEHiddenProc(AHandle: HWND; AParam:NativeInt): boolean; stdcall;
var
IEHiddenHandle: Hwnd;
implementation
procedure TMyWebBrowser.SetValue(const AValue: string);
begin
InternalSetValue(AValue); // выполняем каким либо способом присваивание разметки
WaitWB; // Ждем завершение загрузки документа.
FooFunction; //Какой то функционал для работы которого необходим полностью загруженные html документ.
end;
procedure TMyWebBrowser.WaitWB;
begin
//Как то так обычно выглядит ожидание пока документ полностью не загрузиться
while HTMLDocument2.readyState <> 'complete' do
begin
WBProcessMessage; // выбираем только нужные сообщения
//Forms.Application.ProcessMessages; // выбираем все сообщения из очереди
end;
end;
procedure TbtkHTMLEditor.WBProcessMessage;
var
msg: Windows.tagMSG;
processID : THandle;
begin
IEHiddenHandle := 0;
processID := GetCurrentProcessId;
if EnumWindows(@EnumWindowsToFindIEHiddenProc, processID) then // �щем хендл окна IE в нашем процессе перебирая все окна
if IEHiddenHandle <> 0 then // Проверяем найденный хендл валидный
if PeekMessage(msg, IEHiddenHandle, 0, 0, PM_REMOVE) then // извлекаем из очереди оконных сообщений все сообщения для окна IEHiddenHandle
begin
Windows.DispatchMessage(msg); // Передаем извлеченные сообщения окну IE
end;
end;
function EnumWindowsToFindIEHiddenProc(AHandle: HWND; AParam:NativeInt): boolean;
var
processId: NativeInt;
classbuf: array[0..255] of Char;
const
IEWndClassName = 'Internet Explorer_Hidden';
begin
result := true;
if Windows.GetWindowThreadProcessId(AHandle,@processId) <> 0 then
begin
if AParam = processId then
begin
GetClassName(AHandle, classbuf, SizeOf(classbuf));
if lstrcmp(@classbuf[0], @IEWndClassName[1]) = 0 then
begin
IEHiddenHandle := AHandle;
result := false;
end;
end;
end;
end;