Leaked source code of windows server 2003
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

  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. ASSERT( 0 != _cRef );
  66. ULONG cRef = InterlockedDecrement(&_cRef);
  67. if ( 0 == cRef )
  68. {
  69. delete this;
  70. }
  71. return cRef;
  72. }
  73. // IHWEventHandler
  74. STDMETHODIMP CHWShellExecute::Initialize(LPCWSTR pszParams)
  75. {
  76. ASSERT(NULL == _pszParams);
  77. return SHStrDup(pszParams, &_pszParams);
  78. }
  79. STDMETHODIMP CHWShellExecute::HandleEvent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType)
  80. {
  81. return HandleEventWithContent(pszDeviceID, pszAltDeviceID, pszEventType, NULL, NULL);
  82. }
  83. // pszDeviceID == \\?\STORAGE#RemoveableMedia#9&16...
  84. // pszAltDeviceID == "F:\" (if the device is storage)
  85. STDMETHODIMP CHWShellExecute::HandleEventWithContent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID,
  86. LPCWSTR pszEventType, LPCWSTR pszContentTypeHandler,
  87. IDataObject* pdtobj)
  88. {
  89. HRESULT hr;
  90. if (_pszParams)
  91. {
  92. // make copy of _pszParams to make sure we don't mess up our state
  93. // when we parse the params into the parts
  94. TCHAR szApp[MAX_PATH + MAX_PATH], szArgs[INTERNET_MAX_URL_LENGTH];
  95. hr = StringCchCopy(szApp, ARRAYSIZE(szApp), _pszParams);
  96. if (SUCCEEDED(hr))
  97. {
  98. // this code is a generic dispatcher of the data object to apps
  99. // those that need to work over a potentially large set of file names
  100. hr = PathSeperateArgs(szApp, szArgs, ARRAYSIZE(szArgs), NULL);
  101. if (SUCCEEDED(hr))
  102. {
  103. if (pdtobj)
  104. {
  105. #if DEBUG
  106. TCHAR szText[1024];
  107. if (SUCCEEDED(CoMarshallToCmdLine(IID_IDataObject, pdtobj, szText, ARRAYSIZE(szText))))
  108. {
  109. IDataObject *pdtobjNew;
  110. if (SUCCEEDED(CoUnmarshalFromCmdLine(szText, IID_PPV_ARG(IDataObject, &pdtobjNew))))
  111. {
  112. pdtobjNew->Release();
  113. }
  114. }
  115. #endif
  116. // here we convert the data object into a cmd line form
  117. // there are 2 ways we do that now...
  118. //
  119. // %Files% - gives all of the data object files expanded on the cmd line
  120. // %DataObject% - marshaled data object on cmd line
  121. LPTSTR pszFiles = StrStrI(szArgs, TEXT("%Files%"));
  122. if (NULL == pszFiles)
  123. pszFiles = StrStrI(szArgs, TEXT("%F:")); // old syntax support
  124. if (pszFiles)
  125. {
  126. *pszFiles = 0; // start empty
  127. UINT cch = (UINT)(ARRAYSIZE(szArgs) - (pszFiles - szArgs));
  128. // this expands all of the file names into a cmd line
  129. // lets hope we don't have too many files as this has a fixed
  130. // length buffer
  131. STGMEDIUM medium = {0};
  132. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  133. hr = pdtobj->GetData(&fmte, &medium);
  134. if (SUCCEEDED(hr))
  135. {
  136. TCHAR szPath[MAX_PATH];
  137. for (int i = 0; DragQueryFile((HDROP)medium.hGlobal, i, szPath, ARRAYSIZE(szPath)); i++)
  138. {
  139. LPTSTR pszNext;
  140. size_t cchLeft;
  141. if (SUCCEEDED(StringCchCatEx(pszFiles, cch, TEXT("\""), &pszNext, &cchLeft, 0)))
  142. {
  143. if (SUCCEEDED(StringCchCopyEx(pszNext, cchLeft, szPath, &pszNext, &cchLeft, 0)))
  144. {
  145. if (FAILED(StringCchCopy(pszNext, cchLeft, TEXT("\" "))))
  146. {
  147. break;
  148. }
  149. }
  150. else
  151. {
  152. break;
  153. }
  154. }
  155. else
  156. {
  157. break;
  158. }
  159. }
  160. ReleaseStgMedium(&medium);
  161. }
  162. }
  163. else
  164. {
  165. // prefered way to do this, this convert the data object into a
  166. // marshaled cmd line that we can pass all of the files through
  167. pszFiles = StrStrI(szArgs, TEXT("%DataObject%"));
  168. if (pszFiles)
  169. {
  170. CoMarshallToCmdLine(IID_IDataObject, pdtobj, pszFiles, (UINT)(ARRAYSIZE(szArgs) - (pszFiles - szArgs)));
  171. }
  172. }
  173. }
  174. // special case if app is empty and there is a "alt device" (file system root)
  175. // this must be "Open Folder" mode
  176. if ((0 == szApp[0]) && pszAltDeviceID)
  177. {
  178. hr = StringCchCopy(szApp, ARRAYSIZE(szApp), pszAltDeviceID); // "F:\"
  179. }
  180. if (SUCCEEDED(hr))
  181. {
  182. if (szApp[0])
  183. {
  184. SHELLEXECUTEINFO ei = {0};
  185. ei.cbSize = sizeof(ei);
  186. ei.lpFile = szApp; // we have an app name
  187. ei.lpParameters = szArgs; // and maybe some args
  188. ei.nShow = SW_SHOW;
  189. ei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_DOENVSUBST;
  190. hr = ShellExecuteEx(&ei) ? S_OK : E_FAIL;
  191. }
  192. else
  193. {
  194. hr = E_FAIL;
  195. }
  196. }
  197. }
  198. }
  199. }
  200. else
  201. {
  202. hr = E_UNEXPECTED;
  203. }
  204. return hr;
  205. }
  206. HRESULT CoMarshallToCmdLine(REFIID riid, IUnknown *punk, LPTSTR pszCmdLine, UINT cch)
  207. {
  208. *pszCmdLine = 0;
  209. IStream *pstm;
  210. HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &pstm);
  211. if (SUCCEEDED(hr))
  212. {
  213. hr = CoMarshalInterface(pstm, riid, punk, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
  214. if (SUCCEEDED(hr))
  215. {
  216. IStream_Reset(pstm);
  217. char buf[255]; // big enough for a standard marshall record
  218. ULONG cb;
  219. hr = pstm->Read(buf, sizeof(buf), &cb);
  220. if (SUCCEEDED(hr))
  221. {
  222. hr = StringCchCat(pszCmdLine, cch, TEXT("/DataObject:"));
  223. if (SUCCEEDED(hr))
  224. {
  225. pszCmdLine += lstrlen(pszCmdLine);
  226. // convert binary buffer to hex
  227. for (ULONG i = 0; i < cb; i++)
  228. {
  229. *pszCmdLine++ = 'A' + (0x0F & buf[i]);
  230. *pszCmdLine++ = 'A' + ((0xF0 & buf[i]) >> 4);
  231. }
  232. *pszCmdLine = 0;
  233. }
  234. }
  235. }
  236. pstm->Release();
  237. }
  238. return hr;
  239. }
  240. HRESULT CoUnmarshalFromCmdLine(LPCTSTR pszCmdLine, REFIID riid, void **ppv)
  241. {
  242. HRESULT hr = E_FAIL;
  243. *ppv = NULL;
  244. pszCmdLine = StrStr(pszCmdLine, TEXT("/DataObject:"));
  245. if (pszCmdLine)
  246. {
  247. pszCmdLine += lstrlen(TEXT("/DataObject:"));
  248. char buf[255]; // big enough for standard marshall buffer (which is 68 bytes)
  249. for (ULONG cb = 0; *pszCmdLine && (cb < sizeof(buf)); cb++)
  250. {
  251. buf[cb] = (*pszCmdLine - 'A') + ((*(pszCmdLine + 1) - 'A') << 4);
  252. if (*(pszCmdLine + 1))
  253. pszCmdLine += 2;
  254. else
  255. break; // odd # of chars in cmd line, error
  256. }
  257. if (cb < sizeof(buf))
  258. {
  259. IStream *pstm;
  260. hr = CreateStreamOnHGlobal(NULL, TRUE, &pstm);
  261. if (SUCCEEDED(hr))
  262. {
  263. // fill the marshall stream
  264. pstm->Write(buf, cb, NULL);
  265. // move back to start of stream
  266. IStream_Reset(pstm);
  267. hr = CoUnmarshalInterface(pstm, riid, ppv);
  268. pstm->Release();
  269. }
  270. }
  271. }
  272. return hr;
  273. }