我一直在努力寻找这个,但仍然没有找到任何解决方案。我正在使用 native win32 c++(没有 MFC、ATL 或 .NET)
我正在使用 IHTMLDocument2->write() 加载 html 文档。 我想在页面准备好显示时获取事件,这意味着所有图像和元素都已下载并呈现。
从这里开始关注嵌入式浏览器对象 embedded web control (IWebBrowser2), embedded javascript's onkeydown and onkeyup not firing从这里http://www.codeproject.com/Articles/3365/Embed-an-HTML-control-in-your-own-window-using-pla我设法使嵌入式 Web 浏览器正常工作并加载 html bstr。
我修改了控件,如下所示。 现在我可以从文档中获取事件,但永远无法获取 DISPID_DOCUMENTCOMPLETE。 获得此事件的唯一解决方法是在 html 中有一个带有 src 属性的 iframe:
<html>
...
<body>
<iframe src="...">...</iframe>
</body>
</html>
然后是<iframe src="...">创建的窗口当 src 准备好显示时启动 DISPID_DOCUMENTCOMPLETE。 (写入内容后需要刷新浏览器)
如果<iframe>如果没有“src”,则事件会被触发,但 iframe 内容永远不会呈现。
在http://msdn.microsoft.com/en-us/library/aa768282.aspx它说文档中的每个窗口都会触发一个 documentComplete 事件,但由于我没有使用“导航”来加载 html 内容并且 DocumentComplete 永远不会被触发,我怀疑 DocumentComplete 只会在“导航”之后被触发,而不是当使用“write() + close()”加载 html 时均不使用“write()”。
我还尝试覆盖“its”互联网协议(protocol),因此当导航到“its://...”时,我设法加载了文档(想法来自这里:http://sumatrapdf.googlecode.com/svn/trunk/src/utils/HtmlWindow.cpp)。然后我得到了每次导航的 DISPID_DOCUMENTCOMPLETE,但是在 10-12 次导航之后程序崩溃了(它释放 EventSink 对象的次数比引用的次数多......很奇怪)。经过几天的尝试后,我得出结论,我没有足够的经验让这个工作正常进行,因为它不是一个很好的解决方案,只是一个丑陋的解决方法,我决定回到 IHTMLDocument2->write()。
我还尝试将 DIID_HTMLDocumentEvents2 附加到 DISPID_HTMLELEMENTEVENTS2_ONREADYSTATECHANGE,但完全没有成功。
请问有什么解决办法吗?
EmbBrowser.h:
(EmbBrowserEventSink 是一个简单的 IDIspatch 实现,IBrowserEventListener 是一个带有 onDocumentComplete 和 onClick 方法的接口(interface))
#pragma once
#include <stdio.h>
#include <Windows.h>
#include <string>
#include <Exdisp.h>
#include <comdef.h> // for variant_t
#include <exception>
#include <mshtml.h> // Defines of stuff like IHTMLDocument2. This is an include file with Visual C 6 and above
#include "EmbBrowserEventSink.h"
//------------------------------------------------------------------------------
class EmbBrowser : public IUnknown, public IOleClientSite, public IOleInPlaceSite, public IStorage{
public:
EmbBrowser(HWND _mainWindow, IBrowserEventListener *browserEventListener = NULL);
~EmbBrowser();
HRESULT STDMETHODCALLTYPE setEventListener(IBrowserEventListener *browserEventListener) {
this->mBrowserEventListener = browserEventListener;
return S_OK;
};
HRESULT STDMETHODCALLTYPE hideScrollBars();
HRESULT STDMETHODCALLTYPE displayHTMLStr(LPCTSTR htmlSource);
HRESULT STDMETHODCALLTYPE pixelToHiMetric(const RECT& _rc, RECT *_metricRc);
virtual HRESULT STDMETHODCALLTYPE setRect(const RECT &_rc);
virtual HRESULT STDMETHODCALLTYPE adjustSize(HWND hWnd);
virtual HRESULT STDMETHODCALLTYPE navigate(LPCTSTR _url);
// ----- IUnknown -----
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void**ppvObject) override;
virtual ULONG STDMETHODCALLTYPE AddRef(void) override { return ++m_comRefCount; };
virtual ULONG STDMETHODCALLTYPE Release( void) override {
if (!--m_comRefCount) {
delete this;
}
return m_comRefCount;
};
// ---------- IOleWindow ----------
virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE GetWindow(/* [out] */ __RPC__deref_out_opt HWND *phwnd) override;
virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp( /* [in] */ BOOL fEnterMode) override { return E_NOTIMPL; };
// ---------- IOleInPlaceSite ----------
virtual HRESULT STDMETHODCALLTYPE CanInPlaceActivate(void) override { return S_OK; };
virtual HRESULT STDMETHODCALLTYPE OnInPlaceActivate(void) override;
virtual HRESULT STDMETHODCALLTYPE OnUIActivate(void) override { return S_OK; };
virtual HRESULT STDMETHODCALLTYPE GetWindowContext(
/* [out] */ __RPC__deref_out_opt IOleInPlaceFrame **ppFrame,
/* [out] */ __RPC__deref_out_opt IOleInPlaceUIWindow **ppDoc,
/* [out] */ __RPC__out LPRECT lprcPosRect,
/* [out] */ __RPC__out LPRECT lprcClipRect,
/* [out][in] */ __RPC__inout LPOLEINPLACEFRAMEINFO lpFrameInfo) override;
virtual HRESULT STDMETHODCALLTYPE Scroll(/* [in] */ SIZE scrollExtant) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE OnUIDeactivate(/* [in] */ BOOL fUndoable) override { return S_OK; };
virtual HWND STDMETHODCALLTYPE GetControlWindow();
virtual HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate(void) override;
virtual HRESULT STDMETHODCALLTYPE DiscardUndoState(void) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE DeactivateAndUndo(void) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE OnPosRectChange(/* [in] */ __RPC__in LPCRECT lprcPosRect) override { return E_NOTIMPL; };
// ---------- IOleClientSite ----------
virtual HRESULT STDMETHODCALLTYPE SaveObject(void) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE GetMoniker(
/* [in] */ DWORD dwAssign,
/* [in] */ DWORD dwWhichMoniker,
/* [out] */ __RPC__deref_out_opt IMoniker **ppmk) override
{
if((dwAssign == OLEGETMONIKER_ONLYIFTHERE) && (dwWhichMoniker == OLEWHICHMK_CONTAINER)) {
return E_FAIL;
}
return E_NOTIMPL;
};
virtual HRESULT STDMETHODCALLTYPE GetContainer(/* [out] */ __RPC__deref_out_opt IOleContainer **ppContainer) override { return E_NOINTERFACE; };
virtual HRESULT STDMETHODCALLTYPE ShowObject( void) override { return S_OK; };
virtual HRESULT STDMETHODCALLTYPE OnShowWindow( /* [in] */ BOOL fShow) override { return S_OK; };;
virtual HRESULT STDMETHODCALLTYPE RequestNewObjectLayout( void) override { return E_NOTIMPL; };
// ----- IStorage -----
virtual HRESULT STDMETHODCALLTYPE CreateStream(
/* [string][in] */ __RPC__in_string const OLECHAR *pwcsName,
/* [in] */ DWORD grfMode,
/* [in] */ DWORD reserved1,
/* [in] */ DWORD reserved2,
/* [out] */ __RPC__deref_out_opt IStream **ppstm) override { return E_NOTIMPL; };
virtual /* [local] */ HRESULT STDMETHODCALLTYPE OpenStream(
/* [string][in] */ const OLECHAR *pwcsName,
/* [unique][in] */ void *reserved1,
/* [in] */ DWORD grfMode,
/* [in] */ DWORD reserved2,
/* [out] */ IStream **ppstm) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE CreateStorage(
/* [string][in] */ __RPC__in_string const OLECHAR *pwcsName,
/* [in] */ DWORD grfMode,
/* [in] */ DWORD reserved1,
/* [in] */ DWORD reserved2,
/* [out] */ __RPC__deref_out_opt IStorage **ppstg) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE OpenStorage(
/* [string][unique][in] */ __RPC__in_opt_string const OLECHAR *pwcsName,
/* [unique][in] */ __RPC__in_opt IStorage *pstgPriority,
/* [in] */ DWORD grfMode,
/* [unique][in] */ __RPC__deref_opt_in_opt SNB snbExclude,
/* [in] */ DWORD reserved,
/* [out] */ __RPC__deref_out_opt IStorage **ppstg) override { return E_NOTIMPL; };
virtual /* [local] */ HRESULT STDMETHODCALLTYPE CopyTo(
/* [in] */ DWORD ciidExclude,
/* [size_is][unique][in] */ const IID *rgiidExclude,
/* [annotation][unique][in] */
__RPC__in_opt SNB snbExclude,
/* [unique][in] */ IStorage *pstgDest) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE MoveElementTo(
/* [string][in] */ __RPC__in_string const OLECHAR *pwcsName,
/* [unique][in] */ __RPC__in_opt IStorage *pstgDest,
/* [string][in] */ __RPC__in_string const OLECHAR *pwcsNewName,
/* [in] */ DWORD grfFlags) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE Commit(/* [in] */ DWORD grfCommitFlags) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE Revert(void) override { return E_NOTIMPL; };
virtual /* [local] */ HRESULT STDMETHODCALLTYPE EnumElements(
/* [in] */ DWORD reserved1,
/* [size_is][unique][in] */ void *reserved2,
/* [in] */ DWORD reserved3,
/* [out] */ IEnumSTATSTG **ppenum) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE DestroyElement(/* [string][in] */ __RPC__in_string const OLECHAR *pwcsName) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE RenameElement(
/* [string][in] */ __RPC__in_string const OLECHAR *pwcsOldName,
/* [string][in] */ __RPC__in_string const OLECHAR *pwcsNewName) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE SetElementTimes(
/* [string][unique][in] */ __RPC__in_opt_string const OLECHAR *pwcsName,
/* [unique][in] */ __RPC__in_opt const FILETIME *pctime,
/* [unique][in] */ __RPC__in_opt const FILETIME *patime,
/* [unique][in] */ __RPC__in_opt const FILETIME *pmtime) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE SetClass(/* [in] */ __RPC__in REFCLSID clsid) override { return S_OK; };
virtual HRESULT STDMETHODCALLTYPE SetStateBits(
/* [in] */ DWORD grfStateBits,
/* [in] */ DWORD grfMask) override { return E_NOTIMPL; };
virtual HRESULT STDMETHODCALLTYPE Stat(
/* [out] */ __RPC__out STATSTG *pstatstg,
/* [in] */ DWORD grfStatFlag) override { return E_NOTIMPL; };
protected:
private:
IOleObject *m_oleObject;
LONG m_comRefCount;
HWND m_mainWindow;
RECT m_objectRect;
IWebBrowser2 *m_webBrowser;
IOleInPlaceObject *m_oleInPlaceObject;
HWND m_controlWindow;
DWORD mAdviseBrowserEventsCookie;
IBrowserEventListener *mBrowserEventListener;
EmbBrowserEventSink *mEmbBrowserEventSink;
HRESULT STDMETHODCALLTYPE connectEvents();
HRESULT STDMETHODCALLTYPE disconnectEvents();
};
EmbBrowser.cpp
#include "StdAfx.h"
#include "EmbBrowser.h"
EmbBrowser::EmbBrowser(HWND _mainWindow, IBrowserEventListener *browserEventListener) : m_mainWindow(_mainWindow),
mBrowserEventListener(browserEventListener), m_comRefCount(0), mEmbBrowserEventSink(NULL), mAdviseBrowserEventsCookie(0)
{
::SetRect(&m_objectRect, -300, -300, 300, 300);
HRESULT hr = ::OleCreate(CLSID_WebBrowser, IID_IOleObject, OLERENDER_DRAW, 0, this, this, (void**)&m_oleObject);
if (FAILED(hr)) {
throw new exception("OleCreate() failed");
}
hr = m_oleObject->SetClientSite(this);
hr = OleSetContainedObject(m_oleObject, TRUE);
if (FAILED(m_oleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, -1, m_mainWindow, &m_objectRect))) {
throw new exception("DoVerb(OLEIVERB_INPLACEACTIVATE) failed");
}
hr = m_oleObject->QueryInterface(&m_webBrowser);
if (FAILED(hr)) {
throw new exception("QueryInterface(IWebBrowser) failed");
}
this->navigate(L"about:blank");
}
EmbBrowser::~EmbBrowser() {
this->m_webBrowser->Release();
this->m_oleObject->Close(OLECLOSE_NOSAVE);
this->m_oleObject->Release();
}
HRESULT STDMETHODCALLTYPE EmbBrowser::navigate(LPCTSTR _url) {
BSTR url;
url = SysAllocString(_url);
variant_t flags(0x02u); // navNoHistory;
HRESULT hr = m_webBrowser->Navigate(url, &flags, NULL, NULL, NULL);
SysFreeString(url);
return hr;
}
HRESULT STDMETHODCALLTYPE EmbBrowser::hideScrollBars() {
HRESULT hr;
IDispatch *pDocDispatch = 0;
IHTMLDocument2 *pHtmlDocument = 0;
IHTMLElement *pBodyElm = 0;
IHTMLBodyElement *pBody = 0;
VARIANT v;
hr = m_webBrowser->get_Document(&pDocDispatch);
if (FAILED(hr) || !pDocDispatch) {
goto hideScrollBar_clean;
}
hr = pDocDispatch->QueryInterface(IID_IHTMLDocument2, (void **) &pHtmlDocument);
if (FAILED(hr) && !pHtmlDocument) {
goto hideScrollBar_clean;
}
hr = pHtmlDocument->get_body(&pBodyElm);
if (FAILED(hr) && !pBodyElm) {
goto hideScrollBar_clean;
}
hr = pBodyElm->QueryInterface(IID_IHTMLBodyElement, (void **) &pBody);
if (FAILED(hr) && !pBody) {
goto hideScrollBar_clean;
}
v.vt = VT_INT;
v.intVal = 0;
pBody->put_scroll(L"no");
pBody->put_leftMargin(v);
pBody->put_topMargin(v);
hideScrollBar_clean:
if (pBody) {
pBody->Release();
}
if (pBodyElm) {
pBodyElm->Release();
}
if (pHtmlDocument) {
pHtmlDocument->Release();
}
if (pDocDispatch) {
pDocDispatch->Release();
}
return hr;
}
HRESULT STDMETHODCALLTYPE EmbBrowser::displayHTMLStr(LPCTSTR htmlSource) {
// This is used by DisplayHTMLStr(). It can be global because we never change it.
static const SAFEARRAYBOUND ArrayBound = {1, 0};
HRESULT hr;
IDispatch *pDispatch = 0;
IHTMLDocument2 *pHtmlDoc2 = 0;
SAFEARRAY *sfArray = 0;
VARIANT *pVar = 0;
BSTR bstr = 0;
hr = m_webBrowser->get_Document(&pDispatch);
if (FAILED(hr) || !pDispatch) {
goto displayHTMLStr_clean;
}
hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **) &pHtmlDoc2);
if (FAILED(hr) || !pHtmlDoc2) {
goto displayHTMLStr_clean;
}
// Our HTML must be in the form of a BSTR. And it must be passed to write() in an
// array of "VARIENT" structs. So let's create all that.
sfArray = SafeArrayCreate(VT_VARIANT, 1, (SAFEARRAYBOUND *) &ArrayBound);
if (!sfArray) {
goto displayHTMLStr_clean;
}
hr = SafeArrayAccessData(sfArray, (void**) &pVar);
if (FAILED(hr)) {
goto displayHTMLStr_clean;
}
pVar->vt = VT_BSTR;
#ifndef UNICODE
{
wchar_t *buffer;
DWORD size;
size = MultiByteToWideChar(CP_ACP, 0, htmlSource, -1, 0, 0);
if (!(buffer = (wchar_t *)GlobalAlloc(GMEM_FIXED, sizeof(wchar_t) * size))) {
goto displayHTMLStr_clean;
}
MultiByteToWideChar(CP_ACP, 0, htmlSource, -1, buffer, size);
bstr = SysAllocString(buffer);
GlobalFree(buffer);
}
#else
bstr = SysAllocString(htmlSource);
#endif
if (!bstr) {
hr = E_OUTOFMEMORY;
goto displayHTMLStr_clean;
}
// Connect HTMLDocumentEvents2 to get onDocumentComplete and onClick events
this->connectEvents();
//mEmbBrowserEventSink->setFinishedWriting(false);
hr = pHtmlDoc2->clear();
if (FAILED(hr)) {
goto displayHTMLStr_clean;
}
hr = pHtmlDoc2->write(sfArray);
if (FAILED(hr)) {
goto displayHTMLStr_clean;
}
hr = pHtmlDoc2->close();
if (FAILED(hr)) {
goto displayHTMLStr_clean;
}
//mEmbBrowserEventSink->setFinishedWriting(true);
//hr = m_webBrowser->Refresh();
//if (FAILED(hr)) {
// goto displayHTMLStr_clean;
//}
//this->mBrowserEventListener->onDocumentComplete(true);
displayHTMLStr_clean:
// Normally, we'd need to free our BSTR, but SafeArrayDestroy() does it for us
// SysFreeString(bstr);
if (bstr) {
SysFreeString(bstr);
}
if (sfArray) {
// SafeArrayDestroy(sfArray);
SafeArrayUnaccessData(sfArray);
}
if (pHtmlDoc2) {
pHtmlDoc2->Release();
}
if (pDispatch) {
pDispatch->Release();
}
return hr;
}
HRESULT STDMETHODCALLTYPE EmbBrowser::connectEvents() {
HRESULT hr;
IConnectionPointContainer *pCPC = 0;
IConnectionPoint *pCP = 0;
IUnknown *pUnkEventSink = 0;
this->disconnectEvents();
this->mEmbBrowserEventSink = new EmbBrowserEventSink(m_webBrowser, this->mBrowserEventListener);
hr = this->mEmbBrowserEventSink->QueryInterface(IID_IUnknown, (void **) &pUnkEventSink);
if (FAILED(hr) || !pUnkEventSink) {
goto connectEvents_clean;
}
hr = /*pHtmlDoc2*/m_webBrowser->QueryInterface(IID_IConnectionPointContainer, (void **) &pCPC);
if (FAILED(hr) || !pCPC) {
goto connectEvents_clean;
}
hr = pCPC->FindConnectionPoint(/*DIID_HTMLDocumentEvents2*/ DIID_DWebBrowserEvents2, &pCP);
if (FAILED(hr) || !pCP) {
goto connectEvents_clean;
}
hr = pCP->Advise(pUnkEventSink, &mAdviseBrowserEventsCookie);
if (FAILED(hr)) {
goto connectEvents_clean;
}
connectEvents_clean:
if (pCP) {
pCP->Release();
}
if (pCPC) {
pCPC->Release();
}
if (pUnkEventSink) {
pUnkEventSink->Release();
}
return hr;
}
HRESULT STDMETHODCALLTYPE EmbBrowser::disconnectEvents() {
HRESULT hr;
IDispatch *pDispatch = 0;
IHTMLDocument2 *pHtmlDoc2 = 0;
IConnectionPointContainer *pCPC = 0;
IConnectionPoint *pCP = 0;
if (!mAdviseHTMLEventsCookie) {
hr = S_OK;
goto disconnectHtmlEvents_clean;
}
//hr = m_webBrowser->get_Document(&pDispatch);
//if (FAILED(hr) || !pDispatch) {
// goto disconnectHtmlEvents_clean;
//}
//hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **) &pHtmlDoc2);
//if (FAILED(hr) || !pHtmlDoc2) {
// goto disconnectHtmlEvents_clean;
//}
hr = /*pHtmlDoc2*/this->m_webBrowser->QueryInterface(IID_IConnectionPointContainer, (void **) &pCPC);
if (FAILED(hr) || !pCPC) {
goto disconnectHtmlEvents_clean;
}
hr = pCPC->FindConnectionPoint(/*DIID_HTMLDocumentEvents2*/ DIID_DWebBrowserEvents2, &pCP);
if (FAILED(hr) || !pCP) {
goto disconnectHtmlEvents_clean;
}
hr = pCP->Unadvise(mAdviseBrowserEventsCookie);
if (FAILED(hr)) {
goto disconnectHtmlEvents_clean;
}
mAdviseBrowserEventsCookie= 0;
disconnectHtmlEvents_clean:
if (pCP) {
pCP->Release();
}
if (pCPC) {
pCPC->Release();
}
if (pHtmlDoc2) {
pHtmlDoc2->Release();
}
if (pDispatch) {
pDispatch->Release();
}
return hr;
}
HRESULT STDMETHODCALLTYPE EmbBrowser::pixelToHiMetric(const RECT& _rc, RECT *_metricRc) {
static bool s_initialized = false;
static int s_pixelsPerInchX, s_pixelsPerInchY;
if (!_metricRc) {
return E_INVALIDARG;
}
if (!s_initialized) {
HDC hdc = ::GetDC(NULL);
s_pixelsPerInchX = ::GetDeviceCaps(hdc, LOGPIXELSX);
s_pixelsPerInchY = ::GetDeviceCaps(hdc, LOGPIXELSY);
::ReleaseDC(NULL, hdc);
s_initialized = true;
}
RECT rc;
_metricRc->left = MulDiv(2540, _rc.left, s_pixelsPerInchX);
_metricRc->top = MulDiv(2540, _rc.top, s_pixelsPerInchY);
_metricRc->right = MulDiv(2540, _rc.right, s_pixelsPerInchX);
_metricRc->bottom = MulDiv(2540, _rc.bottom, s_pixelsPerInchY);
return S_OK;
}
HRESULT STDMETHODCALLTYPE EmbBrowser::setRect(const RECT &_rc) {
HRESULT hr = E_FAIL;
m_objectRect = _rc;
{
RECT hiMetricRect;
hr = pixelToHiMetric(m_objectRect, &hiMetricRect);
SIZEL sz;
sz.cx = hiMetricRect.right - hiMetricRect.left;
sz.cy = hiMetricRect.bottom - hiMetricRect.top;
m_oleObject->SetExtent(DVASPECT_CONTENT, &sz);
}
if (m_oleInPlaceObject != NULL) {
hr = m_oleInPlaceObject->SetObjectRects(&m_objectRect, &m_objectRect);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE EmbBrowser::adjustSize(HWND hWnd) {
RECT rc;
GetWindowRect(hWnd, &rc);
rc.bottom -= rc.top;
rc.top -= rc.top;
rc.right -= rc.left;
rc.left -= rc.left;
return setRect(rc);
}
// -----== IUnknown ==----->
HRESULT STDMETHODCALLTYPE EmbBrowser::QueryInterface(REFIID riid, void **ppvObject) {
if (riid == __uuidof(IUnknown)) {
(*ppvObject) = static_cast<IUnknown *> (this);
} else if (riid == __uuidof(IOleClientSite)) {
(*ppvObject) = static_cast<IOleClientSite *> (this);
} else if (riid == __uuidof(IOleInPlaceSite)) {
(*ppvObject) = static_cast<IOleInPlaceSite *> (this);
} else if(riid == __uuidof(IStorage)) {
(*ppvObject) = static_cast<IStorage *> (this);
} else if (riid == __uuidof(IOleWindow)) {
(*ppvObject) = static_cast<IOleWindow *> (this);
} else {
(*ppvObject) = 0;
return E_NOINTERFACE;
}
AddRef(); // implicit AddRef()
return S_OK;
}
// ----------== IOleWindow ==---------->
HRESULT STDMETHODCALLTYPE EmbBrowser::GetWindow(__RPC__deref_out_opt HWND *phwnd) {
(*phwnd) = m_mainWindow;
return S_OK;
}
// ----------== IOleInPlaceSite ==---------->
HRESULT STDMETHODCALLTYPE EmbBrowser::OnInPlaceActivate(void) {
OleLockRunning(m_oleObject, TRUE, FALSE);
m_oleObject->QueryInterface(&m_oleInPlaceObject);
m_oleInPlaceObject->SetObjectRects(&m_objectRect, &m_objectRect);
return S_OK;
}
HRESULT STDMETHODCALLTYPE EmbBrowser::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 = m_mainWindow;
(*ppFrame) = NULL;
(*ppDoc) = NULL;
(*lprcPosRect).left = m_objectRect.left;
(*lprcPosRect).top = m_objectRect.top;
(*lprcPosRect).right = m_objectRect.right;
(*lprcPosRect).bottom = m_objectRect.bottom;
*lprcClipRect = *lprcPosRect;
lpFrameInfo->fMDIApp = false;
lpFrameInfo->hwndFrame = hwnd;
lpFrameInfo->haccel = NULL;
lpFrameInfo->cAccelEntries = 0;
return S_OK;
}
HWND STDMETHODCALLTYPE EmbBrowser::GetControlWindow() {
if(m_controlWindow != NULL) {
return m_controlWindow;
}
if(m_oleInPlaceObject == NULL) {
return NULL;
}
m_oleInPlaceObject->GetWindow(&m_controlWindow);
return m_controlWindow;
}
HRESULT STDMETHODCALLTYPE EmbBrowser::OnInPlaceDeactivate(void) {
m_controlWindow = NULL;
m_oleInPlaceObject = NULL;
return S_OK;
}
最佳答案
感谢 Remy,我再次尝试使用 IPersistStreamInit 加载,现在它似乎可以正常工作了。 这是新的 displayHTMLStr():
HRESULT STDMETHODCALLTYPE EmbBrowser::displayHTMLStr(LPCTSTR htmlSource) {
HRESULT hr;
IDispatch *pDispatch = 0;
IHTMLDocument2 *pHtmlDoc2 = 0;
IPersistStreamInit *pPSI = 0;
IStream *pStream = 0;
HGLOBAL hHTMLContent;
hr = m_webBrowser->get_Document(&pDispatch);
if (FAILED(hr) || !pDispatch) {
goto displayHTMLStr_clean;
}
hr = pDispatch->QueryInterface(IID_IHTMLDocument2, (void **) &pHtmlDoc2);
if (FAILED(hr) || !pHtmlDoc2) {
goto displayHTMLStr_clean;
}
hr = pHtmlDoc2->QueryInterface(IID_IPersistStreamInit, (void **) &pPSI);
if (FAILED(hr) || !pPSI) {
goto displayHTMLStr_clean;
}
// allocate global memory to copy the HTML content to
hHTMLContent = ::GlobalAlloc(GPTR, (::_tcslen(htmlSource) + 1) * sizeof(TCHAR));
if (!hHTMLContent) {
hr = E_OUTOFMEMORY;
goto displayHTMLStr_clean;
}
::_tcscpy((TCHAR *) hHTMLContent, htmlSource);
// create a stream object based on the HTML content
hr = ::CreateStreamOnHGlobal(hHTMLContent, TRUE, &pStream);
if (FAILED(hr) || !pStream) {
goto displayHTMLStr_clean;
}
hr = pPSI->InitNew();
if (FAILED(hr)) {
goto displayHTMLStr_clean;
}
// Connect HTMLDocumentEvents2 to get onDocumentComplete and onClick events
this->connectEvents();
hr = pPSI->Load(pStream);
_tprintf(_T("Written: \n%s\n\n"), htmlSource);
displayHTMLStr_clean:
if (pStream) {
pStream->Release();
}
if (hHTMLContent) {
GlobalFree(hHTMLContent);
}
if (pPSI) {
pPSI->Release();
}
if (pHtmlDoc2) {
pHtmlDoc2->Release();
}
if (pDispatch) {
pDispatch->Release();
}
return hr;
}
关于c++ - 嵌入式 IWebBrowser2 中的 DocumentComplete 事件使用 write() 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20286451/
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只