How to load HTML string to embedded WebBrowser control?

2

I know there are a lot of articles about this on the internet, and I tried many of them. Although I can make my browser load a webpage on internet, somehow I can't manage to make it load HTML from memory.

Most of the time, the two methods below just don't have any visible effect; other times, they throw errors. Although other people say that "neither pPSI nor pHtmlDoc2 point to a valid object", I don't know if this is true. I have marked the lines that cause the error with a comment.

At the end of my question is the fully working code if you want to reproduce the problem.

Method 1

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();

}

The error:

Exception thrown at 0x51539FB1 (mshtml.dll) in Test.exe: 0xC0000005: Access violation reading location 0x00000030

Method 2

This one is modified from this article on CodeProject. In that source, it works perfectly, but it doesn't work when I try to adapt to my code:

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();
}

The error:

Exception thrown at 0x51577F2E (mshtml.dll) in Test.exe: 0xC0000005: Access violation reading location 0x000002C4

A possible third method, Loading HTML content from a Stream from Microsoft, suggests I load from the stream on a DWebBrowserEvents2::DocumentComplete event, which I have tried and failed to figure out how to implement.

Full Source Code

Below is full code. I create a new Win32 project and create/modify the following files:

Browser.h

#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;

};

Browser.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;
}

Win32Project1.cpp

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

HWND mainHWND;
WebBrowser* myBrowser = NULL;

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
    OleInitialize(NULL);

    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WIN32PROJECT1, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    myBrowser = new WebBrowser(mainHWND);
    //myBrowser->Navigate(L"https://google.com.vn");
    myBrowser->SetText(L"Hello!");

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT1));

    MSG msg;

    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW 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(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WIN32PROJECT1);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   mainHWND = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!mainHWND)
   {
      return FALSE;
   }

   ShowWindow(mainHWND, nCmdShow);
   UpdateWindow(mainHWND);

   return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    // Removed
    return (INT_PTR)FALSE;
}

Other source files created by Visual Studio remain untouched.

Question

What problem causes my program to fail to load the HTML string, and how do I fix it? Or do you have any working example (no MFC or ATL) to load an HTML string to the WebBrowser?

Note: I know I can write the HTML string to hard drive and pass the file path to the browser, but I don't want the hard disk involved at all.

Note 2: To win the bounty, please give a complete example of working code, using as much of my code as possible. The browser should load HTML string successfully. Even when it's loading an online web page, it should stop to process this new command.

c++
winapi
ihtmldocument2
asked on Stack Overflow Jul 14, 2017 by Tuyen Pham • edited Jul 19, 2017 by TriskalJM

2 Answers

2
   Navigate(L"about:blank");

The posted source code does not demonstrate the problem well. It shows no errors, but also no "Hello". Only by omitting the Navigate() call does it get close to failing on nullptr errors. The code is simply not finished, it is missing the required plumbing to capture the DocumentComplete event. Only after this event is fired can the code inside WebBrowser::setHtml() work.

Have the WebBrowser class implement the IDispatch and IUnknown interfaces:

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...
};

Note how the htmlSource variable is now a class member, the string needs to be stored until the DocumentComplete event fires.

We need to implement IDispatch. That's pretty easy to do, I put the code inline:

// ---------- 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() needs to be tweaked:

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;
}

The constructor needs to subscribe the event interface:

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();
}

The setText() function becomes very simple, we're not going to worry about setHtml() at all. We'll just set the htmlSource member so we know what to do when the DocumentComplete event fires:

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");
}

And finally the added function that runs when the DocumentComplete event fires. Most of the code got moved from 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;
}

I'll leave proper cleanup as a // todo. Running this code now produces the desired outcome:

enter image description here

answered on Stack Overflow Jul 17, 2017 by Hans Passant
0

Based on Hans Passant's answer, bellow is the full working code, for anyone want to test.
Create new Win32 project named "Win32Project1" and add/edit following files:

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;
}
answered on Stack Overflow Jul 17, 2017 by Tuyen Pham • edited Jun 20, 2020 by Community

User contributions licensed under CC BY-SA 3.0