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.

478 lines
16 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "datautil.h"
  4. #include "brfcase.h"
  5. #include "views.h"
  6. #include "fsdata.h"
  7. //
  8. // This function is called from CFSIDLData_GetData().
  9. //
  10. // Paramters:
  11. // this -- Specifies the IDLData object (selected objects)
  12. // pmedium -- Pointer to STDMEDIUM to be filled; NULL if just querying.
  13. //
  14. HRESULT CFSIDLData::_GetNetResource(STGMEDIUM *pmedium)
  15. {
  16. STGMEDIUM medium;
  17. LPIDA pida = DataObj_GetHIDA(this, &medium);
  18. if (pida)
  19. {
  20. BOOL bIsMyNet = IsIDListInNameSpace(HIDA_GetPIDLFolder(pida), &CLSID_NetworkPlaces);
  21. HIDA_ReleaseStgMedium(pida, &medium);
  22. if (!bIsMyNet)
  23. return DV_E_FORMATETC;
  24. if (!pmedium)
  25. return S_OK; // query, yes we have it
  26. return CNetData_GetNetResourceForFS(this, pmedium);
  27. }
  28. return E_FAIL;
  29. }
  30. HRESULT CFSIDLData::QueryGetData(FORMATETC *pformatetc)
  31. {
  32. if (pformatetc->tymed & TYMED_HGLOBAL)
  33. {
  34. if ((pformatetc->cfFormat == CF_HDROP) ||
  35. (pformatetc->cfFormat == g_cfFileNameA) ||
  36. (pformatetc->cfFormat == g_cfFileNameW))
  37. {
  38. return S_OK;
  39. }
  40. else if (pformatetc->cfFormat == g_cfNetResource)
  41. {
  42. return _GetNetResource(NULL);
  43. }
  44. }
  45. return CIDLDataObj::QueryGetData(pformatetc);
  46. }
  47. HRESULT CFSIDLData::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
  48. {
  49. HRESULT hr = CIDLDataObj::SetData(pformatetc, pmedium, fRelease);
  50. // this enables:
  51. // 1) in the shell "cut" some files
  52. // 2) in an app "paste" to copy the data
  53. // 3) here we complete the "cut" by deleting the files
  54. if ((pformatetc->cfFormat == g_cfPasteSucceeded) &&
  55. (pformatetc->tymed == TYMED_HGLOBAL))
  56. {
  57. DWORD *pdw = (DWORD *)GlobalLock(pmedium->hGlobal);
  58. if (pdw)
  59. {
  60. // NOTE: the old code use g_cfPerformedDropEffect == DROPEFFECT_MOVE here
  61. // so to work on downlevel shells be sure to set the "Performed Effect" before
  62. // using "Paste Succeeded".
  63. // complete the "unoptimized move"
  64. if (DROPEFFECT_MOVE == *pdw)
  65. {
  66. DeleteFilesInDataObject(NULL, CMIC_MASK_FLAG_NO_UI, this, 0);
  67. }
  68. GlobalUnlock(pmedium->hGlobal);
  69. }
  70. }
  71. return hr;
  72. }
  73. // Creates CF_HDROP clipboard format block of memory (HDROP) from HIDA in data object
  74. HRESULT CFSIDLData::CreateHDrop(STGMEDIUM *pmedium, BOOL fAltName)
  75. {
  76. ZeroMemory(pmedium, sizeof(*pmedium));
  77. HRESULT hr;
  78. STGMEDIUM medium;
  79. LPIDA pida = DataObj_GetHIDA(this, &medium);
  80. if (pida)
  81. {
  82. LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder(pida);
  83. ASSERT(pidlFolder);
  84. IShellFolder *psf;
  85. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlFolder, &psf));
  86. if (SUCCEEDED(hr))
  87. {
  88. // Allocate too much to start out with, then re-alloc when we are done
  89. UINT cbAlloc = sizeof(DROPFILES) + sizeof(TCHAR); // header + null terminator
  90. UINT cbOriginalAlloc = cbAlloc + pida->cidl * MAX_PATH * sizeof(TCHAR);
  91. pmedium->hGlobal = GlobalAlloc(GPTR, cbOriginalAlloc);
  92. if (pmedium->hGlobal)
  93. {
  94. DROPFILES *pdf = (DROPFILES *)pmedium->hGlobal;
  95. LPTSTR pszFiles = (LPTSTR)(pdf + 1);
  96. pdf->pFiles = sizeof(DROPFILES);
  97. pdf->fWide = (sizeof(TCHAR) == sizeof(WCHAR));
  98. for (UINT i = 0; i < pida->cidl; i++)
  99. {
  100. LPCITEMIDLIST pidlItem = HIDA_GetPIDLItem(pida, i);
  101. // If we run across the Desktop pidl, then punt because it's
  102. // not a file
  103. if (ILIsEmpty(pidlItem) && ILIsEmpty(pidlFolder))
  104. {
  105. hr = DV_E_CLIPFORMAT; // No hdrop for you!
  106. break;
  107. }
  108. ASSERT(ILIsEmpty(_ILNext(pidlItem)) || ILIsEmpty(pidlFolder)); // otherwise GDNO will fail
  109. TCHAR szPath[MAX_PATH];
  110. hr = DisplayNameOf(psf, pidlItem, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath));
  111. if (FAILED(hr))
  112. break; // something bad happened
  113. if (fAltName)
  114. GetShortPathName(szPath, szPath, ARRAYSIZE(szPath));
  115. int cch = lstrlen(szPath) + 1;
  116. // prevent buffer overrun
  117. if ((LPBYTE)(pszFiles + cch) > ((LPBYTE)pmedium->hGlobal) + cbOriginalAlloc)
  118. {
  119. TraceMsg(TF_WARNING, "hdrop:%d'th file caused us to exceed allocated memory, breaking", i);
  120. break;
  121. }
  122. // we allocated MAX_PATH for each of the entries in this big double-null terminated buffer.
  123. StrCpyN(pszFiles, szPath, cch); // will write NULL terminator for us
  124. pszFiles += cch;
  125. cbAlloc += cch * sizeof(TCHAR);
  126. }
  127. if (SUCCEEDED(hr))
  128. {
  129. *pszFiles = 0; // double NULL terminate
  130. ASSERT((LPTSTR)((BYTE *)pdf + cbAlloc - sizeof(TCHAR)) == pszFiles);
  131. // re-alloc down to the amount we actually need
  132. // note that pdf and pszFiles are now both invalid (and not used anymore)
  133. pmedium->hGlobal = GlobalReAlloc(pdf, cbAlloc, GMEM_MOVEABLE);
  134. // If realloc failed, then just use the original buffer. It's
  135. // a bit wasteful of memory but it's all we've got.
  136. if (!pmedium->hGlobal)
  137. pmedium->hGlobal = (HGLOBAL)pdf;
  138. pmedium->tymed = TYMED_HGLOBAL;
  139. }
  140. else
  141. {
  142. GlobalFree(pmedium->hGlobal);
  143. pmedium->hGlobal = NULL;
  144. }
  145. }
  146. else
  147. hr = E_OUTOFMEMORY;
  148. psf->Release();
  149. }
  150. HIDA_ReleaseStgMedium(pida, &medium);
  151. }
  152. else
  153. hr = E_FAIL;
  154. return hr;
  155. }
  156. // Attempt to get the HDrop format: Create one from the HIDA if necessary
  157. HRESULT CFSIDLData::GetHDrop(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
  158. {
  159. STGMEDIUM tempmedium;
  160. HRESULT hr = CIDLDataObj::GetData(pformatetcIn, &tempmedium);
  161. if (FAILED(hr))
  162. {
  163. // Couldn't get HDROP format, create it
  164. // Set up a dummy formatetc to save in case multiple tymed's were specified
  165. FORMATETC fmtTemp = *pformatetcIn;
  166. fmtTemp.tymed = TYMED_HGLOBAL;
  167. hr = CreateHDrop(&tempmedium, pformatetcIn->dwAspect == DVASPECT_SHORTNAME);
  168. if (SUCCEEDED(hr))
  169. {
  170. // And we also want to cache this new format
  171. // .. Ensure that we actually free the memory associated with the HDROP
  172. // when the data object destructs (pUnkForRelease = NULL)
  173. ASSERT(tempmedium.pUnkForRelease == NULL);
  174. if (SUCCEEDED(SetData(&fmtTemp, &tempmedium, TRUE)))
  175. {
  176. // Now the old medium that we just set is owned by the data object - call
  177. // GetData to get a medium that is safe to release when we're done.
  178. hr = CIDLDataObj::GetData(pformatetcIn, &tempmedium);
  179. }
  180. else
  181. {
  182. TraceMsg(TF_WARNING, "Couldn't save the HDrop format to the data object - returning private version");
  183. }
  184. }
  185. }
  186. // HACKHACK
  187. // Some context menu extensions just release the hGlobal instead of
  188. // calling ReleaseStgMedium. This causes a reference counted data
  189. // object to fail. Therefore, we always allocate a new HGLOBAL for
  190. // each request. Unfortunately necessary because Quickview
  191. // Pro does this.
  192. //
  193. // Ideally we'd like to set the pUnkForRelease and not have to
  194. // dup the hGlobal each time, but alas Quickview has called our bluff
  195. // and GlobalFree()'s it.
  196. if (SUCCEEDED(hr))
  197. {
  198. if (pmedium)
  199. {
  200. *pmedium = tempmedium;
  201. pmedium->pUnkForRelease = NULL;
  202. // Make a copy of this hglobal to pass back
  203. SIZE_T cbhGlobal = GlobalSize(tempmedium.hGlobal);
  204. if (cbhGlobal)
  205. {
  206. pmedium->hGlobal = GlobalAlloc(0, (UINT)cbhGlobal);
  207. if (pmedium->hGlobal)
  208. {
  209. CopyMemory(pmedium->hGlobal, tempmedium.hGlobal, cbhGlobal);
  210. }
  211. else
  212. {
  213. hr = E_OUTOFMEMORY;
  214. }
  215. }
  216. else
  217. {
  218. hr = E_UNEXPECTED;
  219. }
  220. }
  221. else
  222. {
  223. hr = E_INVALIDARG;
  224. }
  225. ReleaseStgMedium(&tempmedium);
  226. }
  227. return hr;
  228. }
  229. // subclass member function to support CF_HDROP and CF_NETRESOURCE
  230. HRESULT CFSIDLData::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
  231. {
  232. HRESULT hr = E_INVALIDARG;
  233. // If we don't zero out the pmedium then briefcase will fault on win9x. Breifcase tries
  234. // to free this medium even if this function returns an error. Not all paths below correctly
  235. // set the pmedium in all cases.
  236. ZeroMemory(pmedium, sizeof(*pmedium));
  237. if ((pformatetcIn->cfFormat == CF_HDROP) && (pformatetcIn->tymed & TYMED_HGLOBAL))
  238. {
  239. hr = GetHDrop(pformatetcIn, pmedium);
  240. }
  241. else if ((pformatetcIn->cfFormat == g_cfFileNameA || pformatetcIn->cfFormat == g_cfFileNameW) &&
  242. (pformatetcIn->tymed & TYMED_HGLOBAL))
  243. {
  244. FORMATETC format = *pformatetcIn;
  245. BOOL bUnicode = pformatetcIn->cfFormat == g_cfFileNameW;
  246. // assume g_cfFileNameA clients want short name
  247. if (pformatetcIn->cfFormat == g_cfFileNameA)
  248. format.dwAspect = DVASPECT_SHORTNAME;
  249. STGMEDIUM medium;
  250. hr = GetHDrop(&format, &medium);
  251. if (SUCCEEDED(hr))
  252. {
  253. TCHAR szPath[MAX_PATH];
  254. if (DragQueryFile((HDROP)medium.hGlobal, 0, szPath, ARRAYSIZE(szPath)))
  255. {
  256. UINT cch = lstrlen(szPath) + 1;
  257. UINT uSize = cch * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
  258. pmedium->hGlobal = GlobalAlloc(GPTR, uSize);
  259. if (pmedium->hGlobal)
  260. {
  261. if (bUnicode)
  262. SHTCharToUnicode(szPath, (LPWSTR)pmedium->hGlobal, cch);
  263. else
  264. SHTCharToAnsi(szPath, (LPSTR)pmedium->hGlobal, cch);
  265. pmedium->tymed = TYMED_HGLOBAL;
  266. pmedium->pUnkForRelease = NULL;
  267. hr = S_OK;
  268. }
  269. else
  270. hr = E_OUTOFMEMORY;
  271. }
  272. else
  273. {
  274. hr = E_UNEXPECTED;
  275. }
  276. ReleaseStgMedium(&medium);
  277. }
  278. }
  279. else if (pformatetcIn->cfFormat == g_cfNetResource && (pformatetcIn->tymed & TYMED_HGLOBAL))
  280. {
  281. hr = _GetNetResource(pmedium);
  282. }
  283. else
  284. {
  285. hr = CIDLDataObj::GetData(pformatetcIn, pmedium);
  286. }
  287. return hr;
  288. }
  289. STDAPI SHCreateFileDataObject(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl,
  290. IDataObject *pdtInner, IDataObject **ppdtobj)
  291. {
  292. *ppdtobj = new CFSIDLData(pidlFolder, cidl, apidl, pdtInner);
  293. return *ppdtobj ? S_OK : E_OUTOFMEMORY;
  294. }
  295. /*
  296. Purpose: Gets the root path of the briefcase storage and copies
  297. it into the buffer.
  298. This function obtains the briefcase storage root by
  299. binding to an IShellFolder (briefcase) instance of the
  300. pidl. This parent is be an CFSBrfFolder *, so we can
  301. call the IBriefcaseStg::GetExtraInfo member function.
  302. Returns: standard result
  303. Cond: --
  304. */
  305. HRESULT GetBriefcaseRoot(LPCITEMIDLIST pidl, LPTSTR pszBuf, int cchBuf)
  306. {
  307. IBriefcaseStg *pbrfstg;
  308. HRESULT hr = CreateBrfStgFromIDList(pidl, NULL, &pbrfstg);
  309. if (SUCCEEDED(hr))
  310. {
  311. hr = pbrfstg->GetExtraInfo(NULL, GEI_ROOT, (WPARAM)cchBuf, (LPARAM)pszBuf);
  312. pbrfstg->Release();
  313. }
  314. return hr;
  315. }
  316. // Packages a BriefObj struct into pmedium from a HIDA.
  317. HRESULT CBriefcaseData_GetBriefObj(IDataObject *pdtobj, STGMEDIUM *pmedium)
  318. {
  319. HRESULT hr = E_OUTOFMEMORY;
  320. LPITEMIDLIST pidl = ILCreate();
  321. if (pidl)
  322. {
  323. STGMEDIUM medium;
  324. if (DataObj_GetHIDA(pdtobj, &medium))
  325. {
  326. UINT cFiles = HIDA_GetCount(medium.hGlobal);
  327. // "cFiles+1" includes the briefpath...
  328. UINT cbSize = sizeof(BriefObj) + MAX_PATH * sizeof(TCHAR) * (cFiles + 1) + 1;
  329. PBRIEFOBJ pbo = (PBRIEFOBJ)GlobalAlloc(GPTR, cbSize);
  330. if (pbo)
  331. {
  332. LPITEMIDLIST pidlT;
  333. LPTSTR pszFiles = (LPTSTR)((LPBYTE)pbo + _IOffset(BriefObj, data));
  334. pbo->cbSize = cbSize;
  335. pbo->cItems = cFiles;
  336. pbo->cbListSize = MAX_PATH * sizeof(TCHAR) * cFiles + 1;
  337. pbo->ibFileList = _IOffset(BriefObj, data);
  338. for (UINT i = 0; i < cFiles; i++)
  339. {
  340. pidlT = HIDA_FillIDList(medium.hGlobal, i, pidl);
  341. if (NULL == pidlT)
  342. break; // out of memory
  343. pidl = pidlT;
  344. SHGetPathFromIDList(pidl, pszFiles); // pszFiles has room for MAX_PATH on each file
  345. pszFiles += lstrlen(pszFiles)+1;
  346. }
  347. *pszFiles = TEXT('\0');
  348. if (i < cFiles)
  349. {
  350. // Out of memory, fail
  351. ASSERT(NULL == pidlT);
  352. }
  353. else
  354. {
  355. // Make pszFiles point to beginning of szBriefPath buffer
  356. pszFiles++;
  357. pbo->ibBriefPath = (UINT) ((LPBYTE)pszFiles - (LPBYTE)pbo);
  358. pidlT = HIDA_FillIDList(medium.hGlobal, 0, pidl);
  359. if (pidlT)
  360. {
  361. pidl = pidlT;
  362. // we have room for the briefcase path from our +1 above
  363. hr = GetBriefcaseRoot(pidl, pszFiles, MAX_PATH);
  364. pmedium->tymed = TYMED_HGLOBAL;
  365. pmedium->hGlobal = pbo;
  366. // Indicate that the caller should release hmem.
  367. pmedium->pUnkForRelease = NULL;
  368. }
  369. }
  370. }
  371. HIDA_ReleaseStgMedium(NULL, &medium);
  372. }
  373. ILFree(pidl);
  374. }
  375. return hr;
  376. }
  377. class CBriefcaseData : public CFSIDLData
  378. {
  379. public:
  380. CBriefcaseData(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[]): CFSIDLData(pidlFolder, cidl, apidl, NULL) { };
  381. // IDataObject
  382. STDMETHODIMP GetData(FORMATETC *pFmtEtc, STGMEDIUM *pstm);
  383. STDMETHODIMP QueryGetData(FORMATETC *pFmtEtc);
  384. };
  385. STDMETHODIMP CBriefcaseData::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
  386. {
  387. HRESULT hr;
  388. if (pformatetcIn->cfFormat == g_cfBriefObj && (pformatetcIn->tymed & TYMED_HGLOBAL))
  389. {
  390. hr = CBriefcaseData_GetBriefObj(this, pmedium);
  391. }
  392. else
  393. {
  394. hr = CFSIDLData::GetData(pformatetcIn, pmedium);
  395. }
  396. return hr;
  397. }
  398. // IDataObject::QueryGetData
  399. STDMETHODIMP CBriefcaseData::QueryGetData(FORMATETC *pformatetc)
  400. {
  401. if (pformatetc->cfFormat == g_cfBriefObj && (pformatetc->tymed & TYMED_HGLOBAL))
  402. return S_OK;
  403. return CFSIDLData::QueryGetData(pformatetc);
  404. }
  405. STDAPI CBrfData_CreateDataObj(LPCITEMIDLIST pidl, UINT cidl, LPCITEMIDLIST *ppidl, IDataObject **ppdtobj)
  406. {
  407. *ppdtobj = new CBriefcaseData(pidl, cidl, ppidl);
  408. return *ppdtobj ? S_OK : E_OUTOFMEMORY;
  409. }