Source code of Windows XP (NT5)
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.

309 lines
9.2 KiB

  1. #include "shellprv.h"
  2. #include "clsobj.h"
  3. #include "shobjidl.h"
  4. HRESULT CoMarshallToCmdLine(REFIID riid, IUnknown *punk, LPTSTR pszCmdLine, UINT cch);
  5. HRESULT CoUnmarshalFromCmdLine(LPCTSTR pszCmdLine, REFIID riid, void **ppv);
  6. class CHWShellExecute : public IHWEventHandler
  7. {
  8. public:
  9. // IUnknown methods
  10. STDMETHODIMP QueryInterface(REFIID, void **);
  11. STDMETHODIMP_(ULONG) AddRef(void);
  12. STDMETHODIMP_(ULONG) Release(void);
  13. // IHWEventHandler methods
  14. STDMETHODIMP Initialize(LPCWSTR pszParams);
  15. STDMETHODIMP HandleEvent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType);
  16. STDMETHODIMP HandleEventWithContent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType,
  17. LPCWSTR pszContentTypeHandler, IDataObject* pdtobj);
  18. protected:
  19. CHWShellExecute();
  20. ~CHWShellExecute();
  21. friend HRESULT CHWShellExecute_CreateInstance(IUnknown* pUnkOuter,
  22. REFIID riid, void **ppv);
  23. private:
  24. LONG _cRef;
  25. LPWSTR _pszParams;
  26. };
  27. CHWShellExecute::CHWShellExecute() : _cRef(1)
  28. {
  29. DllAddRef();
  30. }
  31. CHWShellExecute::~CHWShellExecute()
  32. {
  33. CoTaskMemFree(_pszParams);
  34. DllRelease();
  35. }
  36. STDAPI CHWShellExecute_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppv)
  37. {
  38. HRESULT hr = E_OUTOFMEMORY;
  39. *ppv = NULL;
  40. // aggregation checking is handled in class factory
  41. CHWShellExecute* pHWShellExecute = new CHWShellExecute();
  42. if (pHWShellExecute)
  43. {
  44. hr = pHWShellExecute->QueryInterface(riid, ppv);
  45. pHWShellExecute->Release();
  46. }
  47. return hr;
  48. }
  49. // IUnknown
  50. STDMETHODIMP CHWShellExecute::QueryInterface(REFIID riid, void **ppv)
  51. {
  52. static const QITAB qit[] =
  53. {
  54. QITABENT(CHWShellExecute, IHWEventHandler),
  55. { 0 },
  56. };
  57. return QISearch(this, qit, riid, ppv);
  58. }
  59. STDMETHODIMP_(ULONG) CHWShellExecute::AddRef()
  60. {
  61. return InterlockedIncrement(&_cRef);
  62. }
  63. STDMETHODIMP_(ULONG) CHWShellExecute::Release()
  64. {
  65. if (InterlockedDecrement(&_cRef))
  66. return _cRef;
  67. delete this;
  68. return 0;
  69. }
  70. // IHWEventHandler
  71. STDMETHODIMP CHWShellExecute::Initialize(LPCWSTR pszParams)
  72. {
  73. ASSERT(NULL == _pszParams);
  74. return SHStrDup(pszParams, &_pszParams);
  75. }
  76. STDMETHODIMP CHWShellExecute::HandleEvent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType)
  77. {
  78. return HandleEventWithContent(pszDeviceID, pszAltDeviceID, pszEventType, NULL, NULL);
  79. }
  80. void ExpandArg(LPCTSTR pszArg, LPCTSTR pszName, LPTSTR pszArgs, UINT cch)
  81. {
  82. if (pszArg)
  83. {
  84. LPTSTR pszFiles = StrStrI(pszArgs, pszName);
  85. if (pszFiles)
  86. {
  87. StrCpyN(pszFiles, pszArg, cch - (int)(pszFiles - pszArgs));
  88. }
  89. }
  90. }
  91. // pszDeviceID == \\?\STORAGE#RemoveableMedia#9&16...
  92. // pszAltDeviceID == "F:\" (if the device is storage)
  93. STDMETHODIMP CHWShellExecute::HandleEventWithContent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID,
  94. LPCWSTR pszEventType, LPCWSTR pszContentTypeHandler,
  95. IDataObject* pdtobj)
  96. {
  97. HRESULT hr;
  98. if (_pszParams)
  99. {
  100. // make copy of _pszParams to make sure we don't mess up our state
  101. // when we parse the params into the parts
  102. TCHAR szApp[MAX_PATH + MAX_PATH], szArgs[INTERNET_MAX_URL_LENGTH];
  103. StrCpyN(szApp, _pszParams, ARRAYSIZE(szApp));
  104. // this code is a generic dispatcher of the data object to apps
  105. // those that need to work over a potentially large set of file names
  106. PathSeperateArgs(szApp, szArgs);
  107. if (pdtobj)
  108. {
  109. #if DEBUG
  110. TCHAR szText[1024];
  111. if (SUCCEEDED(CoMarshallToCmdLine(IID_IDataObject, pdtobj, szText, ARRAYSIZE(szText))))
  112. {
  113. IDataObject *pdtobjNew;
  114. if (SUCCEEDED(CoUnmarshalFromCmdLine(szText, IID_PPV_ARG(IDataObject, &pdtobjNew))))
  115. {
  116. pdtobjNew->Release();
  117. }
  118. }
  119. #endif
  120. // here we convert the data object into a cmd line form
  121. // there are 2 ways we do that now...
  122. //
  123. // %Files% - gives all of the data object files expanded on the cmd line
  124. // %DataObject% - marshaled data object on cmd line
  125. LPTSTR pszFiles = StrStrI(szArgs, TEXT("%Files%"));
  126. if (NULL == pszFiles)
  127. pszFiles = StrStrI(szArgs, TEXT("%F:")); // old syntax support
  128. if (pszFiles)
  129. {
  130. *pszFiles = 0; // start empty
  131. UINT cch = (UINT)(ARRAYSIZE(szArgs) - (pszFiles - szArgs));
  132. // this expands all of the file names into a cmd line
  133. // lets hope we don't have too many files as this has a fixed
  134. // length buffer
  135. STGMEDIUM medium = {0};
  136. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  137. hr = pdtobj->GetData(&fmte, &medium);
  138. if (SUCCEEDED(hr))
  139. {
  140. TCHAR szPath[MAX_PATH];
  141. for (int i = 0; DragQueryFile((HDROP)medium.hGlobal, i, szPath, ARRAYSIZE(szPath)); i++)
  142. {
  143. StrCatBuff(pszFiles, TEXT("\""), cch);
  144. StrCatBuff(pszFiles, szPath, cch);
  145. StrCatBuff(pszFiles, TEXT("\" "), cch);
  146. }
  147. ReleaseStgMedium(&medium);
  148. }
  149. }
  150. else
  151. {
  152. // prefered way to do this, this convert the data object into a
  153. // marshaled cmd line that we can pass all of the files through
  154. pszFiles = StrStrI(szArgs, TEXT("%DataObject%"));
  155. if (pszFiles)
  156. {
  157. CoMarshallToCmdLine(IID_IDataObject, pdtobj, pszFiles, (UINT)(ARRAYSIZE(szArgs) - (pszFiles - szArgs)));
  158. }
  159. }
  160. }
  161. #if 0
  162. ExpandArg(pszDeviceID, TEXT("%DeviceID%"), szArgs, ARRAYSIZE(szArgs));
  163. ExpandArg(pszEventType, TEXT("%EventType%"), szArgs, ARRAYSIZE(szArgs));
  164. ExpandArg(pszContentTypeHandler, TEXT("%ContentTypeHandler%"), szArgs, ARRAYSIZE(szArgs));
  165. ExpandArg(pszAltDeviceID, TEXT("%AltDeviceID%"), szArgs, ARRAYSIZE(szArgs));
  166. #endif
  167. // special case if app is empty and there is a "alt device" (file system root)
  168. // this must be "Open Folder" mode
  169. if ((0 == szApp[0]) && pszAltDeviceID)
  170. {
  171. StrCpyN(szApp, pszAltDeviceID, ARRAYSIZE(szApp)); // "F:\"
  172. }
  173. if (szApp[0])
  174. {
  175. SHELLEXECUTEINFO ei = {0};
  176. ei.cbSize = sizeof(ei);
  177. ei.lpFile = szApp; // we have an app name
  178. ei.lpParameters = szArgs; // and maybe some args
  179. ei.nShow = SW_SHOW;
  180. ei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_DOENVSUBST;
  181. hr = ShellExecuteEx(&ei) ? S_OK : E_FAIL;
  182. }
  183. else
  184. {
  185. hr = E_FAIL;
  186. }
  187. }
  188. else
  189. {
  190. hr = E_UNEXPECTED;
  191. }
  192. return hr;
  193. }
  194. HRESULT CoMarshallToCmdLine(REFIID riid, IUnknown *punk, LPTSTR pszCmdLine, UINT cch)
  195. {
  196. *pszCmdLine = 0;
  197. IStream *pstm;
  198. HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &pstm);
  199. if (SUCCEEDED(hr))
  200. {
  201. hr = CoMarshalInterface(pstm, riid, punk, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
  202. if (SUCCEEDED(hr))
  203. {
  204. IStream_Reset(pstm);
  205. char buf[255]; // big enough for a standard marshall record
  206. ULONG cb;
  207. hr = pstm->Read(buf, sizeof(buf), &cb);
  208. if (SUCCEEDED(hr))
  209. {
  210. StrCatBuff(pszCmdLine, TEXT("/DataObject:"), cch);
  211. pszCmdLine += lstrlen(pszCmdLine);
  212. // convert binary buffer to hex
  213. for (ULONG i = 0; i < cb; i++)
  214. {
  215. *pszCmdLine++ = 'A' + (0x0F & buf[i]);
  216. *pszCmdLine++ = 'A' + ((0xF0 & buf[i]) >> 4);
  217. }
  218. *pszCmdLine = 0;
  219. }
  220. }
  221. pstm->Release();
  222. }
  223. return hr;
  224. }
  225. HRESULT CoUnmarshalFromCmdLine(LPCTSTR pszCmdLine, REFIID riid, void **ppv)
  226. {
  227. HRESULT hr = E_FAIL;
  228. *ppv = NULL;
  229. pszCmdLine = StrStr(pszCmdLine, TEXT("/DataObject:"));
  230. if (pszCmdLine)
  231. {
  232. pszCmdLine += lstrlen(TEXT("/DataObject:"));
  233. char buf[255]; // big enough for standard marshall buffer (which is 68 bytes)
  234. for (ULONG cb = 0; *pszCmdLine && (cb < sizeof(buf)); cb++)
  235. {
  236. buf[cb] = (*pszCmdLine - 'A') + ((*(pszCmdLine + 1) - 'A') << 4);
  237. if (*(pszCmdLine + 1))
  238. pszCmdLine += 2;
  239. else
  240. break; // odd # of chars in cmd line, error
  241. }
  242. if (cb < sizeof(buf))
  243. {
  244. IStream *pstm;
  245. hr = CreateStreamOnHGlobal(NULL, TRUE, &pstm);
  246. if (SUCCEEDED(hr))
  247. {
  248. // fill the marshall stream
  249. pstm->Write(buf, cb, NULL);
  250. // move back to start of stream
  251. IStream_Reset(pstm);
  252. hr = CoUnmarshalInterface(pstm, riid, ppv);
  253. pstm->Release();
  254. }
  255. }
  256. }
  257. return hr;
  258. }