I am attempting to load HTML from URL using IPersistMoniker to add relative URLs base path, for example <img src="foo.jpg"> to load from mypath/images/ (or any other path). From what I found the process is (based on this example):
- implement
IMonikerinstance, in particularGetDisplayName(gives the URL for relative links) andBindToStorage(loads the content) QueryInterfaceof the TWebBrowser Document forIID_IPersistMonikerCreateBindCtx(not sure what this is for though)- use
Loadmethod of theIPersistMonikerto load HTML, passing theIMonikerinstance from (1) andCreateBindCtxinstance from (3)
GetDisplayName in my instance does get called, but the BindToStorage where I am supposed to pass the IStream to the actual HTML never gets called so the document always turns out blank, not loaded. The HRESULT is E_INVALIDARG for the call to Load. What have I missed?
IMoniker implementation (some things omitted):
// Simple IMoniker implementation
class TMoniker : public IMoniker
{
private: OleVariant baseUrl;
TMemoryStream* memStream;
LONG m_cRef;
public: TMoniker(const UnicodeString& fBaseUrl, const UnicodeString& fContent)
{
m_cRef = 1; // Set to 1 so that the AddRef() doesn't need to be called when initialized the first time
this->baseUrl = fBaseUrl;
memStream = new TMemoryStream;
memStream->LoadFromFile(fContent.SubString(8,fContent.Length()));
memStream->Position = 0;
}
//--------------------------------------------------------------
// IUnknown
//--------------------------------------------------------------
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
//--------------------------------------------------------------
// IMoniker
//--------------------------------------------------------------
STDMETHODIMP GetDisplayName(IBindCtx *pbc, IMoniker *pmkToLeft, LPOLESTR *ppszDisplayName)
{
Application->MessageBox(L"GetDisplayName", L"Info", MB_OK); // Check if method is called
// UPDATE - should be *ppszDisplayName = this->baseUrl;
ppszDisplayName = this->baseUrl;
return S_OK;
}
STDMETHODIMP BindToStorage(IBindCtx *pbc, IMoniker *pmkToLeft, REFIID riid, void **ppvObj)
{
Application->MessageBox(L"BindToStorage", L"Info", MB_OK); // Check if method is called
ppvObj = NULL;
if (IsEqualIID(riid, IID_IStream))
{
Application->MessageBox(L"IMoniker::BindToStorage", L"Info", MB_OK);
// DelphiInterface<IStream> sa(*(new TStreamAdapter(memStream.get(), soReference)));
// ppvObj = (IStream)sa;
}
return S_OK;
}
STDMETHODIMP BindToObject(IBindCtx *pbc, IMoniker *pmkToLeft, REFIID riidResult, void **ppvResult) { return E_NOTIMPL; }
STDMETHODIMP Reduce(IBindCtx *pbc, DWORD dwReduceHowFar, IMoniker **ppmkToLeft, IMoniker **ppmkReduced) { return E_NOTIMPL; }
STDMETHODIMP ComposeWith(IMoniker *pmkRight, BOOL fOnlyIfNotGeneric, IMoniker **ppmkComposite) { return E_NOTIMPL; }
STDMETHODIMP Enum(BOOL fForward, IEnumMoniker **ppenumMoniker) { return E_NOTIMPL; }
STDMETHODIMP IsEqual(IMoniker *pmkOtherMoniker) { return E_NOTIMPL; }
STDMETHODIMP Hash(DWORD *pdwHash) { return E_NOTIMPL; }
STDMETHODIMP IsRunning(IBindCtx *pbc, IMoniker *pmkToLeft, IMoniker *pmkNewlyRunning) { return E_NOTIMPL; }
STDMETHODIMP GetTimeOfLastChange(IBindCtx *pbc, IMoniker *pmkToLeft, FILETIME *pFileTime) { return E_NOTIMPL; }
STDMETHODIMP Inverse(IMoniker **ppmk) { return E_NOTIMPL; }
STDMETHODIMP CommonPrefixWith(IMoniker *pmkOther, IMoniker **ppmkPrefix) { return E_NOTIMPL; }
STDMETHODIMP RelativePathTo(IMoniker *pmkOther, IMoniker **ppmkRelPath) { return E_NOTIMPL; }
STDMETHODIMP ParseDisplayName(IBindCtx *pbc, IMoniker *pmkToLeft, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut) { return E_NOTIMPL; }
STDMETHODIMP IsSystemMoniker(DWORD *pdwMksys) { return E_NOTIMPL; }
//--------------------------------------------------------------
// IPersistStream
//--------------------------------------------------------------
STDMETHODIMP IsDirty() { return E_NOTIMPL; }
STDMETHODIMP Load(IStream *pStm) { return E_NOTIMPL; }
STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty) { return E_NOTIMPL; }
STDMETHODIMP GetSizeMax(ULARGE_INTEGER *pcbSize) { return E_NOTIMPL; }
//--------------------------------------------------------------
// IPersist
//--------------------------------------------------------------
STDMETHODIMP GetClassID(CLSID *pClassID) { return E_NOTIMPL; }
};
//------------------------------------------------------------------------------
// IUnknown::QueryInterface
//------------------------------------------------------------------------------
STDMETHODIMP TMoniker::QueryInterface(REFIID riid, void** ppv)
{
if (!ppv) return E_POINTER;
if (IID_IUnknown == riid) *ppv = (IUnknown *) this;
else if (IID_IMoniker == riid) *ppv = (IMoniker *) this;
else if (IID_IPersistStream == riid) *ppv = (IPersistStream *)this;
else if (IID_IPersist == riid) *ppv = (IPersist *) this;
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
// AddRef It
((IUnknown*)*ppv)->AddRef();
return S_OK;
}
//------------------------------------------------------------------------------
// IUnknown::AddRef
//------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) TMoniker::AddRef()
{
return ::InterlockedIncrement(&m_cRef);
}
//------------------------------------------------------------------------------
// IUnknown::Release
//------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) TMoniker::Release()
{
LONG cRef = ::InterlockedDecrement(&m_cRef);
if (0 == cRef) delete this;
return cRef;
}
Load the content:
TMoniker* pMnk = new TMoniker("about:blank", "file://c:\\temp\\file.html");
LPBC pbc=0;
DelphiInterface<IHTMLDocument2> diDoc2 = WB->Document;
if (diDoc2)
{
DelphiInterface<IPersistMoniker> diPM;
if (SUCCEEDED(diDoc2->QueryInterface(IID_IPersistMoniker, (void**)&diPM)))
{
if (SUCCEEDED(CreateBindCtx(0, &pbc)))
{
// !!! returns `E_INVALIDARG` here !!!
if (SUCCEEDED(diPM->Load(TRUE, pmk, pbc, STGM_READWRITE)))
{
}
}
}
}
if (pbc) pbc->Release();
pMnk->Release();
I see a few issues with your code:
the
ppszDisplayNameparameter ofGetDisplayName()is an[out]parameter. It receives the address of a caller-providedOLESTR*pointer, and you are expected to set that pointer to an OLE string that is allocated withIMalloc::Alloc()or equivalent. But you are not doing that. In fact, you are not returning any string back to the caller at all, because you are not dereferencing theppszDisplayNameparameter so you can access the pointer it is pointing at to assign a value to it.You can change
baseUrlfromOleVarianttoWideString, and then useWideString::Copy()(which usesSysAllocStringLen(), which is compatible withIMalloc) to return an allocated copy ofbaseUrlto the caller:the
ppvObjparameter ofBindToStorage()is likewise also an[out]parameter, but you are not dereferencing the passed pointer to return something back to the caller.You were on the right track using
TStreamAdapter, though, you just need to finish it:However, I would actually suggest changing
memStreamfromTMemoryStreamtoIStreamso it is not possible for anyIStreamgiven out byBindToStorage()to outlive the HTML data it is referring to:pMnkandpbcvariables inDelphiInterfaceor other smart COM pointer, let it handle callingRelease()for you. You can also useOleCheck()to simplify your error handling: