我知道网上有很多关于这个的文章,我试了很多。虽然我可以让我的浏览器加载互联网上的网页,但我无法设法让它从内存中加载 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*)¶m); 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*)¶m); 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/