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

xingyun86 2023-2-13 1008

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

大多数时候,下面的两种方法没有任何可见的效果;其他时候,他们会抛出错误。虽然其他人说“pPSI 和pHtmlDoc2 都没有指向有效对象”,但我不知道这是不是真的。我已经用注释标记了导致错误的行。

如果您想重现问题,我的问题末尾是完整的工作代码。

方法一

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

错误:

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

方法二

这个修改自this article在 CodeProject 上。在那个源代码中,它工作得很好,但是当我尝试适应我的代码时它不起作用:

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

错误:

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

可能的第三种方法,Loading HTML content from a Stream来自 Microsoft,建议我从 DWebBrowserEvents2::DocumentComplete 事件的流中加载,我已经尝试过但未能弄清楚如何实现。

完整源代码

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

浏览器.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;
};

浏览器.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;
}

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

问题

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

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

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

最佳答案

   Navigate(L"about:blank");

发布的源代码没有很好地说明问题。它没有显示错误,但也没有“你好”。只有通过省略 Navigate() 调用,它才接近于因 nullptr 错误而失败。代码还没有完成,它缺少捕获 DocumentComplete 事件所需的管道。只有此事件被触发后,WebBrowser::setHtml() 中的代码才能工作。

让 WebBrowser 类实现 IDispatch 和 IUnknown 接口(interface):

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

构造函数需要订阅事件接口(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();
}

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

我会将适当的清理留作//待办事项。现在运行这段代码会产生预期的结果:

关于c++ - 如何将 HTML 字符串加载到嵌入式 WebBrowser 控件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45103166/

×
打赏作者
最新回复 (0)
只看楼主
全部楼主
返回