You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
11 KiB
322 lines
11 KiB
#include "shellprv.h"
|
|
#include "clsobj.h"
|
|
|
|
#include "shobjidl.h"
|
|
|
|
|
|
HRESULT CoMarshallToCmdLine(REFIID riid, IUnknown *punk, LPTSTR pszCmdLine, UINT cch);
|
|
HRESULT CoUnmarshalFromCmdLine(LPCTSTR pszCmdLine, REFIID riid, void **ppv);
|
|
|
|
class CHWShellExecute : public IHWEventHandler
|
|
{
|
|
public:
|
|
// IUnknown methods
|
|
STDMETHODIMP QueryInterface(REFIID, void **);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IHWEventHandler methods
|
|
STDMETHODIMP Initialize(LPCWSTR pszParams);
|
|
STDMETHODIMP HandleEvent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType);
|
|
STDMETHODIMP HandleEventWithContent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType,
|
|
LPCWSTR pszContentTypeHandler, IDataObject* pdtobj);
|
|
|
|
protected:
|
|
CHWShellExecute();
|
|
~CHWShellExecute();
|
|
|
|
friend HRESULT CHWShellExecute_CreateInstance(IUnknown* pUnkOuter,
|
|
REFIID riid, void **ppv);
|
|
|
|
private:
|
|
LONG _cRef;
|
|
LPWSTR _pszParams;
|
|
};
|
|
|
|
CHWShellExecute::CHWShellExecute() : _cRef(1)
|
|
{
|
|
DllAddRef();
|
|
}
|
|
|
|
CHWShellExecute::~CHWShellExecute()
|
|
{
|
|
CoTaskMemFree(_pszParams);
|
|
DllRelease();
|
|
}
|
|
|
|
STDAPI CHWShellExecute_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
*ppv = NULL;
|
|
|
|
// aggregation checking is handled in class factory
|
|
CHWShellExecute* pHWShellExecute = new CHWShellExecute();
|
|
|
|
if (pHWShellExecute)
|
|
{
|
|
hr = pHWShellExecute->QueryInterface(riid, ppv);
|
|
pHWShellExecute->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// IUnknown
|
|
STDMETHODIMP CHWShellExecute::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CHWShellExecute, IHWEventHandler),
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CHWShellExecute::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CHWShellExecute::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
// IHWEventHandler
|
|
STDMETHODIMP CHWShellExecute::Initialize(LPCWSTR pszParams)
|
|
{
|
|
ASSERT(NULL == _pszParams);
|
|
return SHStrDup(pszParams, &_pszParams);
|
|
}
|
|
|
|
STDMETHODIMP CHWShellExecute::HandleEvent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType)
|
|
{
|
|
return HandleEventWithContent(pszDeviceID, pszAltDeviceID, pszEventType, NULL, NULL);
|
|
}
|
|
|
|
// pszDeviceID == \\?\STORAGE#RemoveableMedia#9&16...
|
|
// pszAltDeviceID == "F:\" (if the device is storage)
|
|
|
|
STDMETHODIMP CHWShellExecute::HandleEventWithContent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID,
|
|
LPCWSTR pszEventType, LPCWSTR pszContentTypeHandler,
|
|
IDataObject* pdtobj)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (_pszParams)
|
|
{
|
|
// make copy of _pszParams to make sure we don't mess up our state
|
|
// when we parse the params into the parts
|
|
|
|
TCHAR szApp[MAX_PATH + MAX_PATH], szArgs[INTERNET_MAX_URL_LENGTH];
|
|
hr = StringCchCopy(szApp, ARRAYSIZE(szApp), _pszParams);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// this code is a generic dispatcher of the data object to apps
|
|
// those that need to work over a potentially large set of file names
|
|
|
|
hr = PathSeperateArgs(szApp, szArgs, ARRAYSIZE(szArgs), NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (pdtobj)
|
|
{
|
|
#if DEBUG
|
|
TCHAR szText[1024];
|
|
if (SUCCEEDED(CoMarshallToCmdLine(IID_IDataObject, pdtobj, szText, ARRAYSIZE(szText))))
|
|
{
|
|
IDataObject *pdtobjNew;
|
|
if (SUCCEEDED(CoUnmarshalFromCmdLine(szText, IID_PPV_ARG(IDataObject, &pdtobjNew))))
|
|
{
|
|
pdtobjNew->Release();
|
|
}
|
|
}
|
|
#endif
|
|
// here we convert the data object into a cmd line form
|
|
// there are 2 ways we do that now...
|
|
//
|
|
// %Files% - gives all of the data object files expanded on the cmd line
|
|
// %DataObject% - marshaled data object on cmd line
|
|
|
|
LPTSTR pszFiles = StrStrI(szArgs, TEXT("%Files%"));
|
|
if (NULL == pszFiles)
|
|
pszFiles = StrStrI(szArgs, TEXT("%F:")); // old syntax support
|
|
|
|
if (pszFiles)
|
|
{
|
|
*pszFiles = 0; // start empty
|
|
UINT cch = (UINT)(ARRAYSIZE(szArgs) - (pszFiles - szArgs));
|
|
|
|
// this expands all of the file names into a cmd line
|
|
// lets hope we don't have too many files as this has a fixed
|
|
// length buffer
|
|
|
|
STGMEDIUM medium = {0};
|
|
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
hr = pdtobj->GetData(&fmte, &medium);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
for (int i = 0; DragQueryFile((HDROP)medium.hGlobal, i, szPath, ARRAYSIZE(szPath)); i++)
|
|
{
|
|
LPTSTR pszNext;
|
|
size_t cchLeft;
|
|
|
|
if (SUCCEEDED(StringCchCatEx(pszFiles, cch, TEXT("\""), &pszNext, &cchLeft, 0)))
|
|
{
|
|
if (SUCCEEDED(StringCchCopyEx(pszNext, cchLeft, szPath, &pszNext, &cchLeft, 0)))
|
|
{
|
|
if (FAILED(StringCchCopy(pszNext, cchLeft, TEXT("\" "))))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// prefered way to do this, this convert the data object into a
|
|
// marshaled cmd line that we can pass all of the files through
|
|
|
|
pszFiles = StrStrI(szArgs, TEXT("%DataObject%"));
|
|
if (pszFiles)
|
|
{
|
|
CoMarshallToCmdLine(IID_IDataObject, pdtobj, pszFiles, (UINT)(ARRAYSIZE(szArgs) - (pszFiles - szArgs)));
|
|
}
|
|
}
|
|
}
|
|
|
|
// special case if app is empty and there is a "alt device" (file system root)
|
|
// this must be "Open Folder" mode
|
|
|
|
if ((0 == szApp[0]) && pszAltDeviceID)
|
|
{
|
|
hr = StringCchCopy(szApp, ARRAYSIZE(szApp), pszAltDeviceID); // "F:\"
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (szApp[0])
|
|
{
|
|
SHELLEXECUTEINFO ei = {0};
|
|
ei.cbSize = sizeof(ei);
|
|
|
|
ei.lpFile = szApp; // we have an app name
|
|
ei.lpParameters = szArgs; // and maybe some args
|
|
ei.nShow = SW_SHOW;
|
|
ei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_DOENVSUBST;
|
|
|
|
hr = ShellExecuteEx(&ei) ? S_OK : E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CoMarshallToCmdLine(REFIID riid, IUnknown *punk, LPTSTR pszCmdLine, UINT cch)
|
|
{
|
|
*pszCmdLine = 0;
|
|
|
|
IStream *pstm;
|
|
HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &pstm);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CoMarshalInterface(pstm, riid, punk, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IStream_Reset(pstm);
|
|
|
|
char buf[255]; // big enough for a standard marshall record
|
|
ULONG cb;
|
|
hr = pstm->Read(buf, sizeof(buf), &cb);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCchCat(pszCmdLine, cch, TEXT("/DataObject:"));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pszCmdLine += lstrlen(pszCmdLine);
|
|
// convert binary buffer to hex
|
|
for (ULONG i = 0; i < cb; i++)
|
|
{
|
|
*pszCmdLine++ = 'A' + (0x0F & buf[i]);
|
|
*pszCmdLine++ = 'A' + ((0xF0 & buf[i]) >> 4);
|
|
}
|
|
*pszCmdLine = 0;
|
|
}
|
|
}
|
|
}
|
|
pstm->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CoUnmarshalFromCmdLine(LPCTSTR pszCmdLine, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
*ppv = NULL;
|
|
|
|
pszCmdLine = StrStr(pszCmdLine, TEXT("/DataObject:"));
|
|
if (pszCmdLine)
|
|
{
|
|
pszCmdLine += lstrlen(TEXT("/DataObject:"));
|
|
|
|
char buf[255]; // big enough for standard marshall buffer (which is 68 bytes)
|
|
for (ULONG cb = 0; *pszCmdLine && (cb < sizeof(buf)); cb++)
|
|
{
|
|
buf[cb] = (*pszCmdLine - 'A') + ((*(pszCmdLine + 1) - 'A') << 4);
|
|
if (*(pszCmdLine + 1))
|
|
pszCmdLine += 2;
|
|
else
|
|
break; // odd # of chars in cmd line, error
|
|
}
|
|
|
|
if (cb < sizeof(buf))
|
|
{
|
|
IStream *pstm;
|
|
hr = CreateStreamOnHGlobal(NULL, TRUE, &pstm);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// fill the marshall stream
|
|
pstm->Write(buf, cb, NULL);
|
|
|
|
// move back to start of stream
|
|
IStream_Reset(pstm);
|
|
|
|
hr = CoUnmarshalInterface(pstm, riid, ppv);
|
|
|
|
pstm->Release();
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|