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.

1023 lines
32 KiB

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