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.

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