summaryrefslogtreecommitdiffstats
path: root/other-licenses/nsis/Contrib/WebBrowser/WebBrowser.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--other-licenses/nsis/Contrib/WebBrowser/WebBrowser.cpp604
1 files changed, 604 insertions, 0 deletions
diff --git a/other-licenses/nsis/Contrib/WebBrowser/WebBrowser.cpp b/other-licenses/nsis/Contrib/WebBrowser/WebBrowser.cpp
new file mode 100644
index 0000000000..59e0a1d881
--- /dev/null
+++ b/other-licenses/nsis/Contrib/WebBrowser/WebBrowser.cpp
@@ -0,0 +1,604 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0.If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "WebBrowser.h"
+#include <mshtmdid.h>
+
+WebBrowser::WebBrowser(HWND _hWndParent) : mHwndParent(_hWndParent) {
+ // Whatever executed this constructor owns our first reference.
+ AddRef();
+
+ HRESULT hr = ::OleCreate(CLSID_WebBrowser, IID_IOleObject, OLERENDER_DRAW, 0,
+ this, this, (void**)&mOleObject);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ RECT posRect;
+ ::GetClientRect(mHwndParent, &posRect);
+
+ hr = mOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, nullptr, this, 0,
+ mHwndParent, &posRect);
+ if (FAILED(hr)) {
+ mOleObject->Release();
+ mOleObject = nullptr;
+ return;
+ }
+
+ SetRect(posRect);
+
+ hr = mOleObject->QueryInterface(&mWebBrowser2);
+ if (FAILED(hr)) {
+ mOleObject->Release();
+ mOleObject = nullptr;
+ return;
+ }
+
+ mWebBrowser2->put_Silent(VARIANT_TRUE);
+}
+
+WebBrowser::~WebBrowser() {
+ if (mWebBrowser2) {
+ mWebBrowser2->Release();
+ mWebBrowser2 = nullptr;
+ }
+ if (mOleInPlaceActiveObject) {
+ mOleInPlaceActiveObject->Release();
+ mOleInPlaceActiveObject = nullptr;
+ }
+ if (mOleInPlaceObject) {
+ mOleInPlaceObject->Release();
+ mOleInPlaceObject = nullptr;
+ }
+ if (mOleObject) {
+ mOleObject->Release();
+ mOleObject = nullptr;
+ }
+}
+
+void WebBrowser::Shutdown() {
+ if (mOleObject) {
+ mOleObject->Close(OLECLOSE_NOSAVE);
+ mOleObject->SetClientSite(nullptr);
+ }
+}
+
+bool WebBrowser::IsInitialized() { return mOleObject != nullptr; }
+
+HRESULT WebBrowser::ActiveObjectTranslateAccelerator(bool tab, LPMSG lpmsg) {
+ if (IsInitialized() && mOleInPlaceActiveObject) {
+ HRESULT hr = mOleInPlaceActiveObject->TranslateAcceleratorW(lpmsg);
+ if (hr == S_FALSE && tab) {
+ // The browser control will give up focus, but it is the only control so
+ // it would get focus again via IsDialogMessage. This does not result in
+ // the focus returning to the web page, though, so instead let the
+ // control process the tab again.
+ hr = mOleInPlaceActiveObject->TranslateAcceleratorW(lpmsg);
+ }
+ return hr;
+ } else {
+ return S_FALSE;
+ }
+}
+
+void WebBrowser::SetRect(const RECT& _rc) {
+ mRect = _rc;
+
+ if (mOleInPlaceObject) {
+ mOleInPlaceObject->SetObjectRects(&mRect, &mRect);
+ }
+}
+
+void WebBrowser::Resize(DWORD width, DWORD height) {
+ RECT r = mRect;
+ r.bottom = r.top + height;
+ r.right = r.left + width;
+ SetRect(r);
+}
+
+void WebBrowser::Navigate(wchar_t* url) {
+ if (!IsInitialized()) {
+ return;
+ }
+
+ VARIANT flags;
+ VariantInit(&flags);
+ flags.vt = VT_I4;
+ flags.intVal = navNoHistory | navEnforceRestricted | navUntrustedForDownload |
+ navBlockRedirectsXDomain;
+
+ mWebBrowser2->Navigate(url, &flags, nullptr, nullptr, nullptr);
+}
+
+void WebBrowser::AddCustomFunction(wchar_t* name, CustomFunction function,
+ void* arg) {
+ CustomFunctionRecord record = {name, function, arg};
+
+// We've disabled exceptions but push_back can throw on an allocation
+// failure, so we need to suppress a warning trying to tell us that
+// that combination doesn't make any sense.
+#pragma warning(suppress : 4530)
+ mCustomFunctions.push_back(record);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IUnknown
+//////////////////////////////////////////////////////////////////////////////
+// This is a standard IUnknown implementation, we don't need anything special.
+
+HRESULT STDMETHODCALLTYPE WebBrowser::QueryInterface(REFIID riid,
+ void** ppvObject) {
+ if (riid == __uuidof(IUnknown)) {
+ *ppvObject = static_cast<IOleClientSite*>(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(IDropTarget)) {
+ *ppvObject = static_cast<IDropTarget*>(this);
+ } else if (riid == __uuidof(IStorage)) {
+ *ppvObject = static_cast<IStorage*>(this);
+ } else if (riid == __uuidof(IDocHostUIHandler)) {
+ *ppvObject = static_cast<IDocHostUIHandler*>(this);
+ } else if (riid == __uuidof(IDocHostShowUI)) {
+ *ppvObject = static_cast<IDocHostShowUI*>(this);
+ } else if (riid == __uuidof(IDispatch)) {
+ *ppvObject = static_cast<IDispatch*>(this);
+ } else {
+ *ppvObject = nullptr;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+ULONG STDMETHODCALLTYPE WebBrowser::AddRef() {
+ return InterlockedIncrement(&mComRefCount);
+}
+
+ULONG STDMETHODCALLTYPE WebBrowser::Release() {
+ ULONG refCount = InterlockedDecrement(&mComRefCount);
+ if (refCount == 0) {
+ delete this;
+ }
+ return refCount;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IOleWindow
+//////////////////////////////////////////////////////////////////////////////
+
+HRESULT STDMETHODCALLTYPE
+WebBrowser::GetWindow(__RPC__deref_out_opt HWND* phwnd) {
+ *phwnd = mHwndParent;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::ContextSensitiveHelp(BOOL fEnterMode) {
+ // We don't provide context-sensitive help.
+ return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IOleInPlaceSite
+//////////////////////////////////////////////////////////////////////////////
+
+HRESULT STDMETHODCALLTYPE WebBrowser::CanInPlaceActivate() {
+ // We always support in-place activation.
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceActivate() {
+ OleLockRunning(mOleObject, TRUE, FALSE);
+ mOleObject->QueryInterface(&mOleInPlaceObject);
+ mOleInPlaceObject->QueryInterface(&mOleInPlaceActiveObject);
+ mOleInPlaceObject->SetObjectRects(&mRect, &mRect);
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnUIActivate() {
+ // Nothing to do before activating the control's UI.
+ 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) {
+ *ppFrame = nullptr;
+ *ppDoc = nullptr;
+ *lprcPosRect = mRect;
+ *lprcClipRect = mRect;
+
+ lpFrameInfo->fMDIApp = false;
+ lpFrameInfo->hwndFrame = mHwndParent;
+ lpFrameInfo->haccel = nullptr;
+ lpFrameInfo->cAccelEntries = 0;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::Scroll(SIZE scrollExtant) {
+ // We should have disabled all scrollbars.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnUIDeactivate(BOOL fUndoable) {
+ // Nothing to do after deactivating the control's UI.
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnInPlaceDeactivate() {
+ if (mOleInPlaceObject) {
+ mOleInPlaceObject->Release();
+ mOleInPlaceObject = nullptr;
+ }
+
+ return S_OK;
+}
+
+// We don't support the concept of undo.
+HRESULT STDMETHODCALLTYPE WebBrowser::DiscardUndoState() { return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE WebBrowser::DeactivateAndUndo() { return E_NOTIMPL; }
+
+// We don't support moving or resizing the control.
+HRESULT STDMETHODCALLTYPE
+WebBrowser::OnPosRectChange(__RPC__in LPCRECT lprcPosRect) {
+ return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IOleClientSite
+//////////////////////////////////////////////////////////////////////////////
+// We don't need anything that IOleClientSite does, because we're doing OLE
+// only in the most basic sense and we don't support linking (or, indeed,
+// embedding), but some implementation of this interface is required for
+// OleCreate to work, so we have to have a stub version.
+
+HRESULT STDMETHODCALLTYPE WebBrowser::SaveObject() { return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE
+WebBrowser::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker,
+ __RPC__deref_out_opt IMoniker** ppmk) {
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+WebBrowser::GetContainer(__RPC__deref_out_opt IOleContainer** ppContainer) {
+ *ppContainer = nullptr;
+ return E_NOINTERFACE;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::ShowObject() { return S_OK; }
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OnShowWindow(BOOL fShow) { return S_OK; }
+
+HRESULT STDMETHODCALLTYPE WebBrowser::RequestNewObjectLayout() {
+ return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IDropTarget
+//////////////////////////////////////////////////////////////////////////////
+// This is a stub implementation which blocks all dropping. The main reason we
+// want to do that is prevent accidentally dropping something on the control
+// and having it navigate, because there's no recovering from that except to
+// restart the app, and also it would look ridiculous. There could also be
+// security implications though, which we'd rather just avoid engaging with
+// altogether if we can.
+
+HRESULT STDMETHODCALLTYPE
+WebBrowser::DragEnter(__RPC__in_opt IDataObject* pDataObj, DWORD grfKeyState,
+ POINTL pt, __RPC__inout DWORD* pdwEffect) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::DragOver(DWORD grfKeyState, POINTL pt,
+ __RPC__inout DWORD* pdwEffect) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::DragLeave() { return S_OK; }
+
+HRESULT STDMETHODCALLTYPE WebBrowser::Drop(__RPC__in_opt IDataObject* pDataObj,
+ DWORD grfKeyState, POINTL pt,
+ __RPC__inout DWORD* pdwEffect) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IStorage
+//////////////////////////////////////////////////////////////////////////////
+// We don't need anything that IStorage does, but we have to pass some
+// implementation of it to OleCreate, so we need to have a stub version.
+
+HRESULT STDMETHODCALLTYPE WebBrowser::CreateStream(
+ __RPC__in_string const OLECHAR* pwcsName, DWORD grfMode, DWORD reserved1,
+ DWORD reserved2, __RPC__deref_out_opt IStream** ppstm) {
+ *ppstm = nullptr;
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE WebBrowser::OpenStream(const OLECHAR* pwcsName,
+ void* reserved1, DWORD grfMode,
+ DWORD reserved2,
+ IStream** ppstm) {
+ *ppstm = nullptr;
+ 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) {
+ *ppstg = nullptr;
+ 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() { 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;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IDocHostUIHandler
+//////////////////////////////////////////////////////////////////////////////
+// Our implementation for this interface is basically all about disabling
+// things that we don't want/need.
+
+HRESULT __stdcall WebBrowser::ShowContextMenu(DWORD dwID, POINT* ppt,
+ IUnknown* pcmdtReserved,
+ IDispatch* pdispReserved) {
+ // Returning S_OK signals that we've handled the request for a context menu
+ // (which we did, by doing nothing), so the control won't try to open one.
+ return S_OK;
+}
+
+HRESULT __stdcall WebBrowser::GetHostInfo(DOCHOSTUIINFO* pInfo) {
+ pInfo->cbSize = sizeof(DOCHOSTUIINFO);
+ pInfo->dwFlags =
+ DOCHOSTUIFLAG_DIALOG | DOCHOSTUIFLAG_DISABLE_HELP_MENU |
+ DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_SCROLL_NO |
+ DOCHOSTUIFLAG_OPENNEWWIN | DOCHOSTUIFLAG_OVERRIDEBEHAVIORFACTORY |
+ DOCHOSTUIFLAG_THEME | DOCHOSTUIFLAG_LOCAL_MACHINE_ACCESS_CHECK |
+ DOCHOSTUIFLAG_DISABLE_UNTRUSTEDPROTOCOL | DOCHOSTUIFLAG_DPI_AWARE;
+ pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
+ pInfo->pchHostCss = nullptr;
+ pInfo->pchHostNS = nullptr;
+ return S_OK;
+}
+
+HRESULT __stdcall WebBrowser::ShowUI(DWORD dwID,
+ IOleInPlaceActiveObject* pActiveObject,
+ IOleCommandTarget* pCommandTarget,
+ IOleInPlaceFrame* pFrame,
+ IOleInPlaceUIWindow* pDoc) {
+ return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::HideUI() { return E_NOTIMPL; }
+
+HRESULT __stdcall WebBrowser::UpdateUI() { return E_NOTIMPL; }
+
+HRESULT __stdcall WebBrowser::EnableModeless(BOOL fEnable) { return E_NOTIMPL; }
+
+HRESULT __stdcall WebBrowser::OnDocWindowActivate(BOOL fActivate) {
+ return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::OnFrameWindowActivate(BOOL fActivate) {
+ return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::ResizeBorder(LPCRECT prcBorder,
+ IOleInPlaceUIWindow* pUIWindow,
+ BOOL fRameWindow) {
+ return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::TranslateAccelerator(LPMSG lpMsg,
+ const GUID* pguidCmdGroup,
+ DWORD nCmdID) {
+ return S_FALSE;
+}
+
+HRESULT __stdcall WebBrowser::GetOptionKeyPath(LPOLESTR* pchKey, DWORD dw) {
+ return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::GetDropTarget(IDropTarget* pDropTarget,
+ IDropTarget** ppDropTarget) {
+ // The IDropTarget implementation that we need is an empty stub, so we'll do
+ // the easy and convenient thing and just use this object.
+ return QueryInterface(IID_PPV_ARGS(ppDropTarget));
+}
+
+HRESULT __stdcall WebBrowser::GetExternal(IDispatch** ppDispatch) {
+ // This object has to implement IDispatch anyway so that we can use
+ // DISPID_AMBIENT_DLCONTROL, so we'll make this the external handler also.
+ return QueryInterface(IID_PPV_ARGS(ppDispatch));
+}
+
+HRESULT __stdcall WebBrowser::TranslateUrl(DWORD dwTranslate, LPWSTR pchURLIn,
+ LPWSTR* ppchURLOut) {
+ *ppchURLOut = nullptr;
+ return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::FilterDataObject(IDataObject* pDO,
+ IDataObject** ppDORet) {
+ *ppDORet = nullptr;
+ return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IDocHostShowUI
+//////////////////////////////////////////////////////////////////////////////
+
+HRESULT __stdcall WebBrowser::ShowMessage(HWND hwnd, LPOLESTR lpstrText,
+ LPOLESTR lpstrCaption, DWORD dwType,
+ LPOLESTR lpstrHelpFile,
+ DWORD dwHelpContext,
+ LRESULT* plResult) {
+ // Don't allow MSHTML to generate message boxes.
+ return S_OK;
+}
+
+HRESULT __stdcall WebBrowser::ShowHelp(HWND hwnd, LPOLESTR pszHelpFile,
+ UINT uCommand, DWORD dwData,
+ POINT ptMouse,
+ IDispatch* pDispatchObjectHit) {
+ // Don't allow MSHTML to show any help.
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IDispatch
+//////////////////////////////////////////////////////////////////////////////
+
+// We're not using a type library.
+HRESULT __stdcall WebBrowser::GetTypeInfoCount(UINT* pctinfo) {
+ if (pctinfo) {
+ *pctinfo = 0;
+ }
+ return S_OK;
+}
+
+HRESULT __stdcall WebBrowser::GetTypeInfo(UINT iTInfo, LCID lcid,
+ ITypeInfo** ppTInfo) {
+ return E_NOTIMPL;
+}
+
+HRESULT __stdcall WebBrowser::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames,
+ UINT cNames, LCID lcid,
+ DISPID* rgDispId) {
+ if (cNames != 1) {
+ return E_NOTIMPL;
+ }
+
+ for (size_t i = 0; i < mCustomFunctions.size(); ++i) {
+ if (mCustomFunctions[i].mName == rgszNames[0]) {
+ // DISPID values need to be 1-indexed because 0 is reserved
+ // (DISPID_VALUE).
+ *rgDispId = i + 1;
+ return S_OK;
+ }
+ }
+
+ *rgDispId = DISPID_UNKNOWN;
+ return DISP_E_UNKNOWNNAME;
+}
+
+HRESULT __stdcall WebBrowser::Invoke(DISPID dispIdMember, REFIID riid,
+ LCID lcid, WORD wFlags,
+ DISPPARAMS* pDispParams,
+ VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
+ UINT* puArgErr) {
+ if (dispIdMember == DISPID_AMBIENT_DLCONTROL && pVarResult) {
+ VariantClear(pVarResult);
+ pVarResult->vt = VT_I4;
+ // As a light security measure, disable a bunch of stuff we don't want
+ // to be able to run in the web control.
+ pVarResult->intVal = DLCTL_NO_JAVA | DLCTL_NO_DLACTIVEXCTLS |
+ DLCTL_NO_RUNACTIVEXCTLS | DLCTL_NO_FRAMEDOWNLOAD |
+ DLCTL_NO_BEHAVIORS | DLCTL_NO_CLIENTPULL |
+ DLCTL_NOFRAMES | DLCTL_FORCEOFFLINE | DLCTL_SILENT |
+ DLCTL_OFFLINE | DLCTL_DLIMAGES;
+ return S_OK;
+ }
+
+ // Otherwise this should be one of our custom functions.
+ // We only support invoking these as methods, not property access.
+ if ((wFlags & DISPATCH_METHOD) == 0) {
+ return DISP_E_TYPEMISMATCH;
+ }
+
+ // Make sure this DISPID is valid in our custom functions list.
+ // DISPID values are 1-indexed because 0 is reserved (DISPID_VALUE).
+ DISPID customFunctionIndex = dispIdMember - 1;
+ if (customFunctionIndex < 0 ||
+ customFunctionIndex >= (DISPID)mCustomFunctions.size()) {
+ return DISP_E_MEMBERNOTFOUND;
+ }
+
+ // If the caller passed an argument to this custom function, use it.
+ // If not, make an empty VARIANT we can pass to it instead.
+ VARIANT argument;
+ VariantInit(&argument);
+ if (pDispParams->cArgs > 0) {
+ argument = pDispParams->rgvarg[0];
+ }
+
+ CustomFunctionRecord foundFunction = mCustomFunctions[customFunctionIndex];
+ foundFunction.mFunction(foundFunction.mArg, argument, pVarResult);
+
+ return S_OK;
+}