Возникла задача сохранить содержимое html-страницы, открытой в MS Explorer, в виде графического файла. Снять с экрана копию не проблема. Но размер html-страницы может превышать размер клиентской части окна. Соответственно, сохранять нужно поэтапно, скроллингуя клиентскую часть окна. Для этого с помощью WM_HTML_GETOBJECT получаю интерфейс IHTMLDocument2, а из него IHTMLWindow2, в котором есть метод scrollBy. Но как мне узнать размеры html-страницы для определения необходимого количества итераций.
Здравствуйте, Aniskin, Вы писали:
A>Возникла задача сохранить содержимое html-страницы, открытой в MS Explorer, в виде графического файла. Снять с экрана копию не проблема. Но размер html-страницы может превышать размер клиентской части окна. Соответственно, сохранять нужно поэтапно, скроллингуя клиентскую часть окна. Для этого с помощью WM_HTML_GETOBJECT получаю интерфейс IHTMLDocument2, а из него IHTMLWindow2, в котором есть метод scrollBy. Но как мне узнать размеры html-страницы для определения необходимого количества итераций.
Спасибо за совет. Провел эксперименты с WebBroser, получая IViewObject у WebBrowser1.Document – все работает прекрасно. Но в случае, когда я имею только хендл окна, не получается. Пожалуйста, укажите на ошибку.
procedure SaveExplorerCaptureFile(AWin: THandle; const AFileName: string);
var Doc: IHTMLDocument2;
Body: IHTMLElement;
Body2: IHTMLElement2;
BodyElement: IHTMLBodyElement;
W, H: Integer;
ViewObject: IViewObject;
DC: HDC;
R: TRect;
SaveBorderStyle: WideString;
SaveScroll: WideString;
B: TBitmap;
begin
OleCheck(GetIDocFromHWND(AWin, Doc));
Body := Doc.body;
if not Assigned(Body) then raise Exception.Create('Empty body');
OleCheck(Body.QueryInterface(IHTMLElement2, Body2));
W := Body2.scrollWidth;
H := Body2.scrollHeight;
if (W = 0) or (H = 0) then raise Exception.Create('Empty content');
OleCheck(Body.QueryInterface(IHTMLBodyElement, BodyElement));
// Там ли я беру IViewObject?
OleCheck(Doc.QueryInterface(IViewObject, ViewObject));
SaveBorderStyle := Body.style.borderStyle;
Body.style.borderStyle := 'none';
SaveScroll := BodyElement.scroll;
BodyElement.scroll := 'no';
// Еще не плохо было бы установить
// WebBrowser.Width := Body2.scrollWidth;
// WebBrowser.Height := Body2.scrollHeight;
// но где взять IWebBrowser?
// Или использовать SetWindowPos(AWin, 0, 0, 0, W, H, SWP_NOZORDER or SWP_NOMOVE or
// SWP_NOACTIVATE or SWP_NOCOPYBITS or SWP_NOREDRAW or SWP_NOSENDCHANGING or SWP_NOOWNERZORDER)
B := TBitmap.Create;
try
B.Width := W;
B.Height := H;
DC := GetDC(0);
try
R := Rect(0, 0, B.Width, B.Height);
OleCheck(ViewObject.Draw(DVASPECT_CONTENT, 1, nil, nil,
DC, B.Canvas.Handle, @R, nil, nil, 0));
B.SaveToFile(AFileName);
finally
ReleaseDC(0, DC);
end;
finally
B.Free;
Body.style.borderStyle := SaveBorderStyle;
BodyElement.scroll := SaveScroll;
end;
end
Здравствуйте, Aniskin, Вы писали:
A>Здравствуйте, Flamer, Вы писали:
F>>Юзать IViewObject::Draw.
A>Спасибо за совет. Провел эксперименты с WebBroser, получая IViewObject у WebBrowser1.Document – все работает прекрасно. Но в случае, когда я имею только хендл окна, не получается. Пожалуйста, укажите на ошибку.
Ошибка в невнимательном чтении MSDN:
Unlike most other interfaces, IViewObject cannot be marshaled to another process. This is because device contexts are only effective in the context of one process.
<< Если ты не являешься частью решения, то ты являешься частью проблемы. >>
Здравствуйте, Flamer, Вы писали:
F>Ошибка в невнимательном чтении MSDN:
F>
F>Unlike most other interfaces, IViewObject cannot be marshaled to another process. This is because device contexts are only effective in the context of one process.
То есть мне нужно внедрятся в чужой процесс? Как это лучше сделать? Глобальным хуком?
F>>Unlike most other interfaces, IViewObject cannot be marshaled to another process. This is because device contexts are only effective in the context of one process.
A>То есть мне нужно внедрятся в чужой процесс? Как это лучше сделать? Глобальным хуком?
Можно глобальным хуком, можно CreateRemoteTread, есть еще несколько способов. На сайте есть статьи по этой теме, плюс можно Рихтера почитать.
Еще мысль пришла: раз уж все равно IE юзается — написать BHO, потом куданить в шарную область или через named pipe слать команду: мол, нуна снять скрин для этого HWND. BHO смотрит, а не является ли переданный HWND дескриптором окна браузера, в котором ему вызвали SetSite, и если да, то делает черную работу и рапортует о результатах. Имхо самый простой в реализации метод, хотя и не без недостатков.
<< Самое главное — это деньги, а здоровье приходит и уходит. >>
F>pUnkSite — интерфейс, переданный в SetSite. Потом вызываем get_HWND у полученного интерфейса браузера.
Честно говоря, я не понимаю вышенаписнного. Я никогда ранее не работал с интерфейсами Explorer`а, и для меня это темный лес.
Все что у меня есть — это хендл окна и полученные IHTMLDocument2 и IHTMLWindow2. Мне нужно из этого как-нибуть получить IWebBroser. Прямого пути нет?
[]
A>Честно говоря, я не понимаю вышенаписнного. Я никогда ранее не работал с интерфейсами Explorer`а, и для меня это темный лес. A>Все что у меня есть — это хендл окна и полученные IHTMLDocument2 и IHTMLWindow2. Мне нужно из этого как-нибуть получить IWebBroser. Прямого пути нет?
1. Запрашиваете у IHTMLWindow2 интерфейс IServiceProvider c помощью QueryInterface
2. У полученного IServiceProvider запрашиваете интерфейс IWebBrowser2 с помощью QueryService.
Примерно так:
IServiceProvider* pService;
HRESULT hr = pWindow->QueryInterface(IID_IServiceProvider,(void**)&pService);
if(SUCCEEDED(hr))
{
IWebBrowser2* pBrowser;
hr = pService->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2,(void**)&pBrowser);
if(SUCCEEDED(hr))
{
// получили IWebBrowser2 в pBrowser
} // if
} // if
<< Деньги не приносят счастья, но вот то, что на них можно купить…. >>