#include "shole.h" #include "ids.h" #include #ifdef SAVE_OBJECTDESCRIPTOR extern "C" const WCHAR c_wszDescriptor[] = WSTR_SCRAPITEM L"ODS"; HRESULT Scrap_SaveODToStream(IStorage *pstgDoc, OBJECTDESCRIPTOR * pods) { // // According to Anthony Kitowicz, we must clear this flag. // pods->dwStatus &= ~OLEMISC_CANLINKBYOLE1; IStream *pstm; HRESULT hres = pstgDoc->CreateStream(c_wszDescriptor, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &pstm); if (SUCCEEDED(hres)) { ULONG cbWritten; hres = pstm->Write(pods, pods->cbSize, &cbWritten); DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_SOD descriptor written (%x, %d, %d)"), hres, pods->cbSize, cbWritten); pstm->Release(); if (FAILED(hres) || cbWrittencbSize) { pstgDoc->DestroyElement(c_wszDescriptor); } } else { DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_SOD pstg->CreateStream failed (%x)"), hres); } return hres; } HRESULT Scrap_SaveObjectDescriptor(IStorage *pstgDoc, IDataObject *pdtobj, IPersistStorage *pps, BOOL fLink) { STGMEDIUM medium; FORMATETC fmte = {fLink ? CF_LINKSRCDESCRIPTOR : CF_OBJECTDESCRIPTOR, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; HRESULT hres = pdtobj->GetData(&fmte, &medium); if (hres == S_OK) { DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_SOD found CF_OBJECTDESCRIPTOR (%x)"), medium.hGlobal); LPOBJECTDESCRIPTOR pods = (LPOBJECTDESCRIPTOR)GlobalLock(medium.hGlobal); if (pods) { hres = Scrap_SaveODToStream(pstgDoc, pods); GlobalUnlock(medium.hGlobal); } ReleaseStgMedium(&medium); } return hres; } #else #define Scrap_SaveObjectDescriptor(pstgDoc, pdtobj, fLink) (0) #endif // SAVE_OBJECTDESCRIPTOR #ifdef FIX_ROUNDTRIP extern "C" const TCHAR c_szCLSID[] = TEXT("CLSID"); // // This function opens the HKEY for the specified CLSID or its sub-key. // // Parameters: // rclsid -- Specifies the CLSID // pszSubKey -- Specifies the subkey name, may be NULL // // Returns: // non-NULL, if succeeded; the caller must RegCloseKey it. // NULL, if failed. // HKEY _OpenCLSIDKey(REFCLSID rclsid, LPCTSTR pszSubKey) { WCHAR szCLSID[256]; if (StringFromGUID2(rclsid, szCLSID, ARRAYSIZE(szCLSID))) { HRESULT hr = S_OK; TCHAR szKey[256]; if (pszSubKey) { hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\%s\\%s"), c_szCLSID, szCLSID, pszSubKey); } else { hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\%s"), c_szCLSID, szCLSID); } DebugMsg(DM_TRACE, TEXT("sc TR - _OpelCLSIDKey RegOpenKey(%s)"), szKey); if (SUCCEEDED(hr)) { HKEY hkey; if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, szKey, &hkey)) { return hkey; } } } return NULL; } extern "C" const WCHAR c_wszFormatNames[] = WSTR_SCRAPITEM L"FMT"; #define CCH_FORMATNAMES (ARRAYSIZE(c_wszFormatNames)-1) // // This function generates the stream name (UNICODE) for the spcified // clipboard format. // // Parameters: // pszFormat -- Specifies the clipboard format ("#0"-"#15" for predefined ones) // wszStreamName -- Specifies the UNICODE buffer. // cchmax -- Specifies the size of buffer. // HRESULT _GetCacheStreamName(LPCTSTR pszFormat, LPWSTR wszStreamName, UINT cchMax) { CopyMemory(wszStreamName, c_wszFormatNames, min(cchMax * sizeof(WCHAR), sizeof(c_wszFormatNames))); return StringCchCopy(wszStreamName + CCH_FORMATNAMES, max(cchMax - CCH_FORMATNAMES, 0), pszFormat); #ifdef DEBUG DebugMsg(DM_TRACE, TEXT("sc TR _GetCacheStreamName returning %s"), wszStreamName); #endif } HRESULT Scrap_CacheOnePictureFormat(LPCTSTR pszFormat, FORMATETC * pfmte, STGMEDIUM * pmedium, REFCLSID rclsid, LPSTORAGE pstgDoc, LPDATAOBJECT pdtobj) { LPPERSISTSTORAGE ppstg; HRESULT hres = OleCreateDefaultHandler(rclsid, NULL, IID_IPersistStorage, (LPVOID *)&ppstg); DebugMsg(DM_TRACE, TEXT("sc TR Scrap_CacheOPF OleCreteDefHandler returned %x"), hres); if (SUCCEEDED(hres)) { // // Generate the stream name based on the clipboard format name. // WCHAR wszStorageName[256]; hres = _GetCacheStreamName(pszFormat, wszStorageName, ARRAYSIZE(wszStorageName)); if (SUCCEEDED(hres)) { LPSTORAGE pstgPicture; hres = pstgDoc->CreateStorage(wszStorageName, STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstgPicture); if (SUCCEEDED(hres)) { ppstg->InitNew(pstgPicture); LPOLECACHE pcache; hres = ppstg->QueryInterface(IID_IOleCache, (LPVOID*)&pcache); DebugMsg(DM_TRACE, TEXT("sc TR Scrap_CacheOPF QI returned %x"), hres); if (SUCCEEDED(hres)) { hres = pcache->Cache(pfmte, ADVF_PRIMEFIRST, NULL); DebugMsg(DM_TRACE, TEXT("sc TR pcache->Cache returned %x"), hres); hres = pcache->SetData(pfmte, pmedium, FALSE); DebugMsg(DM_TRACE, TEXT("sc TR pcache->SetData returned %x"), hres); pcache->Release(); if (SUCCEEDED(hres)) { hres = OleSave(ppstg, pstgPicture, TRUE); DebugMsg(DM_TRACE, TEXT("sc TR Scrap_CacheOnePictureFormat OleSave returned (%x)"), hres); ppstg->HandsOffStorage(); if (SUCCEEDED(hres)) { hres = pstgPicture->Commit(STGC_OVERWRITE); DebugMsg(DM_TRACE, TEXT("sc TR Scrap_CacheOnePictureFormat Commit() returned (%x)"), hres); } } } pstgPicture->Release(); if (FAILED(hres)) { pstgDoc->DestroyElement(wszStorageName); } } } ppstg->Release(); } return hres; } // // This function stores the specified format of clipboard data. // // Parameters: // pszFormat -- Specifies the clipboard format ("#0"-"#15" for predefined ones) // pstgDoc -- Specifies the top level IStorage. // pdtobj -- Sepcified the data object we should get the data from. // HRESULT Scrap_CacheOneFormat(LPCTSTR pszFormat, LPSTORAGE pstgDoc, LPDATAOBJECT pdtobj) { UINT cf = RegisterClipboardFormat(pszFormat); STGMEDIUM medium; FORMATETC fmte = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; const CLSID * pclsid = NULL; switch(cf) { case CF_METAFILEPICT: pclsid = &CLSID_Picture_Metafile; fmte.tymed = TYMED_MFPICT; break; case CF_ENHMETAFILE: pclsid = &CLSID_Picture_EnhMetafile; fmte.tymed = TYMED_ENHMF; break; case CF_PALETTE: case CF_BITMAP: pclsid = &CLSID_Picture_Dib; fmte.tymed = TYMED_GDI; break; } // // Get the specified format of data (TYMED_GLOBAL only) // HRESULT hres = pdtobj->GetData(&fmte, &medium); if (hres == S_OK) { if (medium.tymed != TYMED_HGLOBAL) { hres = Scrap_CacheOnePictureFormat(pszFormat, &fmte, &medium, *pclsid, pstgDoc, pdtobj); } else { // // Global lock the data. // UINT cbData = (UINT)GlobalSize(medium.hGlobal); const BYTE * pbData = (const BYTE*)GlobalLock(medium.hGlobal); if (pbData) { // // Generate the stream name based on the clipboard format name. // WCHAR wszStreamName[256]; hres = _GetCacheStreamName(pszFormat, wszStreamName, ARRAYSIZE(wszStreamName)); if (SUCCEEDED(hres)) { // // Create the stream. // LPSTREAM pstm; hres = pstgDoc->CreateStream(wszStreamName, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &pstm); if (SUCCEEDED(hres)) { // // Save the size of data. // ULONG cbWritten; hres = pstm->Write(&cbData, SIZEOF(cbData), &cbWritten); if (SUCCEEDED(hres) && cbWritten==SIZEOF(cbData)) { // // Save the data itself. // hres = pstm->Write(pbData, cbData, &cbWritten); DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_Save %s written (%x, %d, %d)"), pszFormat, hres, cbData, cbWritten); } pstm->Release(); // // If anything goes wrong, destroy the stream. // if (FAILED(hres) || cbWrittenDestroyElement(wszStreamName); hres = E_FAIL; } } } GlobalUnlock(medium.hGlobal); } else { hres = E_FAIL; } } ReleaseStgMedium(&medium); } else { DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CacheOneFormat IDO::GetData(cf=%x,tm=%x) failed (%x)"), fmte.cfFormat, fmte.tymed, hres); } return hres; } // // This function caches the specified format of data if the data object // support that format. // // Parameters: // szFormat -- Specifies the format to be cahced // pstgDoc -- Specifies the top level IStorage // pdtobj -- Specifies the data object from where we get data // pstm -- Specifies the stream we should write cached format name. // // Returns: // TRUE if the data object support it. // BOOL Scrap_MayCacheOneFormat(LPCTSTR szFormat, LPSTORAGE pstgDoc, LPDATAOBJECT pdtobj, LPSTREAM pstm) { // // Try to cache the format. // HRESULT hres = Scrap_CacheOneFormat(szFormat, pstgDoc, pdtobj); if (SUCCEEDED(hres)) { // // Store the name of format only if we actually // succeeded to cache the data. // CHAR szAnsiFormat[128]; WideCharToMultiByte(CP_ACP, 0, szFormat, -1, szAnsiFormat, ARRAYSIZE(szAnsiFormat), NULL, NULL ); USHORT cb = (USHORT)lstrlenA(szAnsiFormat); pstm->Write(&cb, SIZEOF(cb), NULL); DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_MayCacheOneFormat writing %s, %d"), szFormat, cb); pstm->Write(szAnsiFormat, cb, NULL); return TRUE; } return FALSE; } // // Returns: // TRUE, if the specified format is already cached (from Global list). // BOOL Scrap_IsAlreadyCached(UINT acf[], UINT ccf, LPCTSTR szFormat) { if (ccf) { UINT cf = RegisterClipboardFormat(szFormat); for (UINT icf=0; icfCreateStream(c_wszFormatNames, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &pstm); DebugMsg(DM_TRACE, TEXT("sc TR S_CCD CreateStream returned %x"), hres); if (SUCCEEDED(hres)) { USHORT cb; HKEY hkey; TCHAR szFormatName[128]; DWORD cchValueName; DWORD dwType; UINT acf[CCF_CACHE_GLOBAL]; UINT ccf = 0; // // First, try the formats in the global list. // if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szGlobalCachedFormats, &hkey) == ERROR_SUCCESS) { // // For each global to-be-cached format... // for(int iValue=0; iValueGetClassID(&clsid); if (SUCCEEDED(hres)) { // // Open the key for the list of to-be-cached formats. // hkey = _OpenCLSIDKey(clsid, c_szCacheFMT); if (hkey) { // // For each class specific to-be-cached format... // for(int iValue=0; iValueWrite(&cb, SIZEOF(cb), NULL); pstm->Release(); } } #endif // FIX_ROUNDTRIP // out: // pszName - short name for object type ("Worksheet", "Word Document", etc) // // returns: // HRESULT Scrap_Save(IStorage *pstg, IStorage *pstgDoc, IDataObject *pdtobj, BOOL fLink, LPTSTR pszName) { IPersistStorage *pps; HRESULT hres; if (fLink) { FORMATETC fmte = {CF_METAFILEPICT, NULL, DVASPECT_ICON, -1, TYMED_MFPICT}; hres = OleCreateLinkFromData(pdtobj, IID_IPersistStorage, OLERENDER_FORMAT, &fmte, NULL, pstg, (LPVOID*)&pps); DebugMsg(DM_TRACE, TEXT("sc Scrap_Save OleCreateLinkFromData(FMT) returned (%x)"), hres); } else { hres = OleCreateFromData(pdtobj, IID_IPersistStorage, OLERENDER_DRAW, NULL, NULL, pstg, (LPVOID*)&pps); DebugMsg(DM_TRACE, TEXT("sc Scrap_Save OleCreateFromData(FMT) returned (%x)"), hres); } if (SUCCEEDED(hres)) { hres = OleSave(pps, pstg, TRUE); // fSameStorage=TRUE DebugMsg(DM_TRACE, TEXT("sc Scrap_Save OleSave returned (%x)"), hres); if (SUCCEEDED(hres) && pszName) { LPOLEOBJECT pole; if (SUCCEEDED(pps->QueryInterface(IID_IOleObject, (LPVOID*)&pole))) { IMalloc *pmem; if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pmem))) { LPWSTR pwsz; if (SUCCEEDED(pole->GetUserType(USERCLASSTYPE_SHORT, &pwsz))) { hres = StringCchCopy(pszName, 64, pwsz); // What is 64? DebugMsg(DM_TRACE, TEXT("sc Scrap_Save short name (%s)"), pszName); // Assert(lstrlen(pszName) < 15); // USERCLASSTYPE_SHORT docs say so pmem->Free(pwsz); } pmem->Release(); } pole->Release(); } } // This is no-op if SAVE_OBJECTDESCRIPTOR is not defined. Scrap_SaveObjectDescriptor(pstgDoc, pdtobj, pps, fLink); #ifdef FIX_ROUNDTRIP if (!fLink) { Scrap_CacheClipboardData(pstgDoc, pdtobj, pps); } #endif // FIX_ROUNDTRIP hres = pps->HandsOffStorage(); pps->Release(); } return hres; } // // We have ANSI text but no UNICODE Text. Look for RTF in order to // see if we can find a language id so that we can use the correct // code page for the Ansi to Unicode translation. // UINT Scrap_SniffCodePage(IDataObject *pdtobj) { UINT CodePage = CP_ACP; FORMATETC fmte = { CF_RTF, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL | TYMED_ISTREAM }; STGMEDIUM medium; if (SUCCEEDED(pdtobj->GetData(&fmte, &medium))) { CHAR szBuf[MAX_PATH * 8] = { 0 }; LPSTR pszRTF = NULL; if (medium.tymed == TYMED_ISTREAM) { // Read one less byte to ensure proper null termination if (SUCCEEDED(medium.pstm->Read((LPVOID)szBuf, sizeof(szBuf) - 1, NULL))) { pszRTF = szBuf; } } else { pszRTF = (LPSTR)GlobalLock(medium.hGlobal); } if (pszRTF) { LPSTR pTmp; // // Find the language id used for this text. // // Ugly way to search, but can't use c-runtimes in the // shell. // CHAR szLang[5]; UINT LangID = 0; pTmp = pszRTF; while (*pTmp) { if ((*pTmp == '\\') && *(pTmp + 1) && (*(pTmp + 1) == 'l') && *(pTmp + 2) && (*(pTmp + 2) == 'a') && *(pTmp + 3) && (*(pTmp + 3) == 'n') && *(pTmp + 4) && (*(pTmp + 4) == 'g')) { // // Get number following the \lang identifier. // int ctr; pTmp += 5; for (ctr = 0; (ctr < 4) && (*(pTmp + ctr)) && ((*(pTmp + ctr)) >= '0') && ((*(pTmp + ctr)) <= '9'); ctr++) { szLang[ctr] = *(pTmp + ctr); } szLang[ctr] = 0; for (pTmp = szLang; *pTmp; pTmp++) { LangID *= 10; LangID += (*pTmp - '0'); } break; } pTmp++; } if (LangID) { if (!GetLocaleInfo( LangID, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, (LPTSTR)&CodePage, sizeof(UINT) / sizeof(TCHAR) )) { CodePage = CP_ACP; } } if (medium.tymed == TYMED_HGLOBAL) { GlobalUnlock(medium.hGlobal); } } ReleaseStgMedium(&medium); } return CodePage; } // get some text from the data object // // out: // pszOut filled in with text // HRESULT Scrap_GetText(IDataObject *pdtobj, LPTSTR pszOut, UINT cchMax) { ASSERT(cchMax > 1); UINT cbMac = (cchMax-1)*SIZEOF(pszOut[0]); ZeroMemory(pszOut, cchMax * SIZEOF(pszOut[0])); STGMEDIUM medium; HRESULT hres; FORMATETC fmte = { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM|TYMED_HGLOBAL }; hres = pdtobj->QueryGetData( &fmte ); if (hres != S_OK) // S_FALSE means no. { fmte.cfFormat = CF_TEXT; } hres = pdtobj->GetData(&fmte, &medium); if (SUCCEEDED(hres)) { DebugMsg(DM_TRACE, TEXT("sh TR - Scrap_GetText found CF_TEXT/CF_UNICODETEXT in %d"), medium.tymed); if (medium.tymed == TYMED_ISTREAM) { if (fmte.cfFormat == CF_TEXT) { // Stream is ansi but we are unicode - yuck LPSTR pAnsi = (LPSTR)LocalAlloc(LPTR, cchMax * sizeof(CHAR)); if (pAnsi) { // Read one short so we are guaranteed a null terminator hres = medium.pstm->Read(pAnsi, cchMax - 1, NULL); if (SUCCEEDED(hres)) { SHAnsiToUnicodeCP(Scrap_SniffCodePage(pdtobj), pAnsi, pszOut, cchMax); } LocalFree(pAnsi); } else hres = E_OUTOFMEMORY; } else hres = medium.pstm->Read(pszOut, cbMac, NULL); } else if (medium.tymed == TYMED_HGLOBAL) { DebugMsg(DM_TRACE, TEXT("sh TR - Scrap_GetText found CF_TEXT/CF_UNICODETEXT in global")); LPTSTR pszSrc = (LPTSTR)GlobalLock(medium.hGlobal); if (pszSrc) { if ( fmte.cfFormat == CF_TEXT ) { SHAnsiToUnicodeCP(Scrap_SniffCodePage(pdtobj), (LPSTR)pszSrc, pszOut, cchMax); } else { hres = StringCchCopy(pszOut, cchMax, pszSrc); } GlobalUnlock(medium.hGlobal); } } ReleaseStgMedium(&medium); } return hres; } // Avoid linking lots of CRuntime stuff. #undef isdigit #undef isalpha #define isdigit(ch) (ch>=TEXT('0') && ch<=TEXT('9')) #define isalpha(ch) ((ch&0xdf)>=TEXT('A') && (ch&0xdf)<=TEXT('Z')) #define CCH_MAXLEADINGSPACE 256 #define CCH_COPY 16 // // create a fancy name for a scrap/doc shortcut given the data object to get some // text from // // out: // pszNewName - assumed to be 64 chars at least // BOOL Scrap_GetFancyName(IDataObject *pdtobj, UINT idTemplate, LPCTSTR pszPath, LPCTSTR pszTypeName, LPTSTR pszNewName, int cchNewName) { TCHAR szText[CCH_MAXLEADINGSPACE + CCH_COPY + 1]; HRESULT hres = Scrap_GetText(pdtobj, szText, ARRAYSIZE(szText)); if (SUCCEEDED(hres)) { #ifdef UNICODE DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_GetFancyName CF_UNICODETEXT has (%s)"), szText); #else DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_GetFancyName CF_TEXT has (%s)"), szText); #endif LPTSTR pszStart; // // skip leading space/non-printing characters // for (pszStart = szText; (TBYTE)*pszStart <= TEXT(' '); pszStart++) { if (*pszStart == TEXT('\0')) return FALSE; // empty string if (pszStart - szText >= CCH_MAXLEADINGSPACE) return FALSE; // too many leading space } DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_GetFancyName pszStart (%s)"), pszStart); // // Chacter conversion // (1) non-printing characters -> ' ' // (2) invalid characters -> '_' // for (LPTSTR pszT = pszStart; *pszT && ((pszT-pszStart) < CCH_COPY); pszT = CharNext(pszT)) { TBYTE ch = (TBYTE)*pszT; if (ch <= TEXT(' ')) { *pszT = TEXT(' '); } else if (ch < 127 && !isdigit(ch) && !isalpha(ch)) { switch(ch) { case TEXT('$'): case TEXT('%'): case TEXT('\''): case TEXT('-'): case TEXT('_'): case TEXT('@'): case TEXT('~'): case TEXT('`'): case TEXT('!'): case TEXT('('): case TEXT(')'): case TEXT('{'): case TEXT('}'): case TEXT('^'): case TEXT('#'): case TEXT('&'): break; default: *pszT = TEXT('_'); break; } } } *pszT = 0; TCHAR szTemplate[MAX_PATH]; TCHAR szName[MAX_PATH]; LoadString(HINST_THISDLL, idTemplate, szTemplate, ARRAYSIZE(szTemplate)); hres = StringCchPrintf(szName, ARRAYSIZE(szName), szTemplate, pszTypeName, pszStart); if (SUCCEEDED(hres) && PathYetAnotherMakeUniqueName(szName, pszPath, szName, szName)) { DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_GetFancyName (%s)"), szName); hres = StringCchCopy(pszNewName, cchNewName, szName); return SUCCEEDED(hres); } } return FALSE; } // *** WARNING *** // // Scrap_CreateFromDataObject is a TCHAR export from SHSCRAP.DLL that is used by SHELL32.DLL. If you // change its calling convention, you must modify shell32's wrapper as well as well. // // *** WARNING *** HRESULT WINAPI Scrap_CreateFromDataObject(LPCTSTR pszPath, IDataObject *pdtobj, BOOL fLink, LPTSTR pszNewFile) { HRESULT hres; TCHAR szTemplateS[32]; TCHAR szTemplateL[128]; TCHAR szTypeName[64]; IStorage *pstg; UINT idErr = 0; DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CreateFromDataObject called at %s"), pszPath); LoadString(HINST_THISDLL, fLink ? IDS_BOOKMARK_S : IDS_SCRAP_S, szTemplateS, ARRAYSIZE(szTemplateS)); LoadString(HINST_THISDLL, fLink ? IDS_BOOKMARK_L : IDS_SCRAP_L, szTemplateL, ARRAYSIZE(szTemplateL)); PathYetAnotherMakeUniqueName(pszNewFile, pszPath, szTemplateS, szTemplateL); DebugMsg(DM_TRACE, TEXT("sc TR - Scrap_CreateFromDataObject creating %s"), pszNewFile); hres = StgCreateDocfile(pszNewFile, STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, &pstg); if (SUCCEEDED(hres)) { IStorage *pstgContents; hres = pstg->CreateStorage(c_wszContents, STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstgContents); if (SUCCEEDED(hres)) { hres = Scrap_Save(pstgContents, pstg, pdtobj, fLink, szTypeName); if (SUCCEEDED(hres)) { hres = pstgContents->Commit(STGC_OVERWRITE); if (FAILED(hres)) idErr = IDS_ERR_COMMIT; } else { idErr = IDS_ERR_SCRAPSAVE; } pstgContents->Release(); } else { idErr = IDS_ERR_CREATESTORAGE; } // // We need to delete the file, if failed to save/commit. // if (SUCCEEDED(hres)) { hres = pstg->Commit(STGC_OVERWRITE); if (FAILED(hres)) idErr = IDS_ERR_COMMIT; } pstg->Release(); if (FAILED(hres)) DeleteFile(pszNewFile); } else { idErr = IDS_ERR_CREATEDOCFILE; } if (SUCCEEDED(hres)) { if (IsLFNDrive(pszPath)) { TCHAR szFancyName[MAX_PATH]; if (Scrap_GetFancyName(pdtobj, fLink ? IDS_TEMPLINK : IDS_TEMPSCRAP, pszPath, szTypeName, szFancyName, ARRAYSIZE(szFancyName))) { if (MoveFile(pszNewFile, szFancyName)) { hres = StringCchCopy(pszNewFile, MAX_PATH, szFancyName); } } } } else { DisplayError((HWND)NULL, hres, idErr, pszNewFile); } return hres; }