/* * urlexec.cpp - IUnknown implementation for Intshcut class. */ #include "project.hpp" #include "urlshell.h" #include "clsfact.h" #include "resource.h" #include // URL Exec Hook class CURLExec : public IShellExecuteHookA, public IShellExecuteHookW { private: ULONG m_cRef; ~CURLExec(void); // Prevent this class from being allocated on the stack or it will fault. public: CURLExec(void); // IShellExecuteHook methods // Ansi STDMETHODIMP Execute(LPSHELLEXECUTEINFOA pei); // Unicode STDMETHODIMP Execute(LPSHELLEXECUTEINFOW pei); // IUnknown methods STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppvObj); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); #ifdef DEBUG friend BOOL IsValidPCURLExec(const CURLExec * pue); #endif }; #ifdef DEBUG BOOL IsValidPCURLExec(CURLExec * pue) { return (IS_VALID_READ_PTR(pue, CURLExec)); } #endif CURLExec::CURLExec(void) : m_cRef(1) { // CURLExec objects should always be allocated ASSERT(IS_VALID_STRUCT_PTR(this, CURLExec)); DLLAddRef(); } CURLExec::~CURLExec(void) { ASSERT(IS_VALID_STRUCT_PTR(this, CURLExec)); DLLRelease(); } /*---------------------------------------------------------- Purpose: IUnknown::QueryInterface handler for CURLExec */ STDMETHODIMP CURLExec::QueryInterface(REFIID riid, PVOID *ppvObj) { if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IShellExecuteHookA)) { *ppvObj = SAFECAST(this, IShellExecuteHookA *); } else if (IsEqualIID(riid, IID_IShellExecuteHookW)) { *ppvObj = SAFECAST(this, IShellExecuteHookW *); } else { *ppvObj = NULL; return E_NOINTERFACE; } AddRef(); return NOERROR; } STDMETHODIMP_(ULONG) CURLExec::AddRef() { return ++m_cRef; } STDMETHODIMP_(ULONG) CURLExec::Release() { m_cRef--; if (m_cRef > 0) return m_cRef; delete this; return 0; } // from shlexec.c #define SEE_MASK_CLASS (SEE_MASK_CLASSNAME|SEE_MASK_CLASSKEY) /*---------------------------------------------------------- Purpose: IShellExecuteHook::Execute handler for CURLExec */ STDMETHODIMP CURLExec::Execute(LPSHELLEXECUTEINFOA pei) { HRESULT hres; ASSERT(IS_VALID_STRUCT_PTR(this, CURLExec)); ASSERT(IS_VALID_READ_PTR(pei, SHELLEXECUTEINFO)); if (! pei->lpVerb || ! lstrcmpi(pei->lpVerb, TEXT("open"))) { if (pei->lpFile) { LPTSTR pszURL; // This should succeed only for real URLs. We should fail // for file paths and let the shell handle those. hres = TranslateURL(pei->lpFile, TRANSLATEURL_FL_GUESS_PROTOCOL | TRANSLATEURL_FL_CANONICALIZE, &pszURL); if (SUCCEEDED(hres)) { LPCTSTR pszURLToUse; pszURLToUse = (hres == S_OK) ? pszURL : pei->lpFile; hres = ValidateURL(pszURLToUse); if (SUCCEEDED(hres)) { IUniformResourceLocator * purl; hres = SHCoCreateInstance(NULL, &CLSID_InternetShortcut, NULL, IID_IUniformResourceLocator, (void **)&purl); if (SUCCEEDED(hres)) { hres = purl->SetURL(pszURLToUse, 0); if (hres == S_OK) { IShellLink * psl; hres = purl->QueryInterface(IID_IShellLink, (void **)&psl); if (SUCCEEDED(hres)) { URLINVOKECOMMANDINFO urlici; EVAL(psl->SetShowCmd(pei->nShow) == S_OK); urlici.dwcbSize = SIZEOF(urlici); urlici.hwndParent = pei->hwnd; urlici.pcszVerb = NULL; urlici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB; if (IsFlagClear(pei->fMask, SEE_MASK_FLAG_NO_UI)) SetFlag(urlici.dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI); hres = purl->InvokeCommand(&urlici); if (hres != S_OK) SetFlag(pei->fMask, SEE_MASK_FLAG_NO_UI); psl->Release(); } } purl->Release(); } } if (pszURL) LocalFree(pszURL); } } else // (scotth): This hook only handles execution of file string, not IDList. hres = S_FALSE; } else // Unrecognized verb. hres = S_FALSE; if (hres == S_OK) pei->hInstApp = (HINSTANCE)42; // huh?? else if (FAILED(hres)) { switch (hres) { case URL_E_INVALID_SYNTAX: case URL_E_UNREGISTERED_PROTOCOL: hres = S_FALSE; break; case E_OUTOFMEMORY: pei->hInstApp = (HINSTANCE)SE_ERR_OOM; hres = E_FAIL; break; case IS_E_EXEC_FAILED: // Translate execution failure into "file not found". pei->hInstApp = (HINSTANCE)SE_ERR_FNF; hres = E_FAIL; break; default: // pei->lpFile is bogus. Treat as file not found. ASSERT(hres == E_POINTER); pei->hInstApp = (HINSTANCE)SE_ERR_FNF; hres = E_FAIL; break; } } else ASSERT(hres == S_FALSE); ASSERT(hres == S_OK || hres == S_FALSE || hres == E_FAIL); return hres; } STDMETHODIMP CURLExec::Execute(LPSHELLEXECUTEINFOW pei) { // thunk stuff copied from shlexec.c InvokeShellExecuteHook SHELLEXECUTEINFOA seia; UINT cchVerb = 0; UINT cchFile = 0; UINT cchParameters = 0; UINT cchDirectory = 0; UINT cchClass = 0; LPSTR lpszBuffer; HRESULT hres = E_FAIL; seia = *(SHELLEXECUTEINFOA*)pei; // Copy all of the binary data if (pei->lpVerb) { cchVerb = WideCharToMultiByte(CP_ACP,0, pei->lpVerb, -1, NULL, 0, NULL, NULL)+1; } if (pei->lpFile) cchFile = WideCharToMultiByte(CP_ACP,0, pei->lpFile, -1, NULL, 0, NULL, NULL)+1; if (pei->lpParameters) cchParameters = WideCharToMultiByte(CP_ACP,0, pei->lpParameters, -1, NULL, 0, NULL, NULL)+1; if (pei->lpDirectory) cchDirectory = WideCharToMultiByte(CP_ACP,0, pei->lpDirectory, -1, NULL, 0, NULL, NULL)+1; if (((pei->fMask & SEE_MASK_CLASS) == SEE_MASK_CLASSNAME) && pei->lpClass) cchClass = WideCharToMultiByte(CP_ACP,0, pei->lpClass, -1, NULL, 0, NULL, NULL)+1; // what is this (alloca)? InvokeShellExecuteHook is not freeing lpszBuffer //lpszBuffer = alloca(cchVerb+cchFile+cchParameters+cchDirectory+cchClass); lpszBuffer = (LPSTR)LocalAlloc(LPTR, cchVerb+cchFile+cchParameters+cchDirectory+cchClass); if (lpszBuffer) { LPSTR lpsz = lpszBuffer; seia.lpVerb = NULL; seia.lpFile = NULL; seia.lpParameters = NULL; seia.lpDirectory = NULL; seia.lpClass = NULL; // // Convert all of the strings to ANSI // if (pei->lpVerb) { WideCharToMultiByte(CP_ACP, 0, pei->lpVerb, -1, lpszBuffer, cchVerb, NULL, NULL); seia.lpVerb = lpszBuffer; lpszBuffer += cchVerb; } if (pei->lpFile) { WideCharToMultiByte(CP_ACP, 0, pei->lpFile, -1, lpszBuffer, cchFile, NULL, NULL); seia.lpFile = lpszBuffer; lpszBuffer += cchFile; } if (pei->lpParameters) { WideCharToMultiByte(CP_ACP, 0, pei->lpParameters, -1, lpszBuffer, cchParameters, NULL, NULL); seia.lpParameters = lpszBuffer; lpszBuffer += cchParameters; } if (pei->lpDirectory) { WideCharToMultiByte(CP_ACP, 0, pei->lpDirectory, -1, lpszBuffer, cchDirectory, NULL, NULL); seia.lpDirectory = lpszBuffer; lpszBuffer += cchDirectory; } if (((pei->fMask & SEE_MASK_CLASS) == SEE_MASK_CLASSNAME) && pei->lpClass) { WideCharToMultiByte(CP_ACP, 0, pei->lpClass, -1, lpszBuffer, cchClass, NULL, NULL); seia.lpClass = lpszBuffer; } hres = Execute(&seia); // now thunk the possible new stuff back pei->hInstApp = seia.hInstApp; if (pei->fMask & SEE_MASK_NOCLOSEPROCESS) pei->hProcess = seia.hProcess; LocalFree(lpsz); } return hres; } STDAPI CreateInstance_URLExec(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut) { *ppvOut = NULL; if (punkOuter) return CLASS_E_NOAGGREGATION; CURLExec *pue = new(CURLExec); if (!pue) return E_OUTOFMEMORY; HRESULT hres = pue->QueryInterface(riid, ppvOut); pue->Release(); return hres; }