Попытка отобразить элемент управления веб-браузера с помощью IViewObject :: Draw () в HDC не удалась с IE8, но успешно с IE11

У меня есть диалоговое окно MFC, где я добавил Элемент управления WebBrowser (который заключает в капсулу Internet Explorer двигатель.)

Целью следующего кода является визуализация содержимого указанного web browser control в device context что я могу позже использовать для печати:

//MFC code, error checks are omitted for brevity

//'m_browser' = is a web browser control of type `CExplorer1`
IDispatch* pHtmlDoc = m_browser.get_Document();

CComPtr<IHTMLDocument2> pHtmlDocument2;
pHtmlDoc->QueryInterface(IID_IHTMLDocument2, (void**)&pHtmlDocument2));

//Get IViewObject2 for the entire document that we will use to render into a DC
CComPtr<IViewObject2> pViewObject;
pHtmlDocument2->QueryInterface(IID_IViewObject2, (void **)&pViewObject));

CComPtr<IHTMLElement> pBody;
pHtmlDocument2->get_body(&pBody));

CComPtr<IHTMLElement2> pBody2;
pBody->QueryInterface(IID_IHTMLElement2, (void **)&pBody2));//Get default printer DC
CPrintDialog pd(TRUE, PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_HIDEPRINTTOFILE | PD_NOSELECTION);
pd.m_pd.Flags |= PD_RETURNDC | PD_RETURNDEFAULT;
pd.DoModal();         //corrected later
HDC hPrintDC = pd.CreatePrinterDC();

//Calc bitmap width based on printer DC specs
//Note that this width will be larger than
//the width of the WebControl window itself due to
//printer's much higher DPI setting...
int n_bitmapWidth = ::GetDeviceCaps(hPrintDC, HORZRES);     //Use entire printable area//Get full size of the document
long n_scrollWidth;
long n_scrollHeight;
pBody2->get_scrollWidth(&n_scrollWidth);
pBody2->get_scrollHeight(&n_scrollHeight);

//Calc proportional size of the bitmap in the DC to render
int nWidth = n_bitmapWidth;
int nHeight = n_bitmapWidth * n_scrollHeight / n_scrollWidth;

//Create memory DC to render into
HDC hDc = ::GetDC(hWnd);
HDC hCompDc = ::CreateCompatibleDC(hDC);

//I'm using a raw DIB section here as I'll need to access
//its bitmap bits directly later in my code...
BITMAPINFOHEADER infoHeader = {0};

infoHeader.biSize          = sizeof(infoHeader);
infoHeader.biWidth         = nWidth;
infoHeader.biHeight        = -nHeight;
infoHeader.biPlanes        = 1;
infoHeader.biBitCount      = 24;
infoHeader.biCompression   = BI_RGB;

BITMAPINFO info;
info.bmiHeader = infoHeader;

//Create a bitmap as DIB section of size `nWidth` by `nHeight` pixels
BYTE* pMemory = 0;
HBITMAP hBitmap = ::CreateDIBSection(hDc, &info, DIB_RGB_COLORS, (void**)&pMemory, 0, 0);

HBITMAP hOldBmp = (HBITMAP)::SelectObject(hCompDc, hBitmap);

RECT rcAll = {0, 0, nWidth, nHeight};
::FillRect(hCompDc, &rcAll, (HBRUSH)::GetStockObject(WHITE_BRUSH));

RECTL rectPrnt = {0, 0, nWidth, nHeight};

//Do the upscaling & render -- note that IE8 fails to render it here!!!!
pViewObject->Draw(DVASPECT_CONTENT, //DVASPECT_DOCPRINT
-1, NULL, NULL, NULL, hCompDc,
&rectPrnt,
NULL,
NULL, 0));

::SelectObject(hCompDc, hOldBmp);//Now the bitmap in `hCompDc` contains the resulting pixels
//For debugging purposes, save it as .bmp file

BITMAPFILEHEADER fileHeader = {0};
fileHeader.bfType      = 0x4d42;
fileHeader.bfSize      = 0;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

CFile file(
L"path-to\\test.bmp",
CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyNone);
file.Write((char*)&fileHeader, sizeof(fileHeader));
file.Write((char*)&infoHeader, sizeof(infoHeader));

