如何将HTML字符串加载到嵌入式WebBrowser控件?

时间:2017-07-14 12:35:48

标签: c++ winapi ihtmldocument2

我知道互联网上有很多关于此的文章,我尝试了很多。虽然我可以让我的浏览器在互联网上加载网页,但我无法设法让它从内存中加载HTML。

大多数时候,以下两种方法都没有任何明显的效果;其他时候,他们会抛出错误。虽然其他人说" for(auto& Array: PArrays)pPSI都没有指向有效的对象"但我不知道这是否属实。我用注释标记了导致错误的行。

如果您想重现问题,我的问题的最后是完整的代码。

方法1

pHtmlDoc2

错误:

  

Test.exe中的0x51539FB1(mshtml.dll)抛出异常:0xC0000005:   访问冲突读取位置0x00000030

方法2

此代码在CodeProject上从this article修改。在该源代码中,它运行良好,但在我尝试适应我的代码时它不起作用:

void WebBrowser::setHTML(const wchar_t *htmlSource) {
    HRESULT                   hr;

    IDispatch                 *pDispatch = 0;
    IHTMLDocument2            *pHtmlDoc2 = 0;
    IPersistStreamInit        *pPSI = 0;
    IStream                   *pStream = 0;
    HGLOBAL                   hHTMLContent;

    Navigate(L"about:blank");
    hr = webBrowser2->get_Document(&pDispatch);
    if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
    //BSTR p = 0;
    //if (SUCCEEDED(hr)) hr = pHtmlDoc2->get_readyState(&p);

    if (SUCCEEDED(hr) && pHtmlDoc2) hr = pHtmlDoc2->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);


    // allocate global memory to copy the HTML content to
    hHTMLContent = ::GlobalAlloc(GMEM_MOVEABLE, (::wcslen(htmlSource) + 1) * sizeof(TCHAR));
    if (hHTMLContent)
    {
        wchar_t * p_content(static_cast<wchar_t *>(GlobalLock(hHTMLContent)));
        ::wcscpy(p_content, htmlSource);  // Debbug: p_content contains HTML string
        GlobalUnlock(hHTMLContent);

        // create a stream object based on the HTML content
        if (SUCCEEDED(hr) && pPSI) hr = ::CreateStreamOnHGlobal(hHTMLContent, TRUE, &pStream);
        {
            hr = pPSI->InitNew();         //////////// <- Sometime throw error.
            if (SUCCEEDED(hr)) hr = pPSI->Load(pStream); 
        }
    }
    if (pStream) pStream->Release();
    if (pPSI) pPSI->Release();
    if (pHtmlDoc2) pHtmlDoc2->Release();
    if (pDispatch) pDispatch->Release();

}

错误:

  

Test.exe中的0x51577F2E(mshtml.dll)抛出异常:0xC0000005:   访问冲突读取位置0x000002C4

