368 lines
10 KiB
C++
368 lines
10 KiB
C++
|
/*
|
||
|
* urlexec.cpp - IUnknown implementation for Intshcut class.
|
||
|
*/
|
||
|
|
||
|
#include "project.hpp"
|
||
|
#include "urlshell.h"
|
||
|
#include "clsfact.h"
|
||
|
#include "resource.h"
|
||
|
|
||
|
#include <mluisupp.h>
|
||
|
|
||
|
// 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;
|
||
|
}
|