int bytes = (((24 * nWidth + 31) & (~31)) / 8) * nHeight;
file.Write(pMemory, bytes);//Clean up
::DeleteObject(hBitmap);
::DeleteDC(hCompDc);

::ReleaseDC(hWnd, hDc);

::DeleteDC(hPrintDC);

Этот код работает нормально, если у меня последняя IE11 установлен на моей машине разработки. Но если, например, кто-то IE8 установлен на их Windows 7, IViewObject :: Draw Метод будет отображать только небольшую часть документа (равную размеру самого элемента управления веб-браузера).

Лучший способ описать это — проиллюстрировать это примерами:

Обычно отображается тестовая страница с IE11 установлены:

введите описание изображения здесь

и вот что происходит с IE8 установлены:

введите описание изображения здесь

Кто-нибудь знает, что я делаю здесь неправильно IE8 не нравится?

EDIT1: Еще немного покопался в IViewObject::Draw функция с WinDbg а затем нашел исходный код для него. Вот CServer :: Draw () то есть IViewObject::Draw, а потом CDoc :: Draw () это называется внутренне из CServer::Draw(),

5

Решение

Во-первых, спасибо за интересный вопрос. Хотя это не так практично — не многие люди используют IE8 сегодня — это было не так тривиально решить. Я опишу в чем проблема и предоставлю упрощенное, но работающее решение, которое вы можете улучшить.


Прежде чем перейти к решению IE8, пара моментов:

  1. Решение с изменением размера окна для соответствия размеру прокрутки не является стабильным, если вы можете встретить большие документы. Если вы не знаете, чего ожидать, вам также необходимо провести рефакторинг решения для последующих исследователей, чтобы не полагаться на изменение размера окна для прокрутки по размеру.

  2. Зачем нести гигантский растровое изображение? Метафайлы и т. Д. Могут подойти лучше. Любая достаточно большая страница с таким разрешением унесет память на ПК с наивным созданием DIB. Страница Google в предоставленном образце отображается в растровом файле размером 100 МБ, тогда как emf, из которого выполняется растеризация, занимает менее 1 МБ.

  3. Хотя я не знаю точных требований и ограничений вашего проекта, я на 99% уверен, что использование гигантского DIB — не лучшее решение. Даже ЭДС, хотя и лучше, тоже не самая лучшая. Если вам нужно, например, добавить подпись, а затем распечатать, есть лучшие способы справиться с этим. Это, конечно, просто дополнительная заметка, не связанная с самим вопросом.


IE8 проблема рендеринга

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

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

Если он не отсекает кого-либо на подлинном IE8, то в системе есть остатки более позднего IE, или есть другие настройки без нуля, обновление системы или тому подобное.

Возможности обхода

Хорошие новости можно обойти, плохие новости обходятся немного неприятно.

Во-первых, это все еще возможно обойти с IViewObject. Но поскольку здесь задействовано произвольное масштабирование, а доступный исходный прямоугольник очень мал, у этого решения есть некоторые сложности, которые, я думаю, находятся за пределами SO-ответа. Поэтому я бы не стал погружаться в этот путь.

Вместо этого мы можем рендерить через другой, теперь устаревший API: IHTMLElementRender. Это позволяет визуализировать страницу с DrawToDC в произвольном контексте. К сожалению, это не так просто, как может показаться, и выходит за рамки простого предоставления контекста устройства.

Во-первых, есть похожая ошибка отсечения. Это может быть легче обработано, потому что отсечение происходит при больших значениях за пределами размеров экрана. Во-вторых, при использовании преобразований контекста устройства оно либо не будет работать, либо испортит визуализированный html, поэтому вы не можете полагаться на масштабирование или перевод. Обе проблемы требуют относительно нетривиального решения и усложняют друг друга.

Решение

Я опишу и предоставлю пример кода для неоптимального, но работающего над решением самых простых страниц. В целом, можно достичь идеального и более эффективного решения, но, опять же, это выходит за рамки ответа. Очевидно, что это только IE8, поэтому вам нужно проверить версию браузера и выполнить разные обработчики для IE8 по сравнению с IE9 или выше, но вы можете воспользоваться некоторыми идеями для улучшения рендеринга в других браузерах.

Здесь есть два взаимосвязанных обходных пути.

Up-масштабирование

Во-первых, как мы можем масштабировать векторный контент до качества принтера, если мы не можем преобразовать? Обходной путь здесь заключается в отображении в контекст, совместимый с принтером постоянного тока. Что произойдет, так это то, что контент будет отображаться в DPI принтера. Обратите внимание, что он не будет точно соответствовать ширине принтера, он будет масштабироваться до printerDPI / screenDPI.

Позже, при растеризации, мы уменьшаем масштаб, чтобы соответствовать ширине принтера. Первоначально мы выполняем рендеринг в EMF, поэтому потери качества практически отсутствуют (в любом случае это произойдет на самом принтере). Если вам нужно более высокое качество (я сомневаюсь в этом), есть две возможности — изменить решение для рендеринга для целевой ширины (это не тривиально) или поработать с результирующей эдс вместо растрового изображения и позволить принтеру подогнать масштаб вниз.

Еще одно замечание: в настоящее время вы используете только ширину принтера, но могут быть непечатаемые поля, которые вам нужно запросить у принтера и учесть их. Таким образом, он может быть масштабирован принтером, даже если вы предоставите точный рисунок с точным размером принтера. Но опять же, я сомневаюсь, что это разрешение несоответствия будет иметь значение для вашего проекта.

вырезка

Второе, что нужно преодолеть — это отсечение. Чтобы преодолеть это ограничение, мы визуализируем содержимое небольшими порциями, чтобы они не обрезались средством визуализации. После рендеринга фрагмента мы меняем позицию прокрутки документа и отображаем следующий фрагмент в соответствующую позицию в целевом DC. Это может быть оптимизировано для использования больших кусков, например Ближайший DPI кратен 1024 (с использованием изменения размера окна), но я этого не реализовал (это просто оптимизация скорости). Если вы не выполняете эту оптимизацию, убедитесь, что минимальный размер окна браузера не слишком мал.

Обратите внимание, что выполнение этой прокрутки в произвольном дробном масштабе будет приближенным и не так просто реализовать в общем случае. Но с обычным принтером и экраном мы можем сделать целочисленные чанки с умножением DPI, например если экран имеет разрешение 96 DPI, а принтер — 600 точек на дюйм, мы делаем шаги с одинаковым кратным 96 и 600 для каждого контекста, и все намного проще. Тем не менее, остаток сверху или снизу после обработки всех целых кусков не будет в множителях DPI, поэтому мы не можем прокрутить там так легко.

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

Обратите внимание, что это может помешать некоторым страницам и изменить макет (вероятно, не в случае простых отчетов). Если это проблема, вам нужно добавить обработку остатков после циклов вместо добавления элемента. Наиболее простым решением в этом случае по-прежнему является заполнение с помощью div, но не с полным размером фрагмента, а просто для увеличения ширины содержимого по отношению к экранному DPI.

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

Код

Это всего лишь образец.

  • Нет обработки ошибок. Вы должны добавить проверки для каждого вызова COM и API и т. Д.

  • Нет стиля кода, просто быстро и грязно.

  • Не уверен, что все полученные ресурсы высвобождаются по мере необходимости, проверьте

  • Вы должен отключите границы страницы в элементе управления браузером, чтобы этот пример работал (если вам нужны границы вокруг браузера, просто визуализируйте их отдельно, встроенные в любом случае не согласованы). На IE8 это не так тривиально, но есть много ответов здесь или в Интернете. В любом случае, я включу этот патч в пример проекта решения. Вы также можете выполнять рендеринг с границами и исключать их, но это будет ненужным осложнением для проблемы, которая имеет простое решение.

  • Полный проект решения можно найти на эта ссылка, Я выложу только соответствующий код здесь.

Код ниже отображает страницу и сохраняет в c: \ temp \ test.emf + c: \ temp \ test.bmp

void convertEmfToBitmap(const RECT& fitRect, HDC hTargetDC, HENHMETAFILE hMetafile, LPCTSTR fileName);
CComPtr<IHTMLDOMNode> appendPadElement(IHTMLDocument2* pDoc, IHTMLElement* pBody, long left, long top, long width, long height);
void removeElement(IHTMLElement* pParent, IHTMLDOMNode* pChild);