可能的第三种方法,来自Microsoft的Loading HTML content from a Stream,建议我从void WebBrowser::setHTML2(const wchar_t *htmlSource) { HRESULT hr; IDispatch *pDispatch = 0; IHTMLDocument2 *pHtmlDoc2 = 0; this->Navigate(L"about:blank"); hr = this->webBrowser2->get_Document(&pDispatch); if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2); if (SUCCEEDED(hr) && pHtmlDoc2) { BSTR bstr = SysAllocString(htmlSource); SAFEARRAY *psaStrings = SafeArrayCreateVector(VT_VARIANT, 0, 1); if (psaStrings) { VARIANT *param; hr = SafeArrayAccessData(psaStrings, (LPVOID*)&param); if (SUCCEEDED(hr) && param) { param->vt = VT_BSTR; param->bstrVal = bstr; //if (SUCCEEDED(hr)) hr = SafeArrayUnaccessData(psaStrings); if (SUCCEEDED(hr)) { hr = pHtmlDoc2->write(psaStrings); //////////// <- Sometime throw error. } } SafeArrayDestroy(psaStrings); } } if (pHtmlDoc2) pHtmlDoc2->Release(); if (pDispatch) pDispatch->Release(); } 事件中加载流,我已经尝试过但未能弄清楚如何实现。

完整源代码

以下是完整代码。我创建一个新的Win32项目并创建/修改以下文件:

Browser.h

DWebBrowserEvents2::DocumentComplete

Browser.cpp

#include <comdef.h>
#include <Exdisp.h>
#include <ExDispid.h>
#include <MsHTML.h>
#include <Mshtmhst.h>
#include <string>
#include <tchar.h>
#include <Windows.h>

using namespace std;

class WebBrowser :
    public IOleClientSite,
    public IOleInPlaceSite,
    public IStorage
{

public:

    WebBrowser(HWND hWndParent);

    bool CreateBrowser();

    void SetText(const wchar_t* t);

    void setHTML(const wchar_t *htmlSource);
    void setHTML2(const wchar_t *htmlSource);

    RECT PixelToHiMetric(const RECT& _rc);

    virtual void SetRect(const RECT& _rc);

    // ----- Control methods -----

    void GoBack();

    void GoForward();

    void Refresh();

    void Navigate(wstring szUrl);

    // ----- IUnknown -----

    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
        void**ppvObject) override;

    virtual ULONG STDMETHODCALLTYPE AddRef(void);

    virtual ULONG STDMETHODCALLTYPE Release(void);

    // ---------- IOleWindow ----------

    virtual HRESULT STDMETHODCALLTYPE GetWindow(
        __RPC__deref_out_opt HWND *phwnd) override;

    virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(
        BOOL fEnterMode) override;

    // ---------- IOleInPlaceSite ----------

    virtual HRESULT STDMETHODCALLTYPE CanInPlaceActivate(void) override;

    virtual HRESULT STDMETHODCALLTYPE OnInPlaceActivate(void) override;

    virtual HRESULT STDMETHODCALLTYPE OnUIActivate(void) override;

    virtual HRESULT STDMETHODCALLTYPE GetWindowContext(
        __RPC__deref_out_opt IOleInPlaceFrame **ppFrame,
        __RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc,
        __RPC__out LPRECT lprcPosRect,
        __RPC__out LPRECT lprcClipRect,
        __RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo) override;

    virtual HRESULT STDMETHODCALLTYPE Scroll(
        SIZE scrollExtant) override;

    virtual HRESULT STDMETHODCALLTYPE OnUIDeactivate(
        BOOL fUndoable) override;

    virtual HWND GetControlWindow();

    virtual HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate(void) override;

    virtual HRESULT STDMETHODCALLTYPE DiscardUndoState(void) override;

    virtual HRESULT STDMETHODCALLTYPE DeactivateAndUndo(void) override;

    virtual HRESULT STDMETHODCALLTYPE OnPosRectChange(
        __RPC__in LPCRECT lprcPosRect) override;

    // ---------- IOleClientSite ----------

    virtual HRESULT STDMETHODCALLTYPE SaveObject(void) override;

    virtual HRESULT STDMETHODCALLTYPE GetMoniker(
        DWORD dwAssign,
        DWORD dwWhichMoniker,
        __RPC__deref_out_opt IMoniker **ppmk) override;

    virtual HRESULT STDMETHODCALLTYPE GetContainer(
        __RPC__deref_out_opt IOleContainer **ppContainer) override;

    virtual HRESULT STDMETHODCALLTYPE ShowObject(void) override;
    virtual HRESULT STDMETHODCALLTYPE OnShowWindow(
        BOOL fShow) override;

    virtual HRESULT STDMETHODCALLTYPE RequestNewObjectLayout(void) override;

    // ----- IStorage -----

    virtual HRESULT STDMETHODCALLTYPE CreateStream(
        __RPC__in_string const OLECHAR *pwcsName,
        DWORD grfMode,
        DWORD reserved1,
        DWORD reserved2,
        __RPC__deref_out_opt IStream **ppstm) override;

    virtual HRESULT STDMETHODCALLTYPE OpenStream(
        const OLECHAR *pwcsName,
        void *reserved1,
        DWORD grfMode,
        DWORD reserved2,
        IStream **ppstm) override;

    virtual HRESULT STDMETHODCALLTYPE CreateStorage(
        __RPC__in_string const OLECHAR *pwcsName,
        DWORD grfMode,
        DWORD reserved1,
        DWORD reserved2,
        __RPC__deref_out_opt IStorage **ppstg) override;

    virtual HRESULT STDMETHODCALLTYPE OpenStorage(
        __RPC__in_opt_string const OLECHAR *pwcsName,
        __RPC__in_opt IStorage *pstgPriority,
        DWORD grfMode,
        __RPC__deref_opt_in_opt SNB snbExclude,
        DWORD reserved,
        __RPC__deref_out_opt IStorage **ppstg) override;

    virtual HRESULT STDMETHODCALLTYPE CopyTo(
        DWORD ciidExclude,
        const IID *rgiidExclude,
        __RPC__in_opt  SNB snbExclude,
        IStorage *pstgDest) override;

    virtual HRESULT STDMETHODCALLTYPE MoveElementTo(
        __RPC__in_string const OLECHAR *pwcsName,
        __RPC__in_opt IStorage *pstgDest,
        __RPC__in_string const OLECHAR *pwcsNewName,
        DWORD grfFlags) override;

    virtual HRESULT STDMETHODCALLTYPE Commit(
        DWORD grfCommitFlags) override;

    virtual HRESULT STDMETHODCALLTYPE Revert(void) override;

    virtual HRESULT STDMETHODCALLTYPE EnumElements(
        DWORD reserved1,
        void *reserved2,
        DWORD reserved3,
        IEnumSTATSTG **ppenum) override;

    virtual HRESULT STDMETHODCALLTYPE DestroyElement(
        __RPC__in_string const OLECHAR *pwcsName) override;

    virtual HRESULT STDMETHODCALLTYPE RenameElement(
        __RPC__in_string const OLECHAR *pwcsOldName,
        __RPC__in_string const OLECHAR *pwcsNewName) override;

    virtual HRESULT STDMETHODCALLTYPE SetElementTimes(
        __RPC__in_opt_string const OLECHAR *pwcsName,
        __RPC__in_opt const FILETIME *pctime,
        __RPC__in_opt const FILETIME *patime,
        __RPC__in_opt const FILETIME *pmtime) override;

    virtual HRESULT STDMETHODCALLTYPE SetClass(
        __RPC__in REFCLSID clsid) override;
    virtual HRESULT STDMETHODCALLTYPE SetStateBits(
        DWORD grfStateBits,
        DWORD grfMask) override;

    virtual HRESULT STDMETHODCALLTYPE Stat(
        __RPC__out STATSTG *pstatstg,
        DWORD grfStatFlag) override;

protected:

    IOleObject* oleObject;
    IOleInPlaceObject* oleInPlaceObject;

    IWebBrowser2* webBrowser2;

    LONG iComRefCount;

    RECT rObject;

    HWND hWndParent;
    HWND hWndControl;

};

Win32Project1.cpp

#include "stdafx.h"
#include "Browser.h"



namespace tkString
{
    wchar_t* Format(const wchar_t* format, ...)
    {
        va_list args;
        va_start(args, format);

        wchar_t *w = NULL;
        int len = _vsnwprintf(NULL, 0, format, args) + 1;
        if (len > 0)
        {
            w = new wchar_t[len];
            w[0] = 0;
            _vsnwprintf_s(w, len, len, format, args);
        }
        va_end(args);

        return w;
    }
}


void WebBrowser::SetText(const wchar_t* t)
{
    const wchar_t* html = L"<html><head><meta http-equiv='x-ua-compatible' content='IE=edge'></head><body><code>%ls</code></body></html>";

    if (t)
    {
        wchar_t *w = tkString::Format(html, t);
        setHTML(w);
        //setHTML2(w);
        delete[] w;
    }
    else this->Navigate(L"https://google.com.vn");
}

void WebBrowser::setHTML(const wchar_t *htmlSource) {
    HRESULT                   hr;

    IDispatch                 *pDispatch = 0;
    IHTMLDocument2            *pHtmlDoc2 = 0;
    IPersistStreamInit        *pPSI = 0;
    IStream                   *pStream = 0;
    HGLOBAL                   hHTMLContent;

    Navigate(L"about:blank");
    hr = webBrowser2->get_Document(&pDispatch);
    if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
    //BSTR p = 0;
    //if (SUCCEEDED(hr)) hr = pHtmlDoc2->get_readyState(&p);

    if (SUCCEEDED(hr) && pHtmlDoc2) hr = pHtmlDoc2->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);


    // allocate global memory to copy the HTML content to
    hHTMLContent = ::GlobalAlloc(GMEM_MOVEABLE, (::wcslen(htmlSource) + 1) * sizeof(TCHAR));
    if (hHTMLContent)
    {
        wchar_t * p_content(static_cast<wchar_t *>(GlobalLock(hHTMLContent)));
        ::wcscpy(p_content, htmlSource);
        GlobalUnlock(hHTMLContent);

        // create a stream object based on the HTML content
        if (SUCCEEDED(hr) && pPSI) hr = ::CreateStreamOnHGlobal(hHTMLContent, TRUE, &pStream);

        if (SUCCEEDED(hr) && pStream) hr = pPSI->InitNew();
        if (SUCCEEDED(hr)) hr = pPSI->Load(pStream);
    }
    if (pStream) pStream->Release();
    if (pPSI) pPSI->Release();
    if (pHtmlDoc2) pHtmlDoc2->Release();
    if (pDispatch) pDispatch->Release();

}

void WebBrowser::setHTML2(const wchar_t *htmlSource) {
    HRESULT                   hr;

    IDispatch                 *pDispatch = 0;
    IHTMLDocument2            *pHtmlDoc2 = 0;

    this->Navigate(L"about:blank");
    hr = this->webBrowser2->get_Document(&pDispatch);
    if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
    if (SUCCEEDED(hr) && pHtmlDoc2)
    {
        BSTR bstr = SysAllocString(htmlSource);
        SAFEARRAY *psaStrings = SafeArrayCreateVector(VT_VARIANT, 0, 1);
        if (psaStrings)
        {
            VARIANT *param;
            hr = SafeArrayAccessData(psaStrings, (LPVOID*)&param);
            if (SUCCEEDED(hr) && param)
            {
                param->vt = VT_BSTR;
                param->bstrVal = bstr;
                //if (SUCCEEDED(hr)) hr = SafeArrayUnaccessData(psaStrings);
                if (SUCCEEDED(hr)) hr = pHtmlDoc2->write(psaStrings);
            }

            SafeArrayDestroy(psaStrings);
        }
    }
    if (pHtmlDoc2) pHtmlDoc2->Release();
    if (pDispatch) pDispatch->Release();
}


WebBrowser::WebBrowser(HWND _hWndParent)
{
    iComRefCount = 0;
    ::SetRect(&rObject, 0, 0, 600, 400);
    hWndParent = _hWndParent;

    if (CreateBrowser() == FALSE)
    {
        return;
    }
    webBrowser2->put_Visible(TRUE);
    ShowWindow(GetControlWindow(), SW_SHOW);

    this->Navigate(_T("about:blank"));
}

bool WebBrowser::CreateBrowser()
{
    HRESULT hr;
    hr = ::OleCreate(CLSID_WebBrowser,
        IID_IOleObject, OLERENDER_DRAW, 0, this, this,
        (void**)&oleObject);

    if (FAILED(hr))
    {
        MessageBox(NULL, _T("Cannot create oleObject CLSID_WebBrowser"),
            _T("Error"),
            MB_ICONERROR);
        return FALSE;
    }

    hr = oleObject->SetClientSite(this);
    hr = OleSetContainedObject(oleObject, TRUE);

    RECT posRect;
    ::SetRect(&rObject, 0, 0, 600, 400);
    hr = oleObject->DoVerb(OLEIVERB_INPLACEACTIVATE,
        NULL, this, -1, hWndParent, &posRect);
    if (FAILED(hr))
    {
        MessageBox(NULL, _T("oleObject->DoVerb() failed"),
            _T("Error"),
            MB_ICONERROR);
        return FALSE;
    }

    hr = oleObject->QueryInterface(&webBrowser2);
    if (FAILED(hr))
    {
        MessageBox(NULL, _T("oleObject->QueryInterface(&webBrowser2) failed"),
            _T("Error"),
            MB_ICONERROR);
        return FALSE;
    }

    return TRUE;
}

RECT WebBrowser::PixelToHiMetric(const RECT& _rc)
{
    static bool s_initialized = false;
    static int s_pixelsPerInchX, s_pixelsPerInchY;
    if (!s_initialized)
    {
        HDC hdc = ::GetDC(0);
        s_pixelsPerInchX = ::GetDeviceCaps(hdc, LOGPIXELSX);
        s_pixelsPerInchY = ::GetDeviceCaps(hdc, LOGPIXELSY);
        ::ReleaseDC(0, hdc);
        s_initialized = true;
    }

    RECT rc;
    rc.left = MulDiv(2540, _rc.left, s_pixelsPerInchX);
    rc.top = MulDiv(2540, _rc.top, s_pixelsPerInchY);
    rc.right = MulDiv(2540, _rc.right, s_pixelsPerInchX);
    rc.bottom = MulDiv(2540, _rc.bottom, s_pixelsPerInchY);
    return rc;
}

void WebBrowser::SetRect(const RECT& _rc)
{
    rObject = _rc;

    {
        RECT hiMetricRect = PixelToHiMetric(rObject);
        SIZEL sz;
        sz.cx = hiMetricRect.right - hiMetricRect.left;
        sz.cy = hiMetricRect.bottom - hiMetricRect.top;
        oleObject->SetExtent(DVASPECT_CONTENT, &sz);
    }

    if (oleInPlaceObject != 0)
    {
        oleInPlaceObject->SetObjectRects(&rObject, &rObject);
    }
}

// ----- Control methods -----

void WebBrowser::GoBack()
{
    this->webBrowser2->GoBack();
}

void WebBrowser::GoForward()
{
    this->webBrowser2->GoForward();
}

void WebBrowser::Refresh()
{
    this->webBrowser2->Refresh();
}

void WebBrowser::Navigate(wstring szUrl)
{
    bstr_t url(szUrl.c_str());
    variant_t flags(0x02u); //navNoHistory
    this->webBrowser2->Navigate(url, &flags, 0, 0, 0);
}

// ----- IUnknown -----

HRESULT STDMETHODCALLTYPE WebBrowser::QueryInterface(REFIID riid,
    void**ppvObject)
{
    if (riid == __uuidof(IUnknown))
    {
        (*ppvObject) = static_cast<IOleClientSite*>(this);
    }
    else if (riid == __uuidof(IOleInPlaceSite))
    {
        (*ppvObject) = static_cast<IOleInPlaceSite*>(this);
    }
    else
    {
        return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;
}

ULONG STDMETHODCALLTYPE WebBrowser::AddRef(void)
{
    iComRefCount++;
    return iComRefCount;
}

ULONG STDMETHODCALLTYPE WebBrowser::Release(void)
{
    iComRefCount--;
    return iComRefCount;
}

// ---------- IOleWindow ----------

HRESULT STDMETHODCALLTYPE WebBrowser::GetWindow(
    __RPC__deref_out_opt HWND *phwnd)
{
    (*phwnd) = hWndParent;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::ContextSensitiveHelp(
    BOOL fEnterMode)
{
    return E_NOTIMPL;
}

// ---------- IOleInPlaceSite ----------

HRESULT STDMETHODCALLTYPE WebBrowser::CanInPlaceActivate(void)
{
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceActivate(void)
{
    OleLockRunning(oleObject, TRUE, FALSE);
    oleObject->QueryInterface(&oleInPlaceObject);
    oleInPlaceObject->SetObjectRects(&rObject, &rObject);

    return S_OK;

}

HRESULT STDMETHODCALLTYPE WebBrowser::OnUIActivate(void)
{
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::GetWindowContext(
    __RPC__deref_out_opt IOleInPlaceFrame **ppFrame,
    __RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc,
    __RPC__out LPRECT lprcPosRect,
    __RPC__out LPRECT lprcClipRect,
    __RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
    HWND hwnd = hWndParent;

    (*ppFrame) = NULL;
    (*ppDoc) = NULL;
    (*lprcPosRect).left = rObject.left;
    (*lprcPosRect).top = rObject.top;
    (*lprcPosRect).right = rObject.right;
    (*lprcPosRect).bottom = rObject.bottom;
    *lprcClipRect = *lprcPosRect;

    lpFrameInfo->fMDIApp = false;
    lpFrameInfo->hwndFrame = hwnd;
    lpFrameInfo->haccel = NULL;
    lpFrameInfo->cAccelEntries = 0;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::Scroll(
    SIZE scrollExtant)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::OnUIDeactivate(
    BOOL fUndoable)
{
    return S_OK;
}

HWND WebBrowser::GetControlWindow()
{
    if (hWndControl != 0)
        return hWndControl;

    if (oleInPlaceObject == 0)
        return 0;

    oleInPlaceObject->GetWindow(&hWndControl);
    return hWndControl;
}

HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceDeactivate(void)
{
    hWndControl = 0;
    oleInPlaceObject = 0;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::DiscardUndoState(void)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::DeactivateAndUndo(void)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::OnPosRectChange(
    __RPC__in LPCRECT lprcPosRect)
{
    return E_NOTIMPL;
}

// ---------- IOleClientSite ----------

HRESULT STDMETHODCALLTYPE WebBrowser::SaveObject(void)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::GetMoniker(
    DWORD dwAssign,
    DWORD dwWhichMoniker,
    __RPC__deref_out_opt IMoniker **ppmk)
{
    if ((dwAssign == OLEGETMONIKER_ONLYIFTHERE) &&
        (dwWhichMoniker == OLEWHICHMK_CONTAINER))
        return E_FAIL;

    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::GetContainer(
    __RPC__deref_out_opt IOleContainer **ppContainer)
{
    return E_NOINTERFACE;
}

HRESULT STDMETHODCALLTYPE WebBrowser::ShowObject(void)
{
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::OnShowWindow(
    BOOL fShow)
{
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::RequestNewObjectLayout(void)
{
    return E_NOTIMPL;
}

// ----- IStorage -----

HRESULT STDMETHODCALLTYPE WebBrowser::CreateStream(
    __RPC__in_string const OLECHAR *pwcsName,
    DWORD grfMode,
    DWORD reserved1,
    DWORD reserved2,
    __RPC__deref_out_opt IStream **ppstm)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::OpenStream(
    const OLECHAR *pwcsName,
    void *reserved1,
    DWORD grfMode,
    DWORD reserved2,
    IStream **ppstm)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::CreateStorage(
    __RPC__in_string const OLECHAR *pwcsName,
    DWORD grfMode,
    DWORD reserved1,
    DWORD reserved2,
    __RPC__deref_out_opt IStorage **ppstg)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::OpenStorage(
    __RPC__in_opt_string const OLECHAR *pwcsName,
    __RPC__in_opt IStorage *pstgPriority,
    DWORD grfMode,
    __RPC__deref_opt_in_opt SNB snbExclude,
    DWORD reserved,
    __RPC__deref_out_opt IStorage **ppstg)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::CopyTo(
    DWORD ciidExclude,
    const IID *rgiidExclude,
    __RPC__in_opt  SNB snbExclude,
    IStorage *pstgDest)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::MoveElementTo(
    __RPC__in_string const OLECHAR *pwcsName,
    __RPC__in_opt IStorage *pstgDest,
    __RPC__in_string const OLECHAR *pwcsNewName,
    DWORD grfFlags)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::Commit(
    DWORD grfCommitFlags)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::Revert(void)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::EnumElements(
    DWORD reserved1,
    void *reserved2,
    DWORD reserved3,
    IEnumSTATSTG **ppenum)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::DestroyElement(
    __RPC__in_string const OLECHAR *pwcsName)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::RenameElement(
    __RPC__in_string const OLECHAR *pwcsOldName,
    __RPC__in_string const OLECHAR *pwcsNewName)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::SetElementTimes(
    __RPC__in_opt_string const OLECHAR *pwcsName,
    __RPC__in_opt const FILETIME *pctime,
    __RPC__in_opt const FILETIME *patime,
    __RPC__in_opt const FILETIME *pmtime)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::SetClass(
    __RPC__in REFCLSID clsid)
{
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::SetStateBits(
    DWORD grfStateBits,
    DWORD grfMask)
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebBrowser::Stat(
    __RPC__out STATSTG *pstatstg,
    DWORD grfStatFlag)
{
    return E_NOTIMPL;
}

Visual Studio创建的其他源文件保持不变。

问题

什么问题导致我的程序无法加载HTML字符串,我该如何解决?或者您是否有任何工作示例(没有MFC或ATL)将HTML字符串加载到WebBrowser?

注意:我知道我可以将HTML字符串写入硬盘并将文件路径传递给浏览器,但我根本不想要涉及硬盘。

注2:为了赢得赏金,请尽可能多地使用我的代码,提供工作代码的完整示例。浏览器应该成功加载HTML字符串。即使它正在加载在线网页,它也应该停止处理这个新命令。

2 个答案:

答案 0 :(得分:2)

   Navigate(L"about:blank");

发布的源代码不能很好地证明问题。它没有显示任何错误,也没有显示错误&#34; Hello&#34;。只有通过省略 Navigate()调用才能接近nullptr错误。代码根本没有完成,它缺少捕获DocumentComplete事件所需的管道。只有此事件被触发后,WebBrowser :: setHtml()中的代码才能正常工作。

让WebBrowser类实现IDispatch和IUnknown接口:

class WebBrowser :
    public IDispatch,
    public IUnknown,
    public IOleClientSite,
    public IOleInPlaceSite,
    public IStorage
{
private:
    HRESULT OnCompleted(DISPPARAMS* args);
    wchar_t* htmlSource;
    IConnectionPoint* callback;
    DWORD eventCookie;
    // etc...
};

注意htmlSource变量现在是一个类成员,需要存储该字符串,直到DocumentComplete事件触发为止。

我们需要实施IDispatch。这很容易做到,我把代码内联:

// ---------- IDispatch ----------
HRESULT GetTypeInfoCount(UINT *pctinfo) {  return E_FAIL; }
HRESULT GetTypeInfo(UINT, LCID, ITypeInfo **) { return E_FAIL; }
HRESULT GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { return E_FAIL; }

HRESULT Invoke(DISPID dispIdMember, REFIID, LCID, WORD,
    DISPPARAMS *pDispParams, VARIANT *pVarResult,
    EXCEPINFO*, UINT*) {
    if (dispIdMember == DISPID_DOCUMENTCOMPLETE) return OnCompleted(pDispParams);
    else return S_OK;
}

需要调整QueryInterface():

HRESULT STDMETHODCALLTYPE WebBrowser::QueryInterface(REFIID riid, void**ppvObject)
{
    if      (riid == __uuidof(IUnknown))        *ppvObject = static_cast<IUnknown*>(this);
    else if (riid == __uuidof(IDispatch))       *ppvObject = static_cast<IDispatch*>(this);
    else if (riid == __uuidof(IOleClientSite))  *ppvObject = static_cast<IOleClientSite*>(this);
    else if (riid == __uuidof(IOleInPlaceSite)) *ppvObject = static_cast<IOleInPlaceSite*>(this);
    else
        return E_NOINTERFACE;
    AddRef();
    return S_OK;
}

构造函数需要订阅事件接口:

WebBrowser::WebBrowser(HWND _hWndParent)
{
    //...
    // appended:
    htmlSource = nullptr;
    IConnectionPointContainer* container = nullptr;
    webBrowser2->QueryInterface(IID_IConnectionPointContainer, (void**)&container);
    container->FindConnectionPoint(__uuidof(DWebBrowserEvents2), &callback);
    IUnknown* punk = nullptr;
    this->QueryInterface(IID_IUnknown, (void**)&punk);
    callback->Advise(punk, &eventCookie);
    punk->Release();
    container->Release();
}

setText()函数变得非常简单,我们根本不会担心setHtml()。我们只是设置htmlSource成员,以便我们知道在DocumentComplete事件触发时该怎么做:

void WebBrowser::SetText(const wchar_t* t)
{
    const wchar_t* html = L"<html><head><meta http-equiv='x-ua-compatible' content='IE=edge'></head><body><code>%ls</code></body></html>";
    if (htmlSource) delete[] htmlSource;
    htmlSource = tkString::Format(html, t);
    this->Navigate(L"about::blank");
}

最后是DocumentComplete事件触发时运行的添加函数。大多数代码都是从setHtml移动的:

HRESULT WebBrowser::OnCompleted(DISPPARAMS* args) {
    HRESULT                   hr;

    IDispatch                 *pDispatch = 0;
    IHTMLDocument2            *pHtmlDoc2 = 0;
    IPersistStreamInit        *pPSI = 0;
    IStream                   *pStream = 0;
    HGLOBAL                   hHTMLContent;


    if (!htmlSource) return S_OK;
    hr = webBrowser2->get_Document(&pDispatch);
    if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
    if (SUCCEEDED(hr) && pHtmlDoc2) hr = pHtmlDoc2->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);


    // allocate global memory to copy the HTML content to
    hHTMLContent = ::GlobalAlloc(GMEM_MOVEABLE, (::wcslen(htmlSource) + 1) * sizeof(TCHAR));
    if (hHTMLContent)
    {
        wchar_t * p_content(static_cast<wchar_t *>(GlobalLock(hHTMLContent)));
        ::wcscpy(p_content, htmlSource);
        GlobalUnlock(hHTMLContent);

        // create a stream object based on the HTML content
        if (SUCCEEDED(hr) && pPSI) hr = ::CreateStreamOnHGlobal(hHTMLContent, TRUE, &pStream);

        if (SUCCEEDED(hr) && pStream) hr = pPSI->InitNew();
        if (SUCCEEDED(hr)) hr = pPSI->Load(pStream);
    }
    if (pStream) pStream->Release();
    if (pPSI) pPSI->Release();
    if (pHtmlDoc2) pHtmlDoc2->Release();
    if (pDispatch) pDispatch->Release();
    delete[] htmlSource;
    htmlSource = nullptr;
    return S_OK;
}

我会留下适当的清理作为// todo。现在运行此代码可以产生预期的结果:

enter image description here

答案 1 :(得分:0)

基于Hans Passant's answer,bellow是完整的工作代码,适合任何想要测试的人 创建名为“Win32Project1”的新Win32项目,并添加/编辑以下文件:

WebBrowser.h

#pragma once

#include <comdef.h>
#include <Exdisp.h>
#include <ExDispid.h>
#include <MsHTML.h>
#include <Mshtmhst.h>
#include <string>
#include <tchar.h>
#include <Windows.h>

class WebBrowser :
    public IDispatch,
    public IOleClientSite,
    public IOleInPlaceSite,
    public IStorage
{
public:

    WebBrowser(HWND hWndParent);

    void SetText(const wchar_t* t);
    void setHTML(const wchar_t *htmlText, bool shouldWrapinBODYtag = true);

    // ----- Create Browser -----
    HRESULT RegisterGIWebBrowser();
    HRESULT GetGIWebBrowser(IWebBrowser2** pwb);
    bool CreateBrowser();

    // ----- For Resize Window -----
    RECT PixelToHiMetric(const RECT& _rc);
    virtual void SetRect(const RECT& _rc);
    virtual HWND GetControlWindow();

    // ----- Control methods -----
    void GoBack();
    void GoForward();
    void Refresh();
    void Navigate(const wchar_t *szUrl);

    // ----- IUnknown -----
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void**ppvObject) override;
    virtual ULONG STDMETHODCALLTYPE AddRef(void);
    virtual ULONG STDMETHODCALLTYPE Release(void);

    // ---------- IOleWindow ----------
    virtual HRESULT STDMETHODCALLTYPE GetWindow(__RPC__deref_out_opt HWND *phwnd) override;
    virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override;

    // ---------- IOleInPlaceSite ----------
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::CanInPlaceActivate(void) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnUIActivate(void) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceActivate(void) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::GetWindowContext(__RPC__deref_out_opt IOleInPlaceFrame **ppFrame, __RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc, __RPC__out LPRECT lprcPosRect, __RPC__out LPRECT lprcClipRect, __RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::Scroll(SIZE scrollExtant) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnUIDeactivate(BOOL fUndoable) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::DiscardUndoState(void) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::DeactivateAndUndo(void) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnPosRectChange(__RPC__in LPCRECT lprcPosRect) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceDeactivate(void) override;

    // ---------- IOleClientSite ----------
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::SaveObject(void) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::GetContainer(__RPC__deref_out_opt IOleContainer **ppContainer) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::ShowObject(void) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::OnShowWindow(BOOL fShow) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::RequestNewObjectLayout(void) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, __RPC__deref_out_opt IMoniker **ppmk) override;

    // ----- IStorage -----
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::CreateStream(__RPC__in_string const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, __RPC__deref_out_opt IStream **ppstm) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::OpenStream(const OLECHAR *pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::CreateStorage(__RPC__in_string const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, __RPC__deref_out_opt IStorage **ppstg) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::OpenStorage(__RPC__in_opt_string const OLECHAR *pwcsName, __RPC__in_opt IStorage *pstgPriority, DWORD grfMode, __RPC__deref_opt_in_opt SNB snbExclude, DWORD reserved, __RPC__deref_out_opt IStorage **ppstg) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::CopyTo(DWORD ciidExclude, const IID *rgiidExclude, __RPC__in_opt  SNB snbExclude, IStorage *pstgDest) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::MoveElementTo(__RPC__in_string const OLECHAR *pwcsName, __RPC__in_opt IStorage *pstgDest, __RPC__in_string const OLECHAR *pwcsNewName, DWORD grfFlags) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::Commit(DWORD grfCommitFlags) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::Revert(void) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::DestroyElement(__RPC__in_string const OLECHAR *pwcsName) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::RenameElement(__RPC__in_string const OLECHAR *pwcsOldName, __RPC__in_string const OLECHAR *pwcsNewName) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::SetElementTimes(__RPC__in_opt_string const OLECHAR *pwcsName, __RPC__in_opt const FILETIME *pctime, __RPC__in_opt const FILETIME *patime, __RPC__in_opt const FILETIME *pmtime) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::SetClass(__RPC__in REFCLSID clsid) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::SetStateBits(DWORD grfStateBits, DWORD grfMask) override;
    virtual HRESULT STDMETHODCALLTYPE WebBrowser::Stat(__RPC__out STATSTG *pstatstg, DWORD grfStatFlag) override;

    // ---------- IDispatch ----------
    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(__RPC__out UINT *pctinfo) override;
    virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT, LCID, __RPC__deref_out_opt ITypeInfo **) override;
    virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(__RPC__in REFIID riid, __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames, __RPC__in_range(0, 16384) UINT cNames, LCID lcid, __RPC__out_ecount_full(cNames) DISPID *rgDispId) override;
    virtual HRESULT STDMETHODCALLTYPE Invoke(_In_  DISPID dispIdMember, _In_  REFIID, _In_  LCID, _In_  WORD, _In_  DISPPARAMS *pDispParams, _Out_opt_ VARIANT *pVarResult, _Out_opt_ EXCEPINFO*, _Out_opt_ UINT*) override;


private:
    IOleObject* oleObject;
    IOleInPlaceObject* oleInPlaceObject;
    IWebBrowser2* webBrowser2;
    LONG iComRefCount;
    RECT rObject;
    HWND hWndParent;
    HWND hWndControl;

    HRESULT OnCompleted(DISPPARAMS* args);
    wchar_t* htmlSource;
    IConnectionPoint* callback;
    DWORD eventCookie;
    DWORD dwGIWebBrowserCookie;

    bool isFullyCreated;
};

WebBrowser.cpp

#include "stdafx.h"
#include "WebBrowser.h"

namespace tkString
{
    wchar_t* Format(const wchar_t* format, ...)
    {
        va_list args;
        va_start(args, format);

        wchar_t* w = NULL;
        int len = _vscwprintf(format, args) + 1;
        if (len > 1)
        {
            w = new wchar_t[len];
            w[0] = 0;
            _vsnwprintf_s(w, len, len, format, args);
        }
        va_end(args);

        return w;
    }
}

void WebBrowser::SetText(const wchar_t* t)
{
    if (isFullyCreated)
    {
        // TODO: escape text
        const wchar_t* html = L"<html><head><meta http-equiv='x-ua-compatible' content='IE=edge'></head><body><code>%ls</code></body></html>";
        if (htmlSource) delete[] htmlSource;
        htmlSource = tkString::Format(html, t);
        this->Navigate(L"about:blank");
    }
    else {}
}

void WebBrowser::setHTML(const wchar_t* htmlText, bool shouldWrapinBODYtag)
{
    if (isFullyCreated)
    {
        if (htmlSource) delete[] htmlSource;
        htmlSource = NULL;
        if (shouldWrapinBODYtag)
        {
            const wchar_t* html = L"<html><head><meta http-equiv='x-ua-compatible' content='IE=edge'></head><body>%ls</body></html>";
            htmlSource = tkString::Format(html, htmlText);
        }
        else
        {
            if (htmlText)
            {
                size_t len = wcslen(htmlText) + 1;
                htmlSource = new wchar_t[len];
                wmemcpy(htmlSource, htmlText, len);
            }
        }
        this->Navigate(L"about:blank");
    }
    else {}
}

HRESULT WebBrowser::OnCompleted(DISPPARAMS* args)
{
    HRESULT                   hr;

    IDispatch                 *pDispatch = 0;
    IHTMLDocument2            *pHtmlDoc2 = 0;
    IPersistStreamInit        *pPSI = 0;
    IStream                   *pStream = 0;
    HGLOBAL                   hHTMLContent;


    if (!htmlSource) return S_OK;
    hr = webBrowser2->get_Document(&pDispatch);
    if (SUCCEEDED(hr) && pDispatch) hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **)&pHtmlDoc2);
    if (SUCCEEDED(hr) && pHtmlDoc2) hr = pHtmlDoc2->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);


    // allocate global memory to copy the HTML content to
    size_t len = wcslen(htmlSource) + 1;
    hHTMLContent = ::GlobalAlloc(GMEM_MOVEABLE, len * sizeof(wchar_t));
    if (hHTMLContent)
    {
        wchar_t * p_content(static_cast<wchar_t *>(GlobalLock(hHTMLContent)));
        wmemcpy_s(p_content, len, htmlSource, len);
        GlobalUnlock(hHTMLContent);

        // create a stream object based on the HTML content
        if (SUCCEEDED(hr) && pPSI) hr = ::CreateStreamOnHGlobal(hHTMLContent, TRUE, &pStream);

        if (SUCCEEDED(hr) && pStream) hr = pPSI->InitNew();
        if (SUCCEEDED(hr)) hr = pPSI->Load(pStream);
    }
    if (pStream) pStream->Release();
    if (pPSI) pPSI->Release();
    if (pHtmlDoc2) pHtmlDoc2->Release();
    if (pDispatch) pDispatch->Release();

    delete[] htmlSource;
    htmlSource = nullptr;
    return S_OK;
}

// ----- Control methods -----
void WebBrowser::Navigate(const wchar_t *szUrl)
{
    HRESULT hr = E_FAIL;
    IWebBrowser2 *pwb = NULL;
    hr = GetGIWebBrowser(&pwb);
    if (SUCCEEDED(hr) && pwb)
    {
        bstr_t url(szUrl);
        variant_t flags(0x02u); //navNoHistory
        pwb->Navigate(url, &flags, 0, 0, 0);
    }
    else { /* Handle error */ }
}

void WebBrowser::GoBack() { this->webBrowser2->GoBack(); }
void WebBrowser::GoForward() { this->webBrowser2->GoForward(); }
void WebBrowser::Refresh() { this->webBrowser2->Refresh(); }

WebBrowser::WebBrowser(HWND _hWndParent) :
isFullyCreated(false)
{
    HRESULT hr = E_FAIL;
    htmlSource = nullptr;
    IConnectionPointContainer* container = nullptr;
    IUnknown* punk = nullptr;

    iComRefCount = 0;
    ::SetRect(&rObject, 0, 0, 300, 300);
    hWndParent = _hWndParent;

    if (CreateBrowser())
    {
        this->Navigate(L"about:blank");
        hr = RegisterGIWebBrowser();
        if (SUCCEEDED(hr))
        {
            webBrowser2->put_Visible(TRUE);
            ShowWindow(GetControlWindow(), SW_SHOW);

            hr = webBrowser2->QueryInterface(IID_IConnectionPointContainer, (void**)&container);
            if (SUCCEEDED(hr) && container) hr = container->FindConnectionPoint(__uuidof(DWebBrowserEvents2), &callback);
            if (SUCCEEDED(hr) && container) hr = this->QueryInterface(IID_IUnknown, (void**)&punk);
            if (SUCCEEDED(hr) && container) hr = callback->Advise(punk, &eventCookie);
            if (SUCCEEDED(hr) && eventCookie) isFullyCreated = true;

            if (punk) punk->Release();
            if (container) container->Release();
        }
    }
}

bool WebBrowser::CreateBrowser()
{
    HRESULT hr;
    hr = ::OleCreate(CLSID_WebBrowser, IID_IOleObject, OLERENDER_DRAW, 0, this, this, (void**)&oleObject);
    if (SUCCEEDED(hr) && oleObject)
    {
        if (SUCCEEDED(hr)) hr = oleObject->SetClientSite(this);
        if (SUCCEEDED(hr)) hr = OleSetContainedObject(oleObject, TRUE);

        if (SUCCEEDED(hr))
        {
            RECT posRect;
            ::SetRect(&posRect, -300, -300, 300, 300);
            hr = oleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, -1, hWndParent, &posRect);
        }
        hr = oleObject->QueryInterface(&webBrowser2);
    }
    if (FAILED(hr) || !webBrowser2)
    {
        MessageBox(NULL, L"Create Browser failed!", L"Error", MB_ICONERROR);
        return false;
    }
    return true;
}

RECT WebBrowser::PixelToHiMetric(const RECT& _rc)
{
    static bool s_initialized = false;
    static int s_pixelsPerInchX, s_pixelsPerInchY;
    if (!s_initialized)
    {
        HDC hdc = ::GetDC(0);
        s_pixelsPerInchX = ::GetDeviceCaps(hdc, LOGPIXELSX);
        s_pixelsPerInchY = ::GetDeviceCaps(hdc, LOGPIXELSY);
        ::ReleaseDC(0, hdc);
        s_initialized = true;
    }

    RECT rc;
    rc.left = MulDiv(2540, _rc.left, s_pixelsPerInchX);
    rc.top = MulDiv(2540, _rc.top, s_pixelsPerInchY);
    rc.right = MulDiv(2540, _rc.right, s_pixelsPerInchX);
    rc.bottom = MulDiv(2540, _rc.bottom, s_pixelsPerInchY);
    return rc;
}

void WebBrowser::SetRect(const RECT& _rc)
{
    rObject = _rc;

    {
        RECT hiMetricRect = PixelToHiMetric(rObject);
        SIZEL sz;
        sz.cx = hiMetricRect.right - hiMetricRect.left;
        sz.cy = hiMetricRect.bottom - hiMetricRect.top;
        oleObject->SetExtent(DVASPECT_CONTENT, &sz);
    }

    if (oleInPlaceObject != 0)
    {
        oleInPlaceObject->SetObjectRects(&rObject, &rObject);
    }
}

HWND WebBrowser::GetControlWindow() { if (!hWndControl && oleInPlaceObject) oleInPlaceObject->GetWindow(&hWndControl); return hWndControl; }

// Register IWebBrowser2 instance for use in threads
HRESULT WebBrowser::RegisterGIWebBrowser()
{
    HRESULT hr = E_FAIL;
    IUnknown*   pIUnknown = NULL;
    IGlobalInterfaceTable* pIGlobalInterfaceTable = NULL;
    dwGIWebBrowserCookie = 0;

    hr = ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&pIGlobalInterfaceTable);
    if (SUCCEEDED(hr) && pIGlobalInterfaceTable) hr = webBrowser2->QueryInterface(IID_IUnknown, (void**)&pIUnknown);
    if (SUCCEEDED(hr) && pIUnknown) hr = pIGlobalInterfaceTable->RegisterInterfaceInGlobal(pIUnknown, __uuidof(IWebBrowser2), &dwGIWebBrowserCookie);

    if (pIUnknown) pIUnknown->Release();
    if (pIGlobalInterfaceTable) pIGlobalInterfaceTable->Release();

    if (SUCCEEDED(hr) && dwGIWebBrowserCookie) return hr;
    return E_FAIL;
}

// Get IWebBrowser2 instance from threads
HRESULT WebBrowser::GetGIWebBrowser(IWebBrowser2** pwb)
{
    HRESULT hr = E_FAIL;
    IGlobalInterfaceTable* pIGlobalInterfaceTable = NULL;
    *pwb = NULL;

    if (dwGIWebBrowserCookie) hr = ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&pIGlobalInterfaceTable);
    if (SUCCEEDED(hr) && pIGlobalInterfaceTable) hr = pIGlobalInterfaceTable->GetInterfaceFromGlobal(dwGIWebBrowserCookie, __uuidof(IWebBrowser2), (void **)pwb);

    if (pIGlobalInterfaceTable) pIGlobalInterfaceTable->Release();
    return hr;
}

// ----- IUnknown -----
ULONG STDMETHODCALLTYPE WebBrowser::AddRef(void) { return ++iComRefCount; }
ULONG STDMETHODCALLTYPE WebBrowser::Release(void) { return --iComRefCount; }

HRESULT STDMETHODCALLTYPE WebBrowser::QueryInterface(REFIID riid, void**ppvObject)
{
    if (riid == __uuidof(IUnknown))        *ppvObject = static_cast<IDispatch*>(this);
    else if (riid == __uuidof(IDispatch))       *ppvObject = static_cast<IDispatch*>(this);
    else if (riid == __uuidof(IOleClientSite))  *ppvObject = static_cast<IOleClientSite*>(this);
    else if (riid == __uuidof(IOleInPlaceSite)) *ppvObject = static_cast<IOleInPlaceSite*>(this);
    else
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}

// ---------- IOleInPlaceSite ----------
HRESULT STDMETHODCALLTYPE WebBrowser::CanInPlaceActivate(void){ return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnUIActivate(void){ return S_OK; }

HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceActivate(void)
{
    OleLockRunning(oleObject, TRUE, FALSE);
    oleObject->QueryInterface(&oleInPlaceObject);
    oleInPlaceObject->SetObjectRects(&rObject, &rObject);
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::GetWindowContext(__RPC__deref_out_opt IOleInPlaceFrame **ppFrame, __RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc, __RPC__out LPRECT lprcPosRect, __RPC__out LPRECT lprcClipRect, __RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
    HWND hwnd = hWndParent;

    (*ppFrame) = NULL;
    (*ppDoc) = NULL;
    (*lprcPosRect).left = rObject.left;
    (*lprcPosRect).top = rObject.top;
    (*lprcPosRect).right = rObject.right;
    (*lprcPosRect).bottom = rObject.bottom;
    *lprcClipRect = *lprcPosRect;

    lpFrameInfo->fMDIApp = false;
    lpFrameInfo->hwndFrame = hwnd;
    lpFrameInfo->haccel = NULL;
    lpFrameInfo->cAccelEntries = 0;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebBrowser::Scroll(SIZE scrollExtant) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnUIDeactivate(BOOL fUndoable) { return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::DiscardUndoState(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::DeactivateAndUndo(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnPosRectChange(__RPC__in LPCRECT lprcPosRect) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceDeactivate(void) { hWndControl = 0; oleInPlaceObject = 0; return S_OK; }

// ---------- IDispatch ----------
HRESULT WebBrowser::GetTypeInfoCount(__RPC__out UINT *pctinfo) { return E_FAIL; }
HRESULT WebBrowser::GetTypeInfo(UINT, LCID, __RPC__deref_out_opt ITypeInfo **) { return E_FAIL; }
HRESULT WebBrowser::GetIDsOfNames(__RPC__in REFIID riid, __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames, __RPC__in_range(0, 16384) UINT cNames, LCID lcid, __RPC__out_ecount_full(cNames) DISPID *rgDispId) { return E_FAIL; }

HRESULT STDMETHODCALLTYPE WebBrowser::Invoke(_In_  DISPID dispIdMember, _In_  REFIID, _In_  LCID, _In_  WORD, _In_  DISPPARAMS *pDispParams, _Out_opt_ VARIANT *pVarResult, _Out_opt_ EXCEPINFO*, _Out_opt_ UINT*)
{
    if (dispIdMember == DISPID_DOCUMENTCOMPLETE) return OnCompleted(pDispParams);
    else return S_OK;
}

// ---------- IOleWindow ----------
HRESULT STDMETHODCALLTYPE WebBrowser::GetWindow(__RPC__deref_out_opt HWND *phwnd) { (*phwnd) = hWndParent; return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::ContextSensitiveHelp(BOOL fEnterMode) { return E_NOTIMPL; }

// ---------- IOleClientSite ----------
HRESULT STDMETHODCALLTYPE WebBrowser::SaveObject(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::GetContainer(__RPC__deref_out_opt IOleContainer **ppContainer) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::ShowObject(void) { return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::OnShowWindow(BOOL fShow) { return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::RequestNewObjectLayout(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, __RPC__deref_out_opt IMoniker **ppmk) { if ((dwAssign == OLEGETMONIKER_ONLYIFTHERE) && (dwWhichMoniker == OLEWHICHMK_CONTAINER))return E_FAIL; return E_NOTIMPL; }

// ----- IStorage -----
HRESULT STDMETHODCALLTYPE WebBrowser::CreateStream(__RPC__in_string const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, __RPC__deref_out_opt IStream **ppstm) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OpenStream(const OLECHAR *pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::CreateStorage(__RPC__in_string const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, __RPC__deref_out_opt IStorage **ppstg) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::OpenStorage(__RPC__in_opt_string const OLECHAR *pwcsName, __RPC__in_opt IStorage *pstgPriority, DWORD grfMode, __RPC__deref_opt_in_opt SNB snbExclude, DWORD reserved, __RPC__deref_out_opt IStorage **ppstg) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::CopyTo(DWORD ciidExclude, const IID *rgiidExclude, __RPC__in_opt  SNB snbExclude, IStorage *pstgDest) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::MoveElementTo(__RPC__in_string const OLECHAR *pwcsName, __RPC__in_opt IStorage *pstgDest, __RPC__in_string const OLECHAR *pwcsNewName, DWORD grfFlags) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::Commit(DWORD grfCommitFlags) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::Revert(void) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::DestroyElement(__RPC__in_string const OLECHAR *pwcsName) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::RenameElement(__RPC__in_string const OLECHAR *pwcsOldName, __RPC__in_string const OLECHAR *pwcsNewName) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::SetElementTimes(__RPC__in_opt_string const OLECHAR *pwcsName, __RPC__in_opt const FILETIME *pctime, __RPC__in_opt const FILETIME *patime, __RPC__in_opt const FILETIME *pmtime) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::SetClass(__RPC__in REFCLSID clsid) { return S_OK; }
HRESULT STDMETHODCALLTYPE WebBrowser::SetStateBits(DWORD grfStateBits, DWORD grfMask) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE WebBrowser::Stat(__RPC__out STATSTG *pstatstg, DWORD grfStatFlag) { return E_NOTIMPL; }

Win32Project1.cpp

#include "stdafx.h"
#include "Win32Project1.h"
#include "WebBrowser.h"

HWND hWnd = NULL;
HWND tkWebBrowserHWND = NULL;
WebBrowser* tkWebBrowser = NULL;
void callFromThread();

ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    OleInitialize(NULL);
    MyRegisterClass(hInstance);

    if (!InitInstance(hInstance, nCmdShow)) return FALSE;

    tkWebBrowserHWND = CreateWindow(L"Static", NULL, WS_CHILD | WS_VISIBLE, 221, 0, 300, 300, hWnd, NULL, hInstance, 0);
    tkWebBrowser = new WebBrowser(tkWebBrowserHWND);

    // Use one of following command
    //tkWebBrowser->SetText(L"Hello!");
    //tkWebBrowser->setHTML(L"<h1>Title</h1><div>Content</div>");
    //tkWebBrowser->setHTML(L"<html><body><h1>Title 2</h1><div>Content 2</div></body></html>", false);
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)callFromThread, 0, 0, 0);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

void callFromThread()
{
    OleInitialize(NULL);

    // Use one of following command
    //tkWebBrowser->SetText(L"Hello from thread!");
    //tkWebBrowser->setHTML(L"<h1>Title from thread</h1><div>Content from thread</div>");
    tkWebBrowser->setHTML(L"<html><body><h1>Title 2 from thread</h1><div>Content 2 from thread</div></body></html>", false);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;

    switch (message)
    {
    case WM_COMMAND:
        wmId = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        switch (wmId)
        {
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

HINSTANCE hInst;
const wchar_t* szTitle = L"Title";
const wchar_t* szWindowClass = L"Window Class";

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT1));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_WIN32PROJECT1);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance;

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd) return FALSE;

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}