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.

957 lines
31 KiB

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