void CMFCApplication1Dlg::OnBnClickedButton2()
{
COleVariant varNull;
COleVariant varUrl = L"http://www.google.com/search?q=ie+8+must+die";
m_browser.Navigate2(varUrl, varNull, varNull, varNull, varNull);
}void CMFCApplication1Dlg::OnBnClickedButton1()
{
//get html interfaces
IDispatch* pHtmlDoc = m_browser.get_Document();
CComPtr<IHTMLDocument2> pHtmlDocument2;
pHtmlDoc->QueryInterface(IID_IHTMLDocument2, (void**)&pHtmlDocument2);

CComPtr<IHTMLElement> pBody;
pHtmlDocument2->get_body(&pBody);

CComPtr<IHTMLElement2> pBody2;
pBody->QueryInterface(IID_IHTMLElement2, (void**)&pBody2);

CComPtr<IHTMLBodyElement> pBodyElement;
pBody->QueryInterface(IID_IHTMLBodyElement, (void**)&pBodyElement);

CComPtr<IHTMLElement> pHtml;
pBody->get_parentElement(&pHtml);

CComPtr<IHTMLElement2> pHtml2;
pHtml->QueryInterface(IID_IHTMLElement2, (void**)&pHtml2);

CComPtr<IHTMLStyle> pHtmlStyle;
pHtml->get_style(&pHtmlStyle);
CComPtr<IHTMLStyle> pBodyStyle;
pBody->get_style(&pBodyStyle);

//get screen info
HDC hWndDc = ::GetDC(m_hWnd);
const int wndLogPx = GetDeviceCaps(hWndDc, LOGPIXELSX);
const int wndLogPy = GetDeviceCaps(hWndDc, LOGPIXELSY);//keep current values
SIZE keptBrowserSize = { m_browser.get_Width(), m_browser.get_Height() };
SIZE keptScrollPos;
//set reasonable viewport size
//m_browser.put_Width(docSize.cx);
//m_browser.put_Height(docSize.cy*2);
pHtml2->get_scrollLeft(&keptScrollPos.cx);
pHtml2->get_scrollTop(&keptScrollPos.cy);
COleVariant keptOverflow;
pBodyStyle->get_overflow(&keptOverflow.bstrVal);

//setup style and hide scroll bars
pHtmlStyle->put_border(L"0px;");
pHtmlStyle->put_overflow(L"hidden");
pBodyStyle->put_border(L"0px;");
pBodyStyle->put_overflow(L"hidden");

//get document size and visible area in screen pixels
SIZE docSize;
pBody2->get_scrollWidth(&docSize.cx);
pBody2->get_scrollHeight(&docSize.cy);
RECT clientRect = { 0 };
pHtml2->get_clientWidth(&clientRect.right);
pHtml2->get_clientHeight(&clientRect.bottom);

//derive chunk size
const SIZE clientChunkSize = {
clientRect.right - clientRect.right % wndLogPx,
clientRect.bottom - clientRect.bottom % wndLogPy };

//pad with absolutely positioned element to have enough scroll area for all chunks
//alternatively, browser can be resized to chunk multiplies (simplest), to DPI multiplies (more work).
//This pad also can be made smaller, to modulus DPI, but then need more work in the loops below
CComPtr<IHTMLDOMNode> pPadNode =
appendPadElement(pHtmlDocument2, pBody, docSize.cx, docSize.cy, clientChunkSize.cx, clientChunkSize.cy);

//get printer info
CPrintDialog pd(TRUE, PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_HIDEPRINTTOFILE | PD_NOSELECTION);
pd.m_pd.Flags |= PD_RETURNDC | PD_RETURNDEFAULT;
pd.DoModal();
HDC hPrintDC = pd.CreatePrinterDC();
const int printLogPx = GetDeviceCaps(hPrintDC, LOGPIXELSX);
const int printLogPy = GetDeviceCaps(hPrintDC, LOGPIXELSY);
const int printHorRes = ::GetDeviceCaps(hPrintDC, HORZRES);
const SIZE printChunkSize = { printLogPx * clientChunkSize.cx / wndLogPx, printLogPy * clientChunkSize.cy / wndLogPy };

//browser total unscaled print area in printer pixel space
const RECT printRectPx = { 0, 0, docSize.cx* printLogPx / wndLogPx, docSize.cy*printLogPy / wndLogPy };
//unscaled target EMF size in 0.01 mm with printer resolution
const RECT outRect001Mm = { 0, 0, 2540 * docSize.cx / wndLogPx, 2540 * docSize.cy / wndLogPy };
HDC hMetaDC = CreateEnhMetaFile(hPrintDC, L"c:\\temp\\test.emf", &outRect001Mm, NULL);
::FillRect(hMetaDC, &printRectPx, (HBRUSH)::GetStockObject(BLACK_BRUSH));

//unscaled chunk EMF size in pixels with printer resolution
const RECT chunkRectPx = { 0, 0, printChunkSize.cx, printChunkSize.cy };
//unscaled chunk EMF size in 0.01 mm with printer resolution
const RECT chunkRect001Mm = { 0, 0, 2540 * clientChunkSize.cx / wndLogPx, 2540 * clientChunkSize.cy / wndLogPy };

////////
//render page content to metafile by small chunks

//get renderer interface
CComPtr<IHTMLElementRender> pRender;
pHtml->QueryInterface(IID_IHTMLElementRender, (void**)&pRender);
COleVariant printName = L"EMF";
pRender->SetDocumentPrinter(printName.bstrVal, hMetaDC);//current positions and target area
RECT chunkDestRectPx = { 0, 0, printChunkSize.cx, printChunkSize.cy };
POINT clientPos = { 0, 0 };
POINT printPos = { 0, 0 };

//loop over chunks left to right top to bottom until scroll area is completely covered
const SIZE lastScroll = { docSize.cx, docSize.cy};
while (clientPos.y < lastScroll.cy)
{
while (clientPos.x < lastScroll.cx)
{
//update horizontal scroll position and set target area
pHtml2->put_scrollLeft(clientPos.x);
chunkDestRectPx.left = printPos.x;
chunkDestRectPx.right = printPos.x + printChunkSize.cx;

//render to new emf, can be optimized to avoid recreation
HDC hChunkDC = CreateEnhMetaFile(hPrintDC, NULL, &chunkRect001Mm, NULL);
::FillRect(hChunkDC, &chunkRectPx, (HBRUSH)::GetStockObject(WHITE_BRUSH));
pRender->DrawToDC(hChunkDC);
HENHMETAFILE hChunkMetafile = CloseEnhMetaFile(hChunkDC);

//copy chunk to the main metafile
PlayEnhMetaFile(hMetaDC, hChunkMetafile, &chunkDestRectPx);
DeleteEnhMetaFile(hChunkMetafile);

//update horizontal positions
clientPos.x += clientChunkSize.cx;
printPos.x += printChunkSize.cx;
}

//reset horizontal positions
clientPos.x = 0;
printPos.x = 0;
//update vertical positions
clientPos.y += clientChunkSize.cy;
printPos.y += printChunkSize.cy;
pHtml2->put_scrollTop(clientPos.y);
chunkDestRectPx.top = printPos.y;
chunkDestRectPx.bottom = printPos.y + printChunkSize.cy;
}

//restore changed values on browser
//if for large pages on slow PC you get content scrolling during rendering and it is a problem,
//you can either hide the browser and show "working" or place on top first chunk content
pBodyStyle->put_overflow(keptOverflow.bstrVal);
pHtml2->put_scrollLeft(keptScrollPos.cx);
pHtml2->put_scrollTop(keptScrollPos.cy);
m_browser.put_Width(keptBrowserSize.cx);
m_browser.put_Height(keptBrowserSize.cy);
removeElement(pBody, pPadNode);

//draw to bitmap and close metafile
HENHMETAFILE hMetafile = CloseEnhMetaFile(hMetaDC);
RECT fitRect = { 0, 0, printHorRes, docSize.cy * printHorRes / docSize.cx };
convertEmfToBitmap(fitRect, hWndDc, hMetafile, L"c:\\temp\\test.bmp");
DeleteEnhMetaFile(hMetafile);

//cleanup - probably more here
::ReleaseDC(m_hWnd, hWndDc);
::DeleteDC(hPrintDC);

//{
//  std::stringstream ss;
//  ss << "====" << docSize.cx << "x" << docSize.cy << " -> " << fitRect.right << "x" << fitRect.bottom << "" << "\n";
//  OutputDebugStringA(ss.str().c_str());
//}

}///////////////
////some util

