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.

1032 lines
32 KiB

  1. #include "shole.h"
  2. #include "ids.h"
  3. #ifdef SAVE_OBJECTDESCRIPTOR
  4. extern "C" const WCHAR c_wszDescriptor[] = WSTR_SCRAPITEM L"ODS";
  5. HRESULT Scrap_SaveODToStream(IStorage *pstgDoc, OBJECTDESCRIPTOR * pods)
  6. {
  7. //
  8. // According to Anthony Kitowicz, we must clear this flag.
  9. //
  10. pods->dwStatus &= ~OLEMISC_CANLINKBYOLE1;
  11. IStream *pstm;
  12. HRESULT hres = pstgDoc->CreateStream(c_wszDescriptor, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &pstm);
  13. if (SUCCEEDED(hres))
  14. {
  15. ULONG cbWritten;
  16. hres = pstm->Write(pods, pods->cbSize, &cbWritten);
  17. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_SOD descriptor written (%x, %d, %d)"),
  18. hres, pods->cbSize, cbWritten);
  19. pstm->Release();
  20. if (FAILED(hres) || cbWritten<pods->cbSize) {
  21. pstgDoc->DestroyElement(c_wszDescriptor);
  22. }
  23. }
  24. else
  25. {
  26. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_SOD pstg->CreateStream failed (%x)"), hres);
  27. }
  28. return hres;
  29. }
  30. HRESULT Scrap_SaveObjectDescriptor(IStorage *pstgDoc, IDataObject *pdtobj, IPersistStorage *pps, BOOL fLink)
  31. {
  32. STGMEDIUM medium;
  33. FORMATETC fmte = {fLink ? CF_LINKSRCDESCRIPTOR : CF_OBJECTDESCRIPTOR, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  34. HRESULT hres = pdtobj->GetData(&fmte, &medium);
  35. if (hres == S_OK)
  36. {
  37. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_SOD found CF_OBJECTDESCRIPTOR (%x)"), medium.hGlobal);
  38. LPOBJECTDESCRIPTOR pods = (LPOBJECTDESCRIPTOR)GlobalLock(medium.hGlobal);
  39. if (pods)
  40. {
  41. hres = Scrap_SaveODToStream(pstgDoc, pods);
  42. GlobalUnlock(medium.hGlobal);
  43. }
  44. ReleaseStgMedium(&medium);
  45. }
  46. //
  47. // Attempt to create object descriptor
  48. //
  49. #if 0
  50. else
  51. {
  52. hres = E_FAIL;
  53. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_SOD CAN'T find CF_OBJECTDESCRIPTOR (%x)"), hres);
  54. LPOLEOBJECT pole;
  55. if (SUCCEEDED(pps->QueryInterface(IID_IOleObject, (LPVOID*)&pole)))
  56. {
  57. IMalloc *pmem;
  58. if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pmem)))
  59. {
  60. LPWSTR pwsz;
  61. if (SUCCEEDED(pole->GetUserType(USERCLASSTYPE_FULL, &pwsz)))
  62. {
  63. UINT cbStr = pmem->GetSize(pwsz) + 1;
  64. UINT cb = SIZEOF(OBJECTDESCRIPTOR) + cbStr;
  65. LPOBJECTDESCRIPTOR pods=(LPOBJECTDESCRIPTOR)LocalAlloc(LPTR, cb);
  66. if (pods)
  67. {
  68. pods->cbSize = cb;
  69. pole->GetUserClassID(&pods->clsid);
  70. pole->GetMiscStatus(DVASPECT_CONTENT, &pods->dwStatus);
  71. pole->GetExtent(DVASPECT_CONTENT, &pods->sizel);
  72. pods->dwFullUserTypeName = SIZEOF(OBJECTDESCRIPTOR);
  73. CopyMemory(pods+1, pwsz, cbStr);
  74. hres = Scrap_SaveODToStream(pstgDoc, pods);
  75. }
  76. pmem->Free(pwsz);
  77. }
  78. pmem->Release();
  79. }
  80. pole->Release();
  81. }
  82. }
  83. #endif
  84. return hres;
  85. }
  86. #else
  87. #define Scrap_SaveObjectDescriptor(pstgDoc, pdtobj, fLink) (0)
  88. #endif // SAVE_OBJECTDESCRIPTOR
  89. #ifdef FIX_ROUNDTRIP
  90. extern "C" const TCHAR c_szCLSID[] = TEXT("CLSID");
  91. //
  92. // This function opens the HKEY for the specified CLSID or its sub-key.
  93. //
  94. // Parameters:
  95. // rclsid -- Specifies the CLSID
  96. // pszSubKey -- Specifies the subkey name, may be NULL
  97. //
  98. // Returns:
  99. // non-NULL, if succeeded; the caller must RegCloseKey it.
  100. // NULL, if failed.
  101. //
  102. HKEY _OpenCLSIDKey(REFCLSID rclsid, LPCTSTR pszSubKey)
  103. {
  104. #ifdef UNICODE
  105. WCHAR szCLSID[256];
  106. if (StringFromGUID2(rclsid, szCLSID, ARRAYSIZE(szCLSID)))
  107. {
  108. #else
  109. WCHAR wszCLSID[256];
  110. if (StringFromGUID2(rclsid, wszCLSID, ARRAYSIZE(wszCLSID)))
  111. {
  112. TCHAR szCLSID[80];
  113. WideCharToMultiByte(CP_ACP, 0, wszCLSID, -1, szCLSID, ARRAYSIZE(szCLSID), NULL, NULL);
  114. #endif
  115. TCHAR szKey[256];
  116. if (pszSubKey) {
  117. wsprintf(szKey, TEXT("%s\\%s\\%s"), c_szCLSID, szCLSID, pszSubKey);
  118. } else {
  119. wsprintf(szKey, TEXT("%s\\%s"), c_szCLSID, szCLSID);
  120. }
  121. DebugMsg(DM_TRACE, TEXT("sc TR - _OpelCLSIDKey RegOpenKey(%s)"), szKey);
  122. HKEY hkey;
  123. if (RegOpenKey(HKEY_CLASSES_ROOT, szKey, &hkey)==ERROR_SUCCESS)
  124. {
  125. return hkey;
  126. }
  127. }
  128. return NULL;
  129. }
  130. extern "C" const WCHAR c_wszFormatNames[] = WSTR_SCRAPITEM L"FMT";
  131. #define CCH_FORMATNAMES (ARRAYSIZE(c_wszFormatNames)-1)
  132. //
  133. // This function generates the stream name (UNICODE) for the spcified
  134. // clipboard format.
  135. //
  136. // Parameters:
  137. // pszFormat -- Specifies the clipboard format ("#0"-"#15" for predefined ones)
  138. // wszStreamName -- Specifies the UNICODE buffer.
  139. // cchmax -- Specifies the size of buffer.
  140. //
  141. void _GetCacheStreamName(LPCTSTR pszFormat, LPWSTR wszStreamName, UINT cchMax)
  142. {
  143. CopyMemory(wszStreamName, c_wszFormatNames, SIZEOF(c_wszFormatNames));
  144. #ifdef UNICODE
  145. lstrcpyn(wszStreamName+CCH_FORMATNAMES,pszFormat,cchMax-CCH_FORMATNAMES);
  146. #ifdef DEBUG
  147. DebugMsg(DM_TRACE, TEXT("sc TR _GetCacheStreamName returning %s"), wszStreamName);
  148. #endif
  149. #else
  150. MultiByteToWideChar(CP_ACP, 0, pszFormat, -1,
  151. wszStreamName+CCH_FORMATNAMES,
  152. cchMax-CCH_FORMATNAMES);
  153. #ifdef DEBUG
  154. TCHAR szT[256];
  155. WideCharToMultiByte(CP_ACP, 0, wszStreamName, -1, szT, ARRAYSIZE(szT), NULL, NULL);
  156. DebugMsg(DM_TRACE, TEXT("sc TR _GetCacheStreamName returning %s"), szT);
  157. #endif
  158. #endif
  159. }
  160. HRESULT Scrap_CacheOnePictureFormat(LPCTSTR pszFormat, FORMATETC * pfmte, STGMEDIUM * pmedium, REFCLSID rclsid, LPSTORAGE pstgDoc, LPDATAOBJECT pdtobj)
  161. {
  162. LPPERSISTSTORAGE ppstg;
  163. HRESULT hres = OleCreateDefaultHandler(rclsid, NULL, IID_IPersistStorage, (LPVOID *)&ppstg);
  164. DebugMsg(DM_TRACE, TEXT("sc TR Scrap_CacheOPF OleCreteDefHandler returned %x"), hres);
  165. if (SUCCEEDED(hres))
  166. {
  167. //
  168. // Generate the stream name based on the clipboard format name.
  169. //
  170. WCHAR wszStorageName[256];
  171. _GetCacheStreamName(pszFormat, wszStorageName, ARRAYSIZE(wszStorageName));
  172. LPSTORAGE pstgPicture;
  173. hres = pstgDoc->CreateStorage(wszStorageName, STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
  174. 0, 0, &pstgPicture);
  175. if (SUCCEEDED(hres))
  176. {
  177. ppstg->InitNew(pstgPicture);
  178. LPOLECACHE pcache;
  179. hres = ppstg->QueryInterface(IID_IOleCache, (LPVOID*)&pcache);
  180. DebugMsg(DM_TRACE, TEXT("sc TR Scrap_CacheOPF QI returned %x"), hres);
  181. if (SUCCEEDED(hres))
  182. {
  183. hres = pcache->Cache(pfmte, ADVF_PRIMEFIRST, NULL);
  184. DebugMsg(DM_TRACE, TEXT("sc TR pcache->Cache returned %x"), hres);
  185. hres = pcache->SetData(pfmte, pmedium, FALSE);
  186. DebugMsg(DM_TRACE, TEXT("sc TR pcache->SetData returned %x"), hres);
  187. pcache->Release();
  188. if (SUCCEEDED(hres))
  189. {
  190. hres = OleSave(ppstg, pstgPicture, TRUE);
  191. DebugMsg(DM_TRACE, TEXT("sc TR Scrap_CacheOnePictureFormat OleSave returned (%x)"), hres);
  192. ppstg->HandsOffStorage();
  193. if (SUCCEEDED(hres))
  194. {
  195. hres = pstgPicture->Commit(STGC_OVERWRITE);
  196. DebugMsg(DM_TRACE, TEXT("sc TR Scrap_CacheOnePictureFormat Commit() returned (%x)"), hres);
  197. }
  198. }
  199. }
  200. pstgPicture->Release();
  201. if (FAILED(hres))
  202. {
  203. pstgDoc->DestroyElement(wszStorageName);
  204. }
  205. }
  206. ppstg->Release();
  207. }
  208. return hres;
  209. }
  210. //
  211. // This function stores the specified format of clipboard data.
  212. //
  213. // Parameters:
  214. // pszFormat -- Specifies the clipboard format ("#0"-"#15" for predefined ones)
  215. // pstgDoc -- Specifies the top level IStorage.
  216. // pdtobj -- Sepcified the data object we should get the data from.
  217. //
  218. HRESULT Scrap_CacheOneFormat(LPCTSTR pszFormat, LPSTORAGE pstgDoc, LPDATAOBJECT pdtobj)
  219. {
  220. UINT cf = RegisterClipboardFormat(pszFormat);
  221. STGMEDIUM medium;
  222. FORMATETC fmte = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  223. const CLSID * pclsid = NULL;
  224. switch(cf)
  225. {
  226. case CF_METAFILEPICT:
  227. pclsid = &CLSID_Picture_Metafile;
  228. fmte.tymed = TYMED_MFPICT;
  229. break;
  230. case CF_ENHMETAFILE:
  231. pclsid = &CLSID_Picture_EnhMetafile;
  232. fmte.tymed = TYMED_ENHMF;
  233. break;
  234. case CF_PALETTE:
  235. case CF_BITMAP:
  236. pclsid = &CLSID_Picture_Dib;
  237. fmte.tymed = TYMED_GDI;
  238. break;
  239. }
  240. //
  241. // Get the specified format of data (TYMED_GLOBAL only)
  242. //
  243. HRESULT hres = pdtobj->GetData(&fmte, &medium);
  244. if (hres == S_OK)
  245. {
  246. if (medium.tymed != TYMED_HGLOBAL)
  247. {
  248. hres = Scrap_CacheOnePictureFormat(pszFormat, &fmte, &medium, *pclsid, pstgDoc, pdtobj);
  249. }
  250. else
  251. {
  252. //
  253. // Global lock the data.
  254. //
  255. UINT cbData = (UINT)GlobalSize(medium.hGlobal);
  256. const BYTE * pbData = (const BYTE*)GlobalLock(medium.hGlobal);
  257. if (pbData)
  258. {
  259. //
  260. // Generate the stream name based on the clipboard format name.
  261. //
  262. WCHAR wszStreamName[256];
  263. _GetCacheStreamName(pszFormat, wszStreamName, ARRAYSIZE(wszStreamName));
  264. //
  265. // Create the stream.
  266. //
  267. LPSTREAM pstm;
  268. hres = pstgDoc->CreateStream(wszStreamName, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &pstm);
  269. if (SUCCEEDED(hres))
  270. {
  271. //
  272. // Save the size of data.
  273. //
  274. ULONG cbWritten;
  275. hres = pstm->Write(&cbData, SIZEOF(cbData), &cbWritten);
  276. if (SUCCEEDED(hres) && cbWritten==SIZEOF(cbData))
  277. {
  278. //
  279. // Save the data itself.
  280. //
  281. hres = pstm->Write(pbData, cbData, &cbWritten);
  282. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_Save %s written (%x, %d, %d)"),
  283. pszFormat, hres, cbData, cbWritten);
  284. }
  285. pstm->Release();
  286. //
  287. // If anything goes wrong, destroy the stream.
  288. //
  289. if (FAILED(hres) || cbWritten<cbData)
  290. {
  291. pstgDoc->DestroyElement(wszStreamName);
  292. hres = E_FAIL;
  293. }
  294. }
  295. GlobalUnlock(medium.hGlobal);
  296. }
  297. else
  298. {
  299. hres = E_FAIL;
  300. }
  301. }
  302. ReleaseStgMedium(&medium);
  303. }
  304. else
  305. {
  306. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CacheOneFormat IDO::GetData(cf=%x,tm=%x) failed (%x)"),
  307. fmte.cfFormat, fmte.tymed, hres);
  308. }
  309. return hres;
  310. }
  311. //
  312. // This function caches the specified format of data if the data object
  313. // support that format.
  314. //
  315. // Parameters:
  316. // szFormat -- Specifies the format to be cahced
  317. // pstgDoc -- Specifies the top level IStorage
  318. // pdtobj -- Specifies the data object from where we get data
  319. // pstm -- Specifies the stream we should write cached format name.
  320. //
  321. // Returns:
  322. // TRUE if the data object support it.
  323. //
  324. BOOL Scrap_MayCacheOneFormat(LPCTSTR szFormat, LPSTORAGE pstgDoc, LPDATAOBJECT pdtobj, LPSTREAM pstm)
  325. {
  326. //
  327. // Try to cache the format.
  328. //
  329. HRESULT hres = Scrap_CacheOneFormat(szFormat, pstgDoc, pdtobj);
  330. if (SUCCEEDED(hres))
  331. {
  332. //
  333. // Store the name of format only if we actually
  334. // succeeded to cache the data.
  335. //
  336. #ifdef UNICODE
  337. CHAR szAnsiFormat[128];
  338. WideCharToMultiByte(CP_ACP, 0,
  339. szFormat, -1,
  340. szAnsiFormat, ARRAYSIZE(szAnsiFormat),
  341. NULL, NULL );
  342. USHORT cb = (USHORT)lstrlenA(szAnsiFormat);
  343. #else
  344. USHORT cb = (USHORT)lstrlen(szFormat);
  345. #endif
  346. pstm->Write(&cb, SIZEOF(cb), NULL);
  347. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_MayCacheOneFormat writing %s, %d"), szFormat, cb);
  348. #ifdef UNICODE
  349. pstm->Write(szAnsiFormat, cb, NULL);
  350. #else
  351. pstm->Write(szFormat, cb, NULL);
  352. #endif
  353. return TRUE;
  354. }
  355. return FALSE;
  356. }
  357. //
  358. // Returns:
  359. // TRUE, if the specified format is already cached (from Global list).
  360. //
  361. BOOL Scrap_IsAlreadyCached(UINT acf[], UINT ccf, LPCTSTR szFormat)
  362. {
  363. if (ccf)
  364. {
  365. UINT cf = RegisterClipboardFormat(szFormat);
  366. for (UINT icf=0; icf<ccf; icf++) {
  367. if (acf[icf]==cf) {
  368. return TRUE;
  369. }
  370. }
  371. }
  372. return FALSE;
  373. }
  374. extern "C" const TCHAR c_szCacheFMT[] = TEXT("DataFormats\\PriorityCacheFormats");
  375. #define REGSTR_PATH_SCRAP TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\ShellScrap")
  376. extern "C" const TCHAR c_szGlobalCachedFormats[] = REGSTR_PATH_SCRAP TEXT("\\PriorityCacheFormats");
  377. //
  378. // This function enumerate the list of to-be-cached clipboard data and
  379. // calls Scrap_CacheOneFormat for each of them.
  380. //
  381. // Parameters:
  382. // pstgDoc -- Specifies the top level IStorage.
  383. // pdtobj -- Specifies the data object we'll get the data from.
  384. // pps -- Specifies the "embedded object" (to get CLSID from)
  385. //
  386. void Scrap_CacheClipboardData(LPSTORAGE pstgDoc, LPDATAOBJECT pdtobj, LPPERSIST pps)
  387. {
  388. //
  389. // Create the stream where we'll store the list of actually
  390. // cached formats, which might be just a subset of to-be-cached
  391. // format specified in the registry.
  392. //
  393. LPSTREAM pstm;
  394. HRESULT hres = pstgDoc->CreateStream(c_wszFormatNames, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &pstm);
  395. DebugMsg(DM_TRACE, TEXT("sc TR S_CCD CreateStream returned %x"), hres);
  396. if (SUCCEEDED(hres))
  397. {
  398. USHORT cb;
  399. HKEY hkey;
  400. TCHAR szFormatName[128];
  401. DWORD cchValueName;
  402. DWORD dwType;
  403. UINT acf[CCF_CACHE_GLOBAL];
  404. UINT ccf = 0;
  405. //
  406. // First, try the formats in the global list.
  407. //
  408. if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szGlobalCachedFormats, &hkey)==ERROR_SUCCESS)
  409. {
  410. //
  411. // For each global to-be-cached format...
  412. //
  413. for(int iValue=0; iValue<CCF_CACHE_GLOBAL ;iValue++)
  414. {
  415. //
  416. // Get the value name of the iValue'th value. The value
  417. // name specifies the clipboard format.
  418. // ("#0"-"#15" for predefined formats).
  419. //
  420. cchValueName = ARRAYSIZE(szFormatName);
  421. if (RegEnumValue(hkey, iValue, szFormatName, &cchValueName, NULL,
  422. &dwType, NULL, NULL)==ERROR_SUCCESS)
  423. {
  424. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CacheClipboardData found with %s, %x (Global)"), szFormatName, dwType);
  425. if (Scrap_MayCacheOneFormat(szFormatName, pstgDoc, pdtobj, pstm))
  426. {
  427. acf[ccf++] = RegisterClipboardFormat(szFormatName);
  428. }
  429. }
  430. else
  431. {
  432. break;
  433. }
  434. }
  435. RegCloseKey(hkey);
  436. }
  437. //
  438. // Then, try the CLSID specific formats.
  439. //
  440. // Get the CLSID of the "embedded object" (the body of scrap)
  441. //
  442. CLSID clsid;
  443. hres = pps->GetClassID(&clsid);
  444. if (SUCCEEDED(hres))
  445. {
  446. //
  447. // Open the key for the list of to-be-cached formats.
  448. //
  449. hkey = _OpenCLSIDKey(clsid, c_szCacheFMT);
  450. if (hkey)
  451. {
  452. //
  453. // For each class specific to-be-cached format...
  454. //
  455. for(int iValue=0; iValue<CCF_CACHE_CLSID ;iValue++)
  456. {
  457. //
  458. // Get the value name of the iValue'th value. The value
  459. // name specifies the clipboard format.
  460. // ("#0"-"#15" for predefined formats).
  461. //
  462. cchValueName = ARRAYSIZE(szFormatName);
  463. if (RegEnumValue(hkey, iValue, szFormatName, &cchValueName, NULL,
  464. &dwType, NULL, NULL)==ERROR_SUCCESS)
  465. {
  466. if (!Scrap_IsAlreadyCached(acf, ccf, szFormatName))
  467. {
  468. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CacheClipboardData found with %s, %x (CLSID specific)"), szFormatName, dwType);
  469. Scrap_MayCacheOneFormat(szFormatName, pstgDoc, pdtobj, pstm);
  470. }
  471. else
  472. {
  473. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CacheClipboardData skipping %s (already cached)"), szFormatName);
  474. }
  475. }
  476. else
  477. {
  478. break;
  479. }
  480. }
  481. //
  482. // HACK: NT 3.5's RegEdit does not support named values...
  483. //
  484. for(iValue=0; iValue<CCF_CACHE_CLSID ;iValue++)
  485. {
  486. TCHAR szKeyName[128];
  487. if (RegEnumKey(hkey, iValue, szKeyName, ARRAYSIZE(szKeyName))==ERROR_SUCCESS)
  488. {
  489. LONG cbValue = ARRAYSIZE(szFormatName);
  490. if ((RegQueryValue(hkey, szKeyName, szFormatName, &cbValue)==ERROR_SUCCESS) && cbValue)
  491. {
  492. if (!Scrap_IsAlreadyCached(acf, ccf, szFormatName))
  493. {
  494. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CacheClipboardData found with %s, %x (CLSID specific)"), szFormatName, dwType);
  495. Scrap_MayCacheOneFormat(szFormatName, pstgDoc, pdtobj, pstm);
  496. }
  497. else
  498. {
  499. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CacheClipboardData skipping %s (already cached)"), szFormatName);
  500. }
  501. }
  502. }
  503. else
  504. {
  505. break;
  506. }
  507. }
  508. RegCloseKey(hkey);
  509. }
  510. }
  511. //
  512. // Put the terminator.
  513. //
  514. cb = 0;
  515. pstm->Write(&cb, SIZEOF(cb), NULL);
  516. pstm->Release();
  517. }
  518. }
  519. #endif // FIX_ROUNDTRIP
  520. // out:
  521. // pszName - short name for object type ("Worksheet", "Word Document", etc)
  522. //
  523. // returns:
  524. //
  525. HRESULT Scrap_Save(IStorage *pstg, IStorage *pstgDoc, IDataObject *pdtobj, BOOL fLink, LPTSTR pszName)
  526. {
  527. IPersistStorage *pps;
  528. HRESULT hres;
  529. if (fLink)
  530. {
  531. FORMATETC fmte = {CF_METAFILEPICT, NULL, DVASPECT_ICON, -1, TYMED_MFPICT};
  532. hres = OleCreateLinkFromData(pdtobj, IID_IPersistStorage, OLERENDER_FORMAT,
  533. &fmte, NULL, pstg, (LPVOID*)&pps);
  534. DebugMsg(DM_TRACE, TEXT("sc Scrap_Save OleCreateLinkFromData(FMT) returned (%x)"), hres);
  535. }
  536. else
  537. {
  538. hres = OleCreateFromData(pdtobj, IID_IPersistStorage, OLERENDER_DRAW,
  539. NULL, NULL, pstg, (LPVOID*)&pps);
  540. DebugMsg(DM_TRACE, TEXT("sc Scrap_Save OleCreateFromData(FMT) returned (%x)"), hres);
  541. }
  542. if (SUCCEEDED(hres))
  543. {
  544. hres = OleSave(pps, pstg, TRUE); // fSameStorage=TRUE
  545. DebugMsg(DM_TRACE, TEXT("sc Scrap_Save OleSave returned (%x)"), hres);
  546. if (SUCCEEDED(hres) && pszName)
  547. {
  548. LPOLEOBJECT pole;
  549. if (SUCCEEDED(pps->QueryInterface(IID_IOleObject, (LPVOID*)&pole)))
  550. {
  551. IMalloc *pmem;
  552. if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pmem)))
  553. {
  554. LPWSTR pwsz;
  555. if (SUCCEEDED(pole->GetUserType(USERCLASSTYPE_SHORT, &pwsz)))
  556. {
  557. #ifdef UNICODE
  558. lstrcpyn(pszName, pwsz, 64); // What is 64?
  559. #else
  560. WideCharToMultiByte(CP_ACP, 0, pwsz, -1, pszName, 64, NULL, NULL);
  561. #endif
  562. DebugMsg(DM_TRACE, TEXT("sc Scrap_Save short name (%s)"), pszName);
  563. // Assert(lstrlen(pszName) < 15); // USERCLASSTYPE_SHORT docs say so
  564. pmem->Free(pwsz);
  565. }
  566. pmem->Release();
  567. }
  568. pole->Release();
  569. }
  570. }
  571. // This is no-op if SAVE_OBJECTDESCRIPTOR is not defined.
  572. Scrap_SaveObjectDescriptor(pstgDoc, pdtobj, pps, fLink);
  573. #ifdef FIX_ROUNDTRIP
  574. if (!fLink)
  575. {
  576. Scrap_CacheClipboardData(pstgDoc, pdtobj, pps);
  577. }
  578. #endif // FIX_ROUNDTRIP
  579. hres = pps->HandsOffStorage();
  580. pps->Release();
  581. }
  582. return hres;
  583. }
  584. //
  585. // We have ANSI text but no UNICODE Text. Look for RTF in order to
  586. // see if we can find a language id so that we can use the correct
  587. // code page for the Ansi to Unicode translation.
  588. //
  589. UINT Scrap_SniffCodePage(IDataObject *pdtobj)
  590. {
  591. UINT CodePage = CP_ACP;
  592. FORMATETC fmte = { CF_RTF, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL | TYMED_ISTREAM };
  593. STGMEDIUM medium;
  594. if (SUCCEEDED(pdtobj->GetData(&fmte, &medium)))
  595. {
  596. CHAR szBuf[MAX_PATH * 8] = { 0 };
  597. LPSTR pszRTF = NULL;
  598. if (medium.tymed == TYMED_ISTREAM)
  599. {
  600. // Read one less byte to ensure proper null termination
  601. if (SUCCEEDED(medium.pstm->Read((LPVOID)szBuf, sizeof(szBuf) - 1, NULL)))
  602. {
  603. pszRTF = szBuf;
  604. }
  605. }
  606. else
  607. {
  608. pszRTF = (LPSTR)GlobalLock(medium.hGlobal);
  609. }
  610. if (pszRTF)
  611. {
  612. LPSTR pTmp;
  613. //
  614. // Find the language id used for this text.
  615. //
  616. // Ugly way to search, but can't use c-runtimes in the
  617. // shell.
  618. //
  619. CHAR szLang[5];
  620. UINT LangID = 0;
  621. pTmp = pszRTF;
  622. while (*pTmp)
  623. {
  624. if ((*pTmp == '\\') &&
  625. *(pTmp + 1) && (*(pTmp + 1) == 'l') &&
  626. *(pTmp + 2) && (*(pTmp + 2) == 'a') &&
  627. *(pTmp + 3) && (*(pTmp + 3) == 'n') &&
  628. *(pTmp + 4) && (*(pTmp + 4) == 'g'))
  629. {
  630. //
  631. // Get number following the \lang identifier.
  632. //
  633. int ctr;
  634. pTmp += 5;
  635. for (ctr = 0;
  636. (ctr < 4) && (*(pTmp + ctr)) &&
  637. ((*(pTmp + ctr)) >= '0') && ((*(pTmp + ctr)) <= '9');
  638. ctr++)
  639. {
  640. szLang[ctr] = *(pTmp + ctr);
  641. }
  642. szLang[ctr] = 0;
  643. for (pTmp = szLang; *pTmp; pTmp++)
  644. {
  645. LangID *= 10;
  646. LangID += (*pTmp - '0');
  647. }
  648. break;
  649. }
  650. pTmp++;
  651. }
  652. if (LangID)
  653. {
  654. if (!GetLocaleInfo( LangID,
  655. LOCALE_IDEFAULTANSICODEPAGE |
  656. LOCALE_RETURN_NUMBER,
  657. (LPTSTR)&CodePage,
  658. sizeof(UINT) / sizeof(TCHAR) ))
  659. {
  660. CodePage = CP_ACP;
  661. }
  662. }
  663. if (medium.tymed == TYMED_HGLOBAL)
  664. {
  665. GlobalUnlock(medium.hGlobal);
  666. }
  667. }
  668. ReleaseStgMedium(&medium);
  669. }
  670. return CodePage;
  671. }
  672. // get some text from the data object
  673. //
  674. // out:
  675. // pszOut filled in with text
  676. //
  677. HRESULT Scrap_GetText(IDataObject *pdtobj, LPTSTR pszOut, UINT cchMax)
  678. {
  679. ASSERT(cchMax > 1);
  680. UINT cbMac = (cchMax-1)*SIZEOF(pszOut[0]);
  681. ZeroMemory(pszOut, cchMax * SIZEOF(pszOut[0]));
  682. STGMEDIUM medium;
  683. HRESULT hres;
  684. #ifdef UNICODE
  685. FORMATETC fmte = { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM|TYMED_HGLOBAL };
  686. hres = pdtobj->QueryGetData( &fmte );
  687. if (hres != S_OK) // S_FALSE means no.
  688. {
  689. fmte.cfFormat = CF_TEXT;
  690. }
  691. hres = pdtobj->GetData(&fmte, &medium);
  692. #else
  693. FORMATETC fmte = { CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM|TYMED_HGLOBAL };
  694. hres = pdtobj->GetData(&fmte, &medium);
  695. #endif
  696. if (SUCCEEDED(hres))
  697. {
  698. DebugMsg(DM_TRACE, TEXT("sh TR - Scrap_GetText found CF_TEXT/CF_UNICODETEXT in %d"), medium.tymed);
  699. if (medium.tymed == TYMED_ISTREAM)
  700. {
  701. #ifdef UNICODE
  702. if (fmte.cfFormat == CF_TEXT)
  703. {
  704. // Stream is ansi but we are unicode - yuck
  705. LPSTR pAnsi = (LPSTR)LocalAlloc(LPTR, cchMax * sizeof(CHAR));
  706. if (pAnsi)
  707. {
  708. // Read one short so we are guaranteed a null terminator
  709. hres = medium.pstm->Read(pAnsi, cchMax - 1, NULL);
  710. if (SUCCEEDED(hres)) {
  711. SHAnsiToUnicodeCP(Scrap_SniffCodePage(pdtobj), pAnsi, pszOut, cchMax);
  712. }
  713. LocalFree(pAnsi);
  714. }
  715. else
  716. hres = E_OUTOFMEMORY;
  717. }
  718. else
  719. #endif
  720. hres = medium.pstm->Read(pszOut, cbMac, NULL);
  721. }
  722. else if (medium.tymed == TYMED_HGLOBAL)
  723. {
  724. DebugMsg(DM_TRACE, TEXT("sh TR - Scrap_GetText found CF_TEXT/CF_UNICODETEXT in global"));
  725. LPTSTR pszSrc = (LPTSTR)GlobalLock(medium.hGlobal);
  726. if (pszSrc)
  727. {
  728. #ifdef UNICODE
  729. if ( fmte.cfFormat == CF_TEXT )
  730. {
  731. SHAnsiToUnicodeCP(Scrap_SniffCodePage(pdtobj), (LPSTR)pszSrc, pszOut, cchMax);
  732. }
  733. else
  734. #endif
  735. {
  736. lstrcpyn(pszOut, pszSrc, cchMax);
  737. }
  738. GlobalUnlock(medium.hGlobal);
  739. }
  740. }
  741. ReleaseStgMedium(&medium);
  742. }
  743. return hres;
  744. }
  745. // Avoid linking lots of CRuntime stuff.
  746. #undef isdigit
  747. #undef isalpha
  748. #define isdigit(ch) (ch>=TEXT('0') && ch<=TEXT('9'))
  749. #define isalpha(ch) ((ch&0xdf)>=TEXT('A') && (ch&0xdf)<=TEXT('Z'))
  750. #define CCH_MAXLEADINGSPACE 256
  751. #define CCH_COPY 16
  752. //
  753. // create a fancy name for a scrap/doc shortcut given the data object to get some
  754. // text from
  755. //
  756. // out:
  757. // pszNewName - assumed to be 64 chars at least
  758. //
  759. BOOL Scrap_GetFancyName(IDataObject *pdtobj, UINT idTemplate, LPCTSTR pszPath, LPCTSTR pszTypeName, LPTSTR pszNewName)
  760. {
  761. TCHAR szText[CCH_MAXLEADINGSPACE + CCH_COPY + 1];
  762. HRESULT hres = Scrap_GetText(pdtobj, szText, ARRAYSIZE(szText));
  763. if (SUCCEEDED(hres))
  764. {
  765. #ifdef UNICODE
  766. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_GetFancyName CF_UNICODETEXT has (%s)"), szText);
  767. #else
  768. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_GetFancyName CF_TEXT has (%s)"), szText);
  769. #endif
  770. LPTSTR pszStart;
  771. //
  772. // skip leading space/non-printing characters
  773. //
  774. for (pszStart = szText; (TBYTE)*pszStart <= TEXT(' '); pszStart++)
  775. {
  776. if (*pszStart == TEXT('\0'))
  777. return FALSE; // empty string
  778. if (pszStart - szText >= CCH_MAXLEADINGSPACE)
  779. return FALSE; // too many leading space
  780. }
  781. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_GetFancyName pszStart (%s)"), pszStart);
  782. //
  783. // Chacter conversion
  784. // (1) non-printing characters -> ' '
  785. // (2) invalid characters -> '_'
  786. //
  787. for (LPTSTR pszT = pszStart; *pszT && ((pszT-pszStart) < CCH_COPY); pszT = CharNext(pszT))
  788. {
  789. TBYTE ch = (TBYTE)*pszT;
  790. if (ch <= TEXT(' '))
  791. {
  792. *pszT = TEXT(' ');
  793. }
  794. else if (ch < 127 && !isdigit(ch) && !isalpha(ch))
  795. {
  796. switch(ch)
  797. {
  798. case TEXT('$'):
  799. case TEXT('%'):
  800. case TEXT('\''):
  801. case TEXT('-'):
  802. case TEXT('_'):
  803. case TEXT('@'):
  804. case TEXT('~'):
  805. case TEXT('`'):
  806. case TEXT('!'):
  807. case TEXT('('):
  808. case TEXT(')'):
  809. case TEXT('{'):
  810. case TEXT('}'):
  811. case TEXT('^'):
  812. case TEXT('#'):
  813. case TEXT('&'):
  814. break;
  815. default:
  816. *pszT = TEXT('_');
  817. break;
  818. }
  819. }
  820. }
  821. *pszT = 0;
  822. TCHAR szTemplate[MAX_PATH];
  823. TCHAR szName[MAX_PATH];
  824. LoadString(HINST_THISDLL, idTemplate, szTemplate, ARRAYSIZE(szTemplate));
  825. wsprintf(szName, szTemplate, pszTypeName, pszStart);
  826. if (PathYetAnotherMakeUniqueName(szName, pszPath, szName, szName))
  827. {
  828. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_GetFancyName (%s)"), szName);
  829. lstrcpy(pszNewName, szName);
  830. return TRUE;
  831. }
  832. }
  833. return FALSE;
  834. }
  835. // *** WARNING ***
  836. //
  837. // Scrap_CreateFromDataObject is a TCHAR export from SHSCRAP.DLL that is used by SHELL32.DLL. If you
  838. // change its calling convention, you must modify shell32's wrapper as well as well.
  839. //
  840. // *** WARNING ***
  841. HRESULT WINAPI Scrap_CreateFromDataObject(LPCTSTR pszPath, IDataObject *pdtobj, BOOL fLink, LPTSTR pszNewFile)
  842. {
  843. HRESULT hres;
  844. TCHAR szTemplateS[32];
  845. TCHAR szTemplateL[128];
  846. TCHAR szTypeName[64];
  847. #ifndef UNICODE
  848. WCHAR wszNewFile[MAX_PATH];
  849. #endif
  850. IStorage *pstg;
  851. UINT idErr = 0;
  852. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CreateFromDataObject called at %s"), pszPath);
  853. LoadString(HINST_THISDLL, fLink ? IDS_BOOKMARK_S : IDS_SCRAP_S, szTemplateS, ARRAYSIZE(szTemplateS));
  854. LoadString(HINST_THISDLL, fLink ? IDS_BOOKMARK_L : IDS_SCRAP_L, szTemplateL, ARRAYSIZE(szTemplateL));
  855. PathYetAnotherMakeUniqueName(pszNewFile, pszPath, szTemplateS, szTemplateL);
  856. DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CreateFromDataObject creating %s"), pszNewFile);
  857. #ifdef UNICODE
  858. hres = StgCreateDocfile(pszNewFile,
  859. STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
  860. 0, &pstg);
  861. #else
  862. MultiByteToWideChar(CP_ACP, 0, pszNewFile, -1, wszNewFile, ARRAYSIZE(wszNewFile));
  863. hres = StgCreateDocfile(wszNewFile,
  864. STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
  865. 0, &pstg);
  866. #endif
  867. if (SUCCEEDED(hres))
  868. {
  869. IStorage *pstgContents;
  870. hres = pstg->CreateStorage(c_wszContents, STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
  871. 0, 0, &pstgContents);
  872. if (SUCCEEDED(hres))
  873. {
  874. hres = Scrap_Save(pstgContents, pstg, pdtobj, fLink, szTypeName);
  875. if (SUCCEEDED(hres))
  876. {
  877. hres = pstgContents->Commit(STGC_OVERWRITE);
  878. if (FAILED(hres))
  879. idErr = IDS_ERR_COMMIT;
  880. }
  881. else
  882. {
  883. idErr = IDS_ERR_SCRAPSAVE;
  884. }
  885. pstgContents->Release();
  886. }
  887. else
  888. {
  889. idErr = IDS_ERR_CREATESTORAGE;
  890. }
  891. //
  892. // We need to delete the file, if failed to save/commit.
  893. //
  894. if (SUCCEEDED(hres))
  895. {
  896. hres = pstg->Commit(STGC_OVERWRITE);
  897. if (FAILED(hres))
  898. idErr = IDS_ERR_COMMIT;
  899. }
  900. pstg->Release();
  901. if (FAILED(hres))
  902. DeleteFile(pszNewFile);
  903. }
  904. else
  905. {
  906. idErr = IDS_ERR_CREATEDOCFILE;
  907. }
  908. if (SUCCEEDED(hres))
  909. {
  910. if (IsLFNDrive(pszPath))
  911. {
  912. TCHAR szFancyName[MAX_PATH];
  913. if (Scrap_GetFancyName(pdtobj, fLink ? IDS_TEMPLINK : IDS_TEMPSCRAP, pszPath, szTypeName, szFancyName))
  914. {
  915. if (MoveFile(pszNewFile, szFancyName))
  916. lstrcpy(pszNewFile, szFancyName);
  917. }
  918. }
  919. }
  920. else
  921. {
  922. DisplayError((HWND)NULL, hres, idErr, pszNewFile);
  923. }
  924. return hres;
  925. }