void convertEmfToBitmap(const RECT& fitRect, HDC hTargetDC, HENHMETAFILE hMetafile, LPCTSTR fileName)
{
//Create memory DC to render into
HDC hCompDc = ::CreateCompatibleDC(hTargetDC);
//NOTE this
BITMAPINFOHEADER infoHeader = { 0 };
infoHeader.biSize = sizeof(infoHeader);
infoHeader.biWidth = fitRect.right;
infoHeader.biHeight = -fitRect.bottom;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biCompression = BI_RGB;

BITMAPINFO info;
info.bmiHeader = infoHeader;

//create bitmap
BYTE* pMemory = 0;
HBITMAP hBitmap = ::CreateDIBSection(hCompDc, &info, DIB_RGB_COLORS, (void**)&pMemory, 0, 0);
HBITMAP hOldBmp = (HBITMAP)::SelectObject(hCompDc, hBitmap);PlayEnhMetaFile(hCompDc, hMetafile, &fitRect);

BITMAPFILEHEADER fileHeader = { 0 };
fileHeader.bfType = 0x4d42;
fileHeader.bfSize = 0;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

CFile file(
fileName,
CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyNone);
file.Write((char*)&fileHeader, sizeof(fileHeader));
file.Write((char*)&infoHeader, sizeof(infoHeader));

int bytes = (((24 * infoHeader.biWidth + 31) & (~31)) / 8) * abs(infoHeader.biHeight);
file.Write(pMemory, bytes);

::SelectObject(hCompDc, hOldBmp);

//Clean up
if (hBitmap)
::DeleteObject(hBitmap);
::DeleteDC(hCompDc);
}CComPtr<IHTMLDOMNode> appendPadElement(IHTMLDocument2* pDoc, IHTMLElement* pBody, long left, long top, long width, long height)
{
CComPtr<IHTMLElement> pPadElement;
pDoc->createElement(L"DIV", &pPadElement);
CComPtr<IHTMLStyle> pPadStyle;
pPadElement->get_style(&pPadStyle);
CComPtr<IHTMLStyle2> pPadStyle2;
pPadStyle->QueryInterface(IID_IHTMLStyle2, (void**)&pPadStyle2);
pPadStyle2->put_position(L"absolute");
CComVariant value = width;
pPadStyle->put_width(value);
value = height;
pPadStyle->put_height(value);
pPadStyle->put_posLeft((float)left);
pPadStyle->put_posTop((float)top);
CComPtr<IHTMLDOMNode> pPadNode;
pPadElement->QueryInterface(IID_IHTMLDOMNode, (void**)&pPadNode);
CComPtr<IHTMLDOMNode> pBodyNode;
pBody->QueryInterface(IID_IHTMLDOMNode, (void **)&pBodyNode);
pBodyNode->appendChild(pPadNode, NULL);
return pPadNode;
}

void removeElement(IHTMLElement* pParent, IHTMLDOMNode* pChild)
{
CComPtr<IHTMLDOMNode> pNode;
pParent->QueryInterface(IID_IHTMLDOMNode, (void **)&pNode);
pNode->removeChild(pChild, NULL);
}

Пример вывода страницы (4958×7656)

Пример вывода

6

Другие решения

Я взял ваш код и запустил его на IE11, когда WebBrowser контроль меньше, чем размер страницы. Это сделало часть страницы равной размеру элемента управления. Не уверен, почему вы говорите, что IE8 и IE11 отличаются.

Похоже, что общий подход к созданию полностраничных скриншотов корректируется WebBrowser Размер до снятия скриншота, вот так:

const long oldH = m_browser.get_Height();
const long oldW = m_browser.get_Width();
m_browser.put_Height(n_scrollHeight);
m_browser.put_Width(n_scrollWidth);

//Do the upscaling & render -- note that IE8 fails to render it here!!!!
pViewObject->Draw(DVASPECT_CONTENT, //DVASPECT_DOCPRINT
-1, NULL, NULL, NULL, hCompDc,
&rectPrnt,
NULL,
NULL, 0);

m_browser.put_Height(oldH);
m_browser.put_Width(oldW);

Кажется, это работает хорошо, даже на больших страницах, таких как та, которую вы сейчас читаете (я сделал снимок экрана 1920×8477). Это работает как на моем IE11, так и на IE8 виртуальная машина

У него есть побочный эффект сброса полос прокрутки, но это можно решить, например, с помощью невидимой копии WebBrowser для снимков экрана.

PS: Вы могли бы сделать лучше, предоставив пример кода, который можно скомпилировать, по крайней мере;)

1

По вопросам рекламы [email protected]