//--------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation 1991-1993 // // File: fstreex.c // // History: // 12-06-93 SatoNa Created. // // Notes: How IShellFolder::BindToObject in FSTREEX.C works // // IShellFolder::BindToObject(pidl, riid, ppvOut) { // if (pidl is either a folder or a juncition point) // return FSBindToObject( // (psf->lpVtbl==&c_FSBrfFolderVtbl) ? &CLSID_Briefcase : &CLSID_NULL, // this->pidl, pidl, riid, ppvOut) // else // return E_NOINTERFACE or E_INVALIARG // } // // FSBindToObject(rclsidKnown, pidlParent, pidlRefl, riid, ppvOut) { // pidlRight = next pidl of a junction point in pidlRel // if (pidlRel has no junction point in it) { // return FSRelBindToFSFolder(rclsidKnown, pidlParent, pirlRel, riid, ppvOut); // } else { // Bind to that junction point (calling FSRelVBindToFSFOlder). // Then, calls its BindToObject member. // } // } // // FSRelBindToFSFolder(rclsid, pidlParent, pidlRel, riid, ppvOut) { // simply combines pidlParent and pidlRel and // calls FSBindToFSFolder // } // // FSBindToFSFolder(rclsid, pidl, riid, ppvOut) { // if the last pidl is a junction point, create its instance. // if rclsid is CLSID_Briefcase, create its instance. // otherwise, create a scrandard FSFolder. // } // //--------------------------------------------------------------------------- #include "shellprv.h" #pragma hdrstop #include "copy.h" #ifdef USE_OLEDB #include "oledbshl.h" #endif #ifdef SYNC_BRIEFCASE #include #endif #ifdef CAIRO_DS #include #include #include "dsdata.h" #endif #include #include "bookmk.h" // in defviewx.c extern HRESULT SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage); // in netviewx.c extern HRESULT CNET_GetNetResourceForPidl(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, PVOID pv, UINT cb); extern LPTSTR NET_CopyProviderNameRelative(LPCITEMIDLIST pidlRelative, LPTSTR pszBuffer, UINT cchBuffer ); #define PROGMAN_ICON //#define FULL_DEBUG extern TCHAR const c_szShellNew[]; // = "ShellNew"; extern TCHAR const c_szShell[]; // = "shell"; TCHAR const c_szIconHandler[] = TEXT("shellex\\IconHandler"); TCHAR const c_szDataHandler[] = TEXT("shellex\\DataHandler"); TCHAR const c_szDropHandler[] = TEXT("shellex\\DropHandler"); TCHAR const c_szMenuHandlers[]= TEXT("shellex\\ContextMenuHandlers"); TCHAR const c_szShellFolder[] = TEXT("\\ShellFolder"); TCHAR const c_szCLSIDSlash[] = TEXT("CLSID\\"); TCHAR const c_szDirectoryClass[] = TEXT("Directory"); TCHAR const c_szAttributes[] = TEXT("Attributes"); TCHAR const c_szProgID[] = TEXT("ProgID"); TCHAR const c_szShellOpenCmd[] = TEXT("shell\\open\\command"); TCHAR const c_szPercentOne[] = TEXT("%1"); TCHAR const c_szPercentOneInQuotes[] = TEXT("\"%1\""); TCHAR const c_szUnknownClass[] = TEXT("Unknown"); TCHAR const c_szNoClass[] = TEXT("."); TCHAR const c_szIsLink[] = TEXT("IsShortcut"); TCHAR const c_szAlwaysShowExt[] = TEXT("AlwaysShowExt"); TCHAR const c_szNeverShowExt[] = TEXT("NeverShowExt"); #ifdef CAIRO_DS TCHAR const c_szIObjectLifecycle[] = TEXT("IObjectLifecycle"); #endif HKEY SHOpenCLSID(HKEY hkeyProgID); LPNCTSTR WINAPI SHGetClass(LPCIDFOLDER pidf, LPTSTR szClass, BOOL fGetFromStorage); BOOL CFSFolder_IsDSFolder (LPCITEMIDLIST pidl); LPCITEMIDLIST FSFindJunction(LPCITEMIDLIST pidl); LPCITEMIDLIST FSFindJunctionNext(LPCITEMIDLIST pidl); // shlexec.c extern UINT ReplaceParameters(LPTSTR lpTo, UINT cbTo, LPCTSTR lpFile, LPCTSTR lpFrom, LPCTSTR lpParms, int nShow, DWORD * pdwHotKey, BOOL fLFNAware, LPCITEMIDLIST lpID, LPITEMIDLIST *ppidlGlobal); // idldata.c HRESULT CIDLData_CloneForMoveCopy(LPDATAOBJECT pdtobjIn, LPDATAOBJECT *ppdtobjOut); HMENU g_hmenuRegMenu = NULL; // we don't get called back with the hmenu TCHAR g_szFolderTypeName[32] = TEXT(""); // Should be big enough to read into... TCHAR g_szFileTypeName[32] = TEXT(""); // (Likewise) TCHAR g_szFileTemplate[32] = TEXT(""); // (Likewise) STDMETHODIMP FS_GetDetailsOf(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS lpDetails); STDMETHODIMP FS_ColumnClick(HWND hwndMain, UINT iColumn); #ifdef CLOUDS STDMETHODIMP Clouds_CreateFromIDList(LPCITEMIDLIST, REFIID, LPVOID *); #endif void BitBucketCheckRestoredFiles(LPTSTR lpszSrc); #define MAX_CLASS 80 #ifdef UNICODE typedef LONGLONG EXTKEY; #else typedef DWORD EXTKEY; #endif // BUGBUG: Share it with DocFind struct { UINT cExts; EXTKEY ExtKeys[MAX_PATH/4]; } s_ExcludeFileExts = { 0, } ; #define FSSortIDToICol(x) ((x) - FSIDM_SORT_FIRST + FS_ICOL_NAME) COL_DATA s_fs_cols[] = { {FS_ICOL_NAME, IDS_NAME_COL, 20, LVCFMT_LEFT}, {FS_ICOL_SIZE, IDS_SIZE_COL, 10, LVCFMT_RIGHT}, {FS_ICOL_TYPE, IDS_TYPE_COL, 20, LVCFMT_LEFT}, {FS_ICOL_MODIFIED, IDS_MODIFIED_COL, 20, LVCFMT_LEFT}, {FS_ICOL_ATTRIB, IDS_ATTRIB_COL, 10, LVCFMT_RIGHT}, }; // // List of file attribute bit values. The order (with respect // to meaning) must match that of the characters in g_szAttributeChars[]. // const DWORD g_adwAttributeBits[] = { FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_COMPRESSED }; #define NUM_ATTRIB_CHARS ARRAYSIZE(g_adwAttributeBits) // // Buffer for characters that represent attributes in Details View attributes // column. Must provide room for 1 character for each bit a NUL. The current 5 // represent Read-only, Archive, Compressed, Hidden and System in that order. // This can't be const because we overwrite it using LoadString. // TCHAR g_szAttributeChars[NUM_ATTRIB_CHARS + 1] = { 0 } ; #define FS_GetType(_pidf) ((_pidf)->bFlags & SHID_FS_TYPEMASK) #define FS_IsFolder(_pidf) (FS_GetType(_pidf) == SHID_FS_DIRECTORY || FS_GetType(_pidf) == SHID_FS_DIRUNICODE) #define FS_IsFileFolder(_pidf) (FS_IsFolder(_pidf) && !FS_IsJunction(_pidf)) #define FS_IsFile(_pidf) (FS_GetType(_pidf) == SHID_FS_FILE) #define FS_IsJunction(_pidf) ((_pidf)->bFlags & SHID_JUNCTION) #define FS_GetName(_pidf) ((_pidf)->fs.cFileName) #define FS_GetUID(_pidf) ((_pidf)->fs.dwSize + ((DWORD)(_pidf)->fs.dateModified<<8) + ((DWORD)(_pidf)->fs.timeModified<<12)) #define FS_Combine(_pidl, _pidf2) \ ILCombine(_pidl, (LPITEMIDLIST)(_pidf2)) #define FS_FindLastID(_pidl) (LPIDFOLDER)ILFindLastID(_pidl) #define FS_Next(_pidf) ((LPIDFOLDER)_ILNext((LPITEMIDLIST)_pidf)) #define FS_IsEmpty(_pidf) ILIsEmpty((LPITEMIDLIST)_pidf) #define FS_IsRealID(_pidf) ((_pidf)->fs.dwSize | ((_pidf)->fs.wAttrs & FILE_ATTRIBUTE_DIRECTORY) | (_pidf)->fs.dateModified) #define GROUPOF_IDL(pidl) (SIL_GetType(pidl) & SHID_GROUPMASK) #define IS_FSIDL(pidl) (GROUPOF_IDL(pidl)==SHID_FS) #define IS_DRIVEIDL(pidl) (GROUPOF_IDL(pidl)==SHID_COMPUTER) #define IS_PATHIDL(pidl) (IS_FSIDL(pidl) || IS_DRIVEIDL(pidl)) #if (defined(DBCS) || (defined(FE_SB) && !defined(UNICODE))) // We don't want to take capital roman characters and small roman characters // in DBCS as the same because our file system doesn't. #ifdef lstrcmpi #undef lstrcmpi #endif #define lstrcmpi(lpsz1, lpsz2) lstrcmpiNoDBCS(lpsz1, lpsz2) #endif // Semi-gross, but use some of the unused bits of the wAttrs to save some state... #define FSTREEX_ATTRIBUTE_NOLFN 0x00008000 #define FSTREEX_ATTRIBUTE_MASK 0x00008000 BOOL FSGetFolderCLSID(LPCITEMIDLIST pidl, CLSID * pclsid); void FSShowNoSelectionState(HWND hwndOwner, PFSSELCHANGEINFO pfssci); BOOL FSFolder_CombinePathI(LPCIDFOLDER pidfT, LPTSTR pszPath, BOOL fAltName); void FS_GetSize(LPCITEMIDLIST pidlParent, LPIDFOLDER pidf, ULONGLONG *pcbSize) { ULONGLONG cbSize; cbSize = pidf->fs.dwSize; if (cbSize != 0xFFFFFFFF) *pcbSize = cbSize; else if (pidlParent == NULL) *pcbSize = 0; else { HANDLE hfind; ULARGE_INTEGER uli; WIN32_FIND_DATA wfd; TCHAR szPath[MAX_PATH]; // Get the real size by asking the file system SHGetPathFromIDList(pidlParent, szPath); FSFolder_CombinePathI(pidf, szPath, FALSE); // fAltName=FALSE hfind = FindFirstFileRetry(HWND_DESKTOP, szPath, &wfd, NULL); if (hfind == INVALID_HANDLE_VALUE) *pcbSize = 0; else { FindClose(hfind); uli.LowPart = wfd.nFileSizeLow; uli.HighPart = wfd.nFileSizeHigh; *pcbSize = uli.QuadPart; } } } BOOL FS_IsFolderI(LPIDFOLDER pidf) { return FS_IsFolder(pidf); } LPTSTR FS_CopyName(LPCIDFOLDER pidf, LPTSTR pszName, UINT cchName) { VDATEINPUTBUF(pszName, TCHAR, cchName); #ifdef UNICODE if ((FS_GetType(pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE) { ualstrcpynW(pszName, pidf->fs.cFileName, cchName); return pszName; } else { MultiByteToWideChar(CP_ACP, 0, ((LPIDFOLDERA)pidf)->fs.cFileName, -1, pszName, cchName); return pszName; } #else if ((FS_GetType(pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE) { return lstrcpyn(pszName, pidf->fs.cAltFileName, cchName); } else { return lstrcpyn(pszName, pidf->fs.cFileName, cchName); } #endif } LPTSTR FS_CopyAltName(LPCIDFOLDER pidf, LPTSTR pszName, UINT cchName) { UINT cbName; VDATEINPUTBUF(pszName, TCHAR, cchName); if ((FS_GetType(pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE) { cbName = (ualstrlenW(((LPIDFOLDERW)pidf)->fs.cFileName) + 1) * SIZEOF(TCHAR); } else { cbName = (lstrlenA(((LPIDFOLDERA)pidf)->fs.cFileName) + 1); } #ifdef UNICODE MultiByteToWideChar(CP_ACP, 0, ((LPIDFOLDERA)pidf)->fs.cFileName + cbName, -1, pszName, cchName); return pszName; #else return lstrcpyn(pszName, pidf->fs.cFileName + cbName, cchName); #endif } BOOL FS_ShowExtension(LPCIDFOLDER pidf, BOOL fGetFromStorage) { DWORD dwFlags; SHELLSTATE ss; dwFlags = SHGetClassFlags(pidf, fGetFromStorage); if (dwFlags & SHCF_NEVER_SHOW_EXT) return FALSE; SHGetSetSettings(&ss, SSF_SHOWEXTENSIONS, FALSE); if (ss.fShowExtensions) return TRUE; if (dwFlags & SHCF_ALWAYS_SHOW_EXT) { return TRUE; } else if (dwFlags & SHCF_UNKNOWN) { return TRUE; } return FALSE; } void FSSetStatusText(HWND hwndOwner, LPTSTR *ppszText, int iStart, int iEnd) { HWND hwndStatus = NULL; LPSHELLBROWSER psb = FileCabinet_GetIShellBrowser(hwndOwner); if (psb) { psb->lpVtbl->GetControlWindow(psb, FCW_STATUS, &hwndStatus); if (hwndStatus) { for (; iStart <= iEnd; iStart++) { LPTSTR lpsz; if (ppszText) { lpsz = *ppszText; ppszText++; } else { lpsz = (LPTSTR)c_szNULL; } #ifdef WINDOWS_ME SendMessage(hwndStatus, SB_SETTEXT, SB_RTLREADING | (WPARAM)iStart, (LPARAM)lpsz); #else SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)iStart, (LPARAM)lpsz); #endif } } } } // idDrive = Show freespace info for drive idDrive. -1 means don't show nuttin' void FSInitializeStatus(HWND hwndOwner, int idDrive, PDVSELCHANGEINFO pdvsci) { HWND hwndStatus = NULL; LPSHELLBROWSER psb = NULL; HDC hdc; int nInch; PFSSELCHANGEINFO pfssci; if (pdvsci && !*pdvsci->plParam) { // if this fails, we'll just blow off status stuff pfssci = (void*)LocalAlloc(LPTR, SIZEOF(FSSELCHANGEINFO)); *pdvsci->plParam = (LPARAM)pfssci; pfssci->idDrive = -1; // initialize this to 0, not to -1 or else we'll get the freespace twice. // at the end of the firstenumeration, we'll get an updatestatusbar message // where we'll set this to -1 //pfssci->cbFree = 0; if (hwndOwner && (idDrive != -1)) { HWND hwndTree; psb = FileCabinet_GetIShellBrowser(hwndOwner); if (SUCCEEDED(psb->lpVtbl->GetControlWindow(psb, FCW_TREE, &hwndTree)) && hwndTree) { pfssci->idDrive = idDrive; } } } if (hwndOwner) { psb = FileCabinet_GetIShellBrowser(hwndOwner); hdc = GetDC(NULL); nInch = GetDeviceCaps(hdc, LOGPIXELSX); ReleaseDC(NULL, hdc); if (psb) { #if (defined(DBCS) || (defined(FE_SB) && !defined(UNICODE))) int ciParts[] = {(nInch * 5) / 2, -1}; #else int ciParts[] = {(nInch * 3) / 2, -1}; #endif psb->lpVtbl->GetControlWindow(psb, FCW_STATUS, &hwndStatus); if (hwndStatus) { SendMessage(hwndStatus, SB_SETPARTS, ARRAYSIZE(ciParts), (LPARAM)ciParts); } if (pdvsci) FSShowNoSelectionState(hwndOwner, (PFSSELCHANGEINFO)*pdvsci->plParam); } } } // // get the type name from the registry, if the name is blank make // up a default. // // directory ==> "Folder" // foo ==> "File" // foo.xyz ==> "XYZ File" // void SHGetTypeName(LPCTSTR pszFile, HKEY hkey, BOOL fFolder, LPTSTR pszName, int cchNameMax) { ULONG cb = cchNameMax*SIZEOF(TCHAR); VDATEINPUTBUF(pszName, TCHAR, cchNameMax); if (RegQueryValue(hkey, NULL, pszName, &cb) != ERROR_SUCCESS || pszName[0] == 0) { if (fFolder) { // NOTE the registry doesn't have a name for Folder // because old apps would think it was a file type. lstrcpy(pszName, g_szFolderTypeName); } else { LPTSTR pszExt = PathFindExtension(pszFile); if (*pszExt == 0) { // Probably don't need the cchmax here, but... lstrcpyn(pszName, g_szFileTypeName, cchNameMax); // Don't copy blank.. } else { TCHAR szExt[_MAX_EXT]; int cchMaxExtCopy = min((cchNameMax-lstrlen(g_szFileTemplate)), ARRAYSIZE(szExt)); // Compose ' File' (or what ever the template defines we do. lstrcpyn(szExt, pszExt+1, cchMaxExtCopy); #if (defined(DBCS) || (defined(FE_SB) && !defined(UNICODE))) AnsiUpperNoDBCS(szExt); #else CharUpper(szExt); #endif wsprintf(pszName, g_szFileTemplate, szExt); } } } } // // return a pointer to the type name for the given PIDL // the pointer is only valid while in a critical section // LPCTSTR _GetTypeName(LPIDFOLDER pidf) { TCHAR szClass[MAX_PATH]; LPCTSTR pszClassName; TCHAR ach[MAX_CLASS]; ASSERTCRITICAL ualstrcpyn(szClass, SHGetClass(pidf, ach, FALSE), ARRAYSIZE(szClass)); pszClassName = LookupFileClassName(szClass); if (pszClassName == NULL) { HKEY hkey; SHGetClassKey(pidf, &hkey, NULL, FALSE); { TCHAR szTmp[MAX_PATH]; FS_CopyName(pidf, szTmp, ARRAYSIZE(szTmp)); SHGetTypeName(szTmp, hkey, FS_IsFolder(pidf), ach, ARRAYSIZE(ach)); } SHCloseClassKey(hkey); pszClassName = AddFileClassName(szClass, ach); } return pszClassName; } // // return the type name for the given PIDL // void FS_GetTypeName(LPIDFOLDER pidf, LPTSTR pszName, int cchNameMax) { VDATEINPUTBUF(pszName, TCHAR, cchNameMax); ENTERCRITICAL lstrcpyn(pszName, _GetTypeName(pidf), cchNameMax); LEAVECRITICAL } void BldDateTimeString(WORD wDate, WORD wTime, LPTSTR pszText) { FILETIME ft; // Netware directories do not have dates... if (wDate==0) { *pszText=TEXT('\0'); return; } DosDateTimeToFileTime(wDate, wTime, &ft); FileTimeToDateTimeString(&ft, pszText); } void FileTimeToDateTimeString(LPFILETIME lpft, LPTSTR pszText) { SYSTEMTIME st; FileTimeToLocalFileTime(lpft, lpft); FileTimeToSystemTime(lpft, &st); GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, pszText, 64); pszText += lstrlen(pszText); *pszText++ = TEXT(' '); GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, pszText, 64); } // // Build a text string containing characters that represent attributes of a file. // The attribute characters are assigned as follows: // (R)eadonly, (H)idden, (S)ystem, (A)rchive, (H)idden. // LPTSTR BuildAttributeString(DWORD dwAttributes, LPTSTR pszString, UINT nChars) { if (NULL != pszString) { int j = 0; if (nChars > NUM_ATTRIB_CHARS) { int i = 0; for (i = 0, j = 0; i < NUM_ATTRIB_CHARS; i++) if (dwAttributes & g_adwAttributeBits[i]) *(pszString + (j++)) = g_szAttributeChars[i]; } *(pszString + j) = TEXT('\0'); } return pszString; } int g_iUseLinkPrefix = -1; #define INITIALLINKPREFIXCOUNT 20 #define MAXLINKPREFIXCOUNT 30 void LoadUseLinkPrefixCount() { DebugMsg(DM_TRACE, TEXT("LoadUseLinkPrefixCount %d"), g_iUseLinkPrefix); if (g_iUseLinkPrefix < 0) { HKEY hkey; // the default g_iUseLinkPrefix = INITIALLINKPREFIXCOUNT; hkey = SHGetExplorerHkey(HKEY_CURRENT_USER, FALSE); if (hkey) { DWORD dwType; int iUseLinkPrefix; DWORD dwSize = SIZEOF(iUseLinkPrefix); // read in the registry value if ((RegQueryValueEx(hkey, c_szLink, NULL, &dwType, (LPBYTE)&iUseLinkPrefix, &dwSize) == ERROR_SUCCESS) && iUseLinkPrefix >= 0) { g_iUseLinkPrefix = iUseLinkPrefix; } } } } void SaveUseLinkPrefixCount() { if (g_iUseLinkPrefix >= 0) { HKEY hkey = SHGetExplorerHkey(HKEY_CURRENT_USER, TRUE); if (hkey) { RegSetValueEx(hkey, c_szLink, 0L, REG_BINARY, (LPBYTE)&g_iUseLinkPrefix, SIZEOF(g_iUseLinkPrefix)); } } } #define ISDIGIT(c) ((c) >= TEXT('0') && (c) <= TEXT('9')) // lpsz2 = destination // lpsz1 = source void StripNumber(LPTSTR lpsz2, LPCTSTR lpsz1) { // strip out the '(' and the numbers after it // We need to verify that it is either simply () or (999) but not (A) for (; *lpsz1; lpsz1 = CharNext(lpsz1), lpsz2 = CharNext(lpsz2)) { if (*lpsz1 == TEXT('(')) { LPCTSTR lpszT = lpsz1; do { lpsz1 = CharNext(lpsz1); } while (*lpsz1 && ISDIGIT(*lpsz1)); if (*lpsz1 == TEXT(')')) { lpsz1 = CharNext(lpsz1); if (*lpsz1 == TEXT(' ')) lpsz1 = CharNext(lpsz1); // skip the extra space lstrcpy(lpsz2, lpsz1); return; } // We have a ( that does not match the format correctly! lpsz1 = lpszT; // restore pointer back to copy this char through and continue... } *lpsz2 = *lpsz1; } *lpsz2 = *lpsz1; } #define SHORTCUT_PREFIX_DECR 5 #define SHORTCUT_PREFIX_INCR 1 // this checks to see if you've renamed 'Shortcut #x To Foo' to 'Foo' void CheckShortcutRename(LPCTSTR lpszOldPath, LPCTSTR lpszNewPath) { LPCTSTR lpszOldName = PathFindFileName(lpszOldPath); LPCTSTR lpszNewName = PathFindFileName(lpszNewPath); // already at 0. if (!g_iUseLinkPrefix) return; if (PathIsLink(lpszOldName)) { TCHAR szBaseName[MAX_PATH]; TCHAR szLinkTo[80]; TCHAR szMockName[MAX_PATH]; lstrcpy(szBaseName, lpszNewName); PathRemoveExtension(szBaseName); // mock up a name using the basename and the linkto template LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo)); wsprintf(szMockName, szLinkTo, szBaseName); StripNumber(szMockName, szMockName); StripNumber(szBaseName, lpszOldName); // are the remaining gunk the same? if (!lstrcmp(szMockName, szBaseName)) { // yes! do the link count magic LoadUseLinkPrefixCount(); Assert(g_iUseLinkPrefix >= 0); g_iUseLinkPrefix -= SHORTCUT_PREFIX_DECR; if (g_iUseLinkPrefix < 0) g_iUseLinkPrefix = 0; SaveUseLinkPrefixCount(); } } } LRESULT WINAPI SHRenameFile(HWND hwnd, LPCTSTR pszDir, LPCTSTR pszOldName, LPCTSTR pszNewName, BOOL bRetainExtension) { TCHAR szOldPathName[MAX_PATH+1]; // +1 for double nul terminating TCHAR szNewPathName[MAX_PATH+1]; // +1 for double nul terminating int iret = 0; LPTSTR lpszExt; // Don't bother if they are the same name... or any are null if (lstrcmp(pszOldName, pszNewName) == 0) return -1; // Not zero so to not to update item... //// PathCleanupSpec does this check already. No need to do it. Check //// return of PathCleanupSpec for PRC_PATHTOOLONG. -- BUGBUG // if (lstrlen(pszDir) + 1 + lstrlen(pszNewName) + 1 > MAX_PATH) // { // // The Path* functions require this limit // // BUGBUG: We should put a message in the user's face // MessageBeep(MB_ICONEXCLAMATION); // return ERROR_ACCESS_DENIED; // } lstrcpy(szOldPathName, pszNewName); if (PathCleanupSpec(pszDir, szOldPathName)) { ShellMessageBox(HINST_THISDLL, hwnd, IsLFNDrive(pszDir)? MAKEINTRESOURCE(IDS_INVALIDFN) : MAKEINTRESOURCE(IDS_INVALIDFNFAT), MAKEINTRESOURCE(IDS_RENAME), MB_OK | MB_ICONHAND); return ERROR_ACCESS_DENIED; } // We want to strip off leading and trailing blanks off of the new // file name. lstrcpy(szOldPathName, pszNewName); PathRemoveBlanks(szOldPathName); if (!szOldPathName[0] || (szOldPathName[0] == TEXT('.'))) { ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_NONULLNAME), MAKEINTRESOURCE(IDS_RENAME), MB_OK | MB_ICONHAND); return ERROR_ACCESS_DENIED; } // if there was an old extension and the new and old don't match complain lpszExt = PathFindExtension(pszOldName); if (*lpszExt && lstrcmpi(lpszExt, PathFindExtension(szOldPathName))) { TCHAR szTemp[MAX_PATH]; PathCombine(szNewPathName, pszDir, pszOldName); if (!PathIsDirectory(szNewPathName) && GetClassDescription(HKEY_CLASSES_ROOT, lpszExt, szTemp, ARRAYSIZE(szTemp),GCD_ALLOWPSUDEOCLASSES | GCD_MUSTHAVEOPENCMD)) { if (ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_WARNCHANGEEXT), MAKEINTRESOURCE(IDS_RENAME), MB_YESNO | MB_ICONEXCLAMATION) != IDYES) return ERROR_ACCESS_DENIED; } } PathCombine(szNewPathName, pszDir, szOldPathName); PathCombine(szOldPathName, pszDir, pszOldName); szOldPathName[lstrlen(szOldPathName) + 1] = TEXT('\0'); // BUGBUG: we need UI to warn if they are trying to change extension if (bRetainExtension) { // Retain the extension from the old name. // If the user wanted a different extension, tough. Play // a little violin for them, then get on with life... // PathRenameExtension(szNewPathName, PathFindExtension(szOldPathName)); } szNewPathName[lstrlen(szNewPathName) + 1] = TEXT('\0'); // double NULL terminate { SHFILEOPSTRUCT sFileOp = { hwnd, FO_RENAME, szOldPathName, szNewPathName, FOF_SILENT | FOF_ALLOWUNDO, }; iret = SHFileOperation(&sFileOp); } if (!iret) { CheckShortcutRename(szOldPathName, szNewPathName); } return iret; } #ifdef SYNC_BRIEFCASE HRESULT CFSBrfFolder_CreateFromIDList(LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut); HRESULT BrfStg_CreateInstance(LPCITEMIDLIST pidl, HWND hwnd, LPVOID * ppvOut); extern IShellFolderVtbl c_FSBrfFolderVtbl; #endif // SYNC_BRIEFCASE //=========================================================================== // CFSFolder : Vtable //=========================================================================== IShellFolderVtbl c_FSFolderVtbl = { CFSFolder_QueryInterface, CFSFolder_AddRef, CFSFolder_Release, CFSFolder_ParseDisplayName, CFSFolder_EnumObjects, CFSFolder_BindToObject, CDefShellFolder_BindToStorage, CFSFolder_CompareIDs, CFSFolder_CreateViewObject, CFSFolder_GetAttributesOf, CFSFolder_GetUIObjectOf, CFSFolder_GetDisplayNameOf, CFSFolder_SetNameOf, }; //=========================================================================== // CFSFolder : IShellIcon Vtable //=========================================================================== IShellIconVtbl c_FSFolderIconVtbl = { CFSFolder_Icon_QueryInterface, CFSFolder_Icon_AddRef, CFSFolder_Icon_Release, CFSFolder_Icon_GetIconOf, }; //=========================================================================== // CFSFolder : IPersistFolder Vtable //=========================================================================== IPersistFolderVtbl c_FSFolderPFVtbl = { CFSFolder_PF_QueryInterface, CFSFolder_PF_AddRef, CFSFolder_PF_Release, CFSFolder_PF_GetClassID, CFSFolder_PF_Initialize, }; BOOL FSFolder_CombinePathI(LPCIDFOLDER pidfT, LPTSTR pszPath, BOOL fAltName) { BOOL fSuccess = TRUE; TCHAR szName[MAX_PATH]; for ( ; fSuccess && !FS_IsEmpty(pidfT); pidfT=FS_Next(pidfT)) { int cchPath = lstrlen(pszPath); if (fAltName) { // // If we have the alternate name, use it. // FS_CopyAltName(pidfT,szName,ARRAYSIZE(szName)); if (*szName) { // Assert(lstrlen(pszPath)+lstrlen(szName)+2 <= MAX_PATH); if ((cchPath+lstrlen(szName)+2) > MAX_PATH) { fSuccess = FALSE; break; } fSuccess = (BOOL)PathCombine(pszPath, pszPath, szName); continue; } } FS_CopyName(pidfT, szName, ARRAYSIZE(szName)); // Assert(lstrlen(pszPath)+lstrlen(szName)+2 <= MAX_PATH); if ((cchPath+lstrlen(szName)+2) > MAX_PATH) { fSuccess = FALSE; break; } fSuccess = (BOOL)PathCombine(pszPath, pszPath, szName); // if there really is no long name for the item than we may // want to unpretty the name and convert back to all uppercase, // as file operations that create new files will than not // need to create dual names for these items. if (pidfT->fs.wAttrs & FSTREEX_ATTRIBUTE_NOLFN) { #if (defined(DBCS) || (defined(FE_SB) && !defined(UNICODE))) AnsiUpperNoDBCS(pszPath + cchPath); #else CharUpper(pszPath + cchPath); #endif } } if (!fSuccess) { *pszPath = TEXT('\0'); } return fSuccess; } BOOL FSFolder_CombinePath(LPCITEMIDLIST pidl, LPTSTR pszPath, BOOL fAltName) { return FSFolder_CombinePathI((LPCIDFOLDER)pidl, pszPath, fAltName); } const UNALIGNED CLSID * FS_GetCLSID(LPCIDFOLDER pidf) { const UNALIGNED CLSID * pclsid = NULL; if (FS_IsJunction(pidf)) { pclsid = (UNALIGNED CLSID *)(((LPBYTE)pidf)+pidf->cb-SIZEOF(CLSID)); #ifdef DEBUG { TCHAR szTmp[MAX_PATH]; LPBYTE lpEnd = (LPBYTE)pidf + FIELD_OFFSET(IDFOLDER,fs.cFileName); FS_CopyName(pidf,szTmp,ARRAYSIZE(szTmp)); if ((FS_GetType(pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE) { lpEnd += (lstrlen(szTmp) + 1) * SIZEOF(TCHAR); } else { lpEnd += (lstrlen(szTmp) + 1); } FS_CopyAltName(pidf,szTmp,ARRAYSIZE(szTmp)); lpEnd += (lstrlen(szTmp) + 1); // WARNING:: DocFind adds 3 bytes in. If it is a junction point // it puts it in before the CLSID. This code allows this without // ripping Assert(((LPBYTE)pclsid == lpEnd) || ((LPBYTE)pclsid == lpEnd+3)); } #endif } return pclsid; } // // SHGetClass // // Description: // // returns a unique name for a class, dont use this function to get // the ProgID for a class call SHGetClassKey() for that // // Returns: pointer to class name // // foo.ext ".ext" // foo "." // (empty) "Folder" // directory "Directory" // junction "CLSID\{clsid}" // // Notes: // szClass is a work buffer, data is not retured here, always // use the return value (used to build name in junction case) // LPNCTSTR WINAPI SHGetClass(LPCIDFOLDER pidf, LPTSTR szClass, BOOL fGetFromStorage) { #ifdef CAIRO_DS TCHAR szPath[MAX_PATH]; LPITEMIDLIST pidlAbs; #endif if (ILIsEmpty((LPITEMIDLIST)pidf)) { // the desktop. Always use the "Folder" class. return c_szFolderClass; } else { #ifdef CAIRO_DS UINT uType; pidlAbs = (LPITEMIDLIST)pidf; SHGetPathFromIDList ((LPITEMIDLIST)pidlAbs, szPath); if (fGetFromStorage) { pidf = FS_FindLastID ((LPITEMIDLIST)pidf); SHGetPathFromIDList ((LPITEMIDLIST)pidf, szPath); } uType = pidf->bFlags; #else UINT uType = pidf->bFlags; #endif // we want to find all callers of this that dont give rel pidls //BUGBUG (jimharr) what do i do with this assert? it can't be true // anymore! // Assert(pidf == FS_FindLastID((LPITEMIDLIST)pidf)); // // BUGBUG: git rid of the old FS type flags we dont use any more // Do not include SHID_FS_COMMONITEM in this list. // if (IS_FSIDL((LPITEMIDLIST)pidf)) { uType &= SHID_FS_DIRECTORY|SHID_FS_FILE|SHID_FS_UNICODE|SHID_JUNCTION; } // // If this is a network share point, we should treat it as // a standard folder. // else if (uType==(SHID_NET_SHARE|SHID_JUNCTION)) { // BUGBUG - BobDay what about net shares whose name is unicode? uType = SHID_FS_DIRECTORY; } else { // obviously not true given below ifs, and I'm sick of hitting // this assert... // DebugMsg(DM_TRACE, "Invalid pidl passed to SHGetClass type=%02X", uType); // Assert(0); } if (uType & SHID_JUNCTION) { // This is a junction point, get the CLSID from it. const UNALIGNED CLSID * uapclsid = FS_GetCLSID(pidf); Assert(uapclsid); Assert(lstrlen(c_szCLSIDSlash) == 6); // Put the class ID at the end of "CLSID\\" lstrcpy(szClass, c_szCLSIDSlash); StringFromGUID2A(uapclsid, szClass+6, GUIDSTR_MAX); return szClass; } else if ((uType == SHID_FS_FILE) || (uType == SHID_FS)) { // This is a file. Get the class based on the extension. #ifdef UNICODE TCHAR szName[MAX_PATH]; LPCWSTR szExt; FS_CopyName(pidf, szName, ARRAYSIZE(szName)); szExt = PathFindExtension(szName); lstrcpyn(szClass,szExt,MAX_CLASS); szExt = szClass; #else LPCSTR szExt = PathFindExtension(FS_GetName(pidf)); #endif if (*szExt == 0) // file has no extension #ifdef CAIRO_DS { if (!fGetFromStorage) { szExt = c_szNoClass; // ...use special class for that } else { CLSID clsid; if (SHGetClassFromStorage ((LPCITEMIDLIST)pidlAbs, &clsid)) { lstrcpy(szClass, c_szCLSIDSlash); StringFromGUID2A(&clsid, szClass+6, GUIDSTR_MAX); } else { szExt = c_szNoClass; } } } #else { szExt = c_szNoClass; // ...use special class for that } #endif //CAIRO_DS return szExt; } else if (uType == SHID_FS_FILEUNICODE || (uType == SHID_FS_UNICODE)) { // This is a file with a unicode name TCHAR szName[MAX_PATH]; LPCTSTR szExt; FS_CopyName(pidf, szName, ARRAYSIZE(szName)); szExt = PathFindExtension(szName); lstrcpyn(szClass,szExt,MAX_CLASS); szExt = szClass; if (*szExt == 0) // file has no extension #ifdef CAIRO_DS { if (!fGetFromStorage) { szExt = c_szNoClass; // ...use special class for that } else { CLSID clsid; if (SHGetClassFromStorage ((LPCITEMIDLIST)pidlAbs, &clsid)) { lstrcpy(szClass, c_szCLSIDSlash); StringFromGUID2A(&clsid, szClass+6, GUIDSTR_MAX); } else { szExt = c_szNoClass; } } } #else { szExt = c_szNoClass; // ...use special class for that } #endif //CAIRO_DS return szExt; } else { #ifdef CAIRO_DS if (fGetFromStorage) { CLSID clsid; if (SHGetClassFromStorage ((LPCITEMIDLIST)pidlAbs, &clsid)) { lstrcpy(szClass, c_szCLSIDSlash); StringFromGUID2A(&clsid, szClass+6, GUIDSTR_MAX); return szClass; } else { Assert((uType == SHID_FS_DIRECTORY) || (uType == SHID_FS_DIRUNICODE) || ((uType & SHID_GROUPMASK) == SHID_COMPUTER) || (uType == SHID_NET_SERVER)); return c_szDirectoryClass; } } else { Assert((uType == SHID_FS_DIRECTORY) || (uType == SHID_FS_DIRUNICODE) || ((uType & SHID_GROUPMASK) == SHID_COMPUTER) || (uType == SHID_NET_SERVER)); return c_szDirectoryClass; } #else // This is a directory. Always use the "Directory" class. // This can also be a Drive id. Assert((uType == SHID_FS_DIRECTORY) || (uType == SHID_FS_DIRUNICODE) || ((uType & SHID_GROUPMASK) == SHID_COMPUTER) || (uType == SHID_NET_SERVER)); return c_szDirectoryClass; #endif //CAIRO_DS } } Assert(0); } // reverse the OLE CLSID for the file to the ProgID and return an open key // on that ProgID. if there is no ProdID section use the CLSID instead. this // way you can hang shell things off the CLSID\{GUID} // HKEY ProgIDKeyFromCLSIDStr(LPCTSTR pszClass) { HKEY hkeyCLSID = NULL; HKEY hkeyProgID = NULL; Assert(pszClass[5] == TEXT('\\') && pszClass[6] == CH_GUIDFIRST); if (SHRegOpenKey(HKEY_CLASSES_ROOT, pszClass, &hkeyCLSID) == ERROR_SUCCESS) { // Get the progID from the specified CLSID TCHAR szProgID[80]; ULONG cb = SIZEOF(szProgID); if (RegQueryValue(hkeyCLSID, c_szProgID, szProgID, &cb) == ERROR_SUCCESS) { // CLSID has a ProgID entry, use that. RegCloseKey(hkeyCLSID); // close CLSID key SHRegOpenKey(HKEY_CLASSES_ROOT, szProgID, &hkeyProgID); } else { // This extension has CLSID only (like Control panel). // use the hkeyCLSID as the hkeyProgID. // It will allow us to have "shell" stuff here. hkeyProgID = hkeyCLSID; } } else { DebugMsg(DM_ERROR, TEXT("%s not found in registry"), pszClass); } return hkeyProgID; } HKEY ProgIDKeyFromCLSID(const CLSID *pclsid) { TCHAR szClass[GUIDSTR_MAX + 10]; lstrcpy(szClass, c_szCLSIDSlash); StringFromGUID2A(pclsid, szClass + 6, GUIDSTR_MAX); return ProgIDKeyFromCLSIDStr(szClass); } // // SHGetClassKey // // Description: // get the class key to be used for this class // This function is THE only place we get the class (ProgID) // of a file or directory. // // Parameters: pidf -- Specifies the file/directory // this can be a full or relative, but it must end in a // FS idlist. // phkeyProgID -- place to return "class" key // pdwDefClassUsed -- place to return code describing which default // class key was used if a default was used. Ptr can be NULL. // SHGCK_DEFCLASS_NOTUSED - No default used. // SHGCK_DEFCLASS_UNKNOWN - Used "Unknown" class. // SHGCK_DEFCLASS_BASE - Used "Base" class. // // fGetFromStorage -- open Storage to read CLSID, if needed // // Required: pidf points to a file system object. // pidf should include no junction point in the middle. // pidf MUST be an absolute path, if pidl points to a junction point. // if fGetFromStorage is TRUE, pidf MUST be absolute. // // Notes: // The caller should close returned keys (via SHCloseClassKey) // // REVIEW: We should decide what class key is returned as a default instead // of returning "Unknown" for some cases and "Base" for others. // The pdwDefClassUsed arg was added to compensate for this // so the caller can determine which was used as a default. // Otherwise, if FALSE is returned, you don't know what // class key was returned. // BOOL SHGetClassKey(LPCIDFOLDER pidf, HKEY * phkeyProgID, LPDWORD pdwDefClassUsed, BOOL fGetFromStorage) { HKEY hkeyProgID = NULL; BOOL fKnownType = TRUE; HKEY hkeyCLSID = NULL; TCHAR achClass[MAX_CLASS]; TCHAR szClass[MAX_PATH]; LPITEMIDLIST pidlAbs; DWORD dwDefClassUsed = SHGCK_DEFCLASS_NOTUSED; #if CAIRO_DS if (!fGetFromStorage) { pidf = FS_FindLastID((LPITEMIDLIST)pidf); } #else pidf = FS_FindLastID((LPITEMIDLIST)pidf); #endif ualstrcpyn(szClass, SHGetClass(pidf, achClass, fGetFromStorage), ARRAYSIZE(szClass)); // // class is a file extension, try to get the ProgID it points to, or just // use it. // if (szClass[0] == TEXT('.')) { TCHAR szProgID[MAX_CLASS]; ULONG cb = SIZEOF(szProgID); if (RegQueryValue(HKEY_CLASSES_ROOT, szClass, szProgID, &cb) != ERROR_SUCCESS) { // This file has no type (no extension or has unknown extension) SHRegOpenKey(HKEY_CLASSES_ROOT, c_szUnknownClass, &hkeyProgID); dwDefClassUsed = SHGCK_DEFCLASS_UNKNOWN; fKnownType = FALSE; } else if (cb <= SIZEOF(TCHAR)) { // No ProgID use the extension as the program ID. SHRegOpenKey(HKEY_CLASSES_ROOT, szClass, &hkeyProgID); } else { // the entension points to a ProgID use that. SHRegOpenKey(HKEY_CLASSES_ROOT, szProgID, &hkeyProgID); } } // // class is a junction, look at the CLSID // else if (szClass[6] == CH_GUIDFIRST) { hkeyProgID = ProgIDKeyFromCLSIDStr(szClass); } // // class is a not a extension or junction (like "Folder") // else { SHRegOpenKey(HKEY_CLASSES_ROOT, szClass, &hkeyProgID); } // // If we can find the registered class, use the base class // if (hkeyProgID == NULL) { FullDebugMsg(DM_TRACE, TEXT("SHGetClassKey: using base class for '%s'"), szClass); SHGetBaseClassKey(pidf, &hkeyProgID); dwDefClassUsed = SHGCK_DEFCLASS_BASE; fKnownType = FALSE; } if (phkeyProgID) *phkeyProgID = hkeyProgID; else if (hkeyProgID) RegCloseKey(hkeyProgID); if (NULL != pdwDefClassUsed) *pdwDefClassUsed = dwDefClassUsed; return fKnownType; } BOOL _SHGetBaseKey(BOOL bFolder, HKEY *phkeyBase) { SHRegOpenKey(HKEY_CLASSES_ROOT, bFolder ? c_szFolderClass : c_szBaseClass, phkeyBase); return(TRUE); } // // SHGetBaseClassKey - get the base class for a pidl // // Description: // firgures out the base class of a FS object, currently we only // have two base classes '*' and 'Folder' // // Returns: // // foo.ext "*" // foo "*" // (empty) "Folder" // directory "Folder" // junction "Folder" // // Notes: // The caller should close returned key (via SHCloseClassKey) // BOOL WINAPI SHGetBaseClassKey(LPCIDFOLDER pidf, HKEY * phkeyBaseID) { LPITEMIDLIST pidl = (LPITEMIDLIST)pidf; LPITEMIDLIST pidlLast = (LPITEMIDLIST)FS_FindLastID(pidl); BOOL bFolder = (ILIsEmpty(pidl) || !IS_FSIDL(pidlLast) || FS_IsFolder((LPCIDFOLDER)pidlLast)); return(_SHGetBaseKey(bFolder, phkeyBaseID)); } // // SHGetFileClassKey // // Description: // get the class key given a file (not a idlist) // // Notes: // The caller should close returned key (via SHCloseClassKey) // BOOL WINAPI SHGetFileClassKey(LPCTSTR szFile, HKEY * phkey, HKEY * phkeyBase) { LPITEMIDLIST pidl; BOOL f = FALSE; pidl = ILCreateFromPath(szFile); if (pidl == NULL) pidl = SHSimpleIDListFromPath(szFile); if (pidl) { f = SHGetClassKey((LPCIDFOLDER)pidl, phkey, NULL, FALSE); if (f && !SHGetBaseClassKey((LPIDFOLDER)pidl, phkeyBase)) { *phkeyBase = NULL; } ILFree(pidl); } return f; } // // Description: // close a key open'ed via SHGetClassKey // // the idea here is we can cache a few class related hkeys, but all we // do right now is call RegCloseKey // void WINAPI SHCloseClassKey(HKEY hkeyProgID) { if (hkeyProgID) RegCloseKey(hkeyProgID); } //=========================================================================== // SHGetClassFlags - get flags for a file class. // // given a FS PIDL returns a DWORD of flags, or 0 for error // // SHCF_ICON_INDEX this is this sys image index for per class // SHCF_ICON_PERINSTANCE icons are per instance (one per file) // SHCF_ICON_DOCICON icon is in shell\open\command (simulate doc icon) // // SHCF_HAS_VERBS set if class has verbs // SHCF_HAS_ICONHANDLER set if class has a IExtractIcon handler // SHCF_HAS_DATAHANDLER set if class has a IDataObject handler // SHCF_HAS_DROPHANDLER set if class has a IDropTarget handler // // SHCF_UNKNOWN set if extenstion is not registered // // SHCF_IS_LINK set if class is a link // SHCF_IS_JUNCTION set if junction // SHCF_ALWAYS_SHOW_EXT always show the extension // SHCF_NEVER_SHOW_EXT never show the extension // // flag fGetFromStorage meaning: if other methods of getting CLSID fail, // this pidf is Absolute, and an attempt should be made to read the // CLSID from the object storage. this is to facilitate exploring of // the Cairo DS space. //=========================================================================== DWORD WINAPI SHGetClassFlags(LPCIDFOLDER pidf, BOOL fGetFromStorage) { HKEY hkey; HKEY hkeyCLSID; DWORD dwFlags; DWORD dwType; TCHAR ach[MAX_PATH]; DWORD cb; int iIcon; int iImage; TCHAR class[MAX_CLASS]; TCHAR szClass[MAX_CLASS]; // // look up the file type in the cache. // ualstrcpyn(szClass, SHGetClass(pidf, class, fGetFromStorage), ARRAYSIZE(szClass)); dwFlags = LookupFileClass(szClass); #ifndef FULL_DEBUG // // if we got a cache hit we are done // if (dwFlags != 0) return dwFlags; #endif // // nothing is in our cache, do it the hard way. // dwFlags = 0; // // check for junction // if (FS_IsJunction(pidf)) { dwFlags |= SHCF_IS_JUNCTION; dwFlags |= SHCF_NEVER_SHOW_EXT; } else if (FS_IsFolder(pidf)) { dwFlags |= SHCF_ALWAYS_SHOW_EXT; } // // open the class key. // if (!SHGetClassKey(pidf, &hkey, NULL, fGetFromStorage)) { int iIcon; // // unknown type - pick defaults and get out. // dwFlags |= SHCF_UNKNOWN; dwFlags |= SHCF_ALWAYS_SHOW_EXT; if (FS_IsFolder(pidf)) { iIcon = II_FOLDER; } else { iIcon = II_DOCNOASSOC; } dwFlags |= Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0); goto done; } // // see what handlers exist // if ((0 != (cb=SIZEOF(ach))) && RegQueryValue(hkey, c_szShell, ach, &cb) == ERROR_SUCCESS) dwFlags |= SHCF_HAS_VERBS; if ((0 != (cb=SIZEOF(ach))) && RegQueryValue(hkey, c_szMenuHandlers, ach, &cb) == ERROR_SUCCESS) dwFlags |= SHCF_HAS_VERBS; if ((0 != (cb=SIZEOF(ach))) && RegQueryValue(hkey, c_szIconHandler, ach, &cb) == ERROR_SUCCESS) dwFlags |= SHCF_HAS_ICONHANDLER; if ((0 != (cb=SIZEOF(ach))) && RegQueryValue(hkey, c_szDataHandler, ach, &cb) == ERROR_SUCCESS) dwFlags |= SHCF_HAS_DATAHANDLER; if ((0 != (cb=SIZEOF(ach))) && RegQueryValue(hkey, c_szDropHandler, ach, &cb) == ERROR_SUCCESS) dwFlags |= SHCF_HAS_DROPHANDLER; // // get attributes // if ((0 != (cb=SIZEOF(ach))) && RegQueryValueEx(hkey, c_szIsLink, NULL, &dwType, (LPBYTE)ach, &cb) == ERROR_SUCCESS) dwFlags |= SHCF_IS_LINK; if ((0 != (cb=SIZEOF(ach))) && RegQueryValueEx(hkey, c_szAlwaysShowExt, NULL, &dwType, (LPBYTE)ach, &cb) == ERROR_SUCCESS) dwFlags |= SHCF_ALWAYS_SHOW_EXT; if ((0 != (cb=SIZEOF(ach))) && RegQueryValueEx(hkey, c_szNeverShowExt, NULL, &dwType, (LPBYTE)ach, &cb) == ERROR_SUCCESS) dwFlags |= SHCF_NEVER_SHOW_EXT; #ifdef CAIRO_DS if ((0 != (cb=SIZEOF(ach))) && RegQueryValueEx(hkey, c_szIObjectLifecycle, NULL, &dwType, (LPBYTE)ach, &cb) == ERROR_SUCCESS) dwFlags |= SHCF_SUPPORTS_IOBJLIFE; #endif // // figure out what type of icon this type of file uses. // if (dwFlags & SHCF_HAS_ICONHANDLER) { dwFlags |= SHCF_ICON_PERINSTANCE; } else { // check for icon in ProgID ach[0] = 0; cb=SIZEOF(ach); RegQueryValue(hkey, c_szDefaultIcon, ach, &cb); // Then, check if the default icon is specified in OLE-style. if (ach[0]==0 && (NULL != (hkeyCLSID = SHOpenCLSID(hkey)))) { cb=SIZEOF(ach); RegQueryValue(hkeyCLSID, c_szDefaultIcon, ach, &cb); RegCloseKey(hkeyCLSID); } if (ach[0]==0 && (0 != (cb=SIZEOF(ach))) && RegQueryValue(hkey, c_szShellOpenCmd, ach, &cb) == ERROR_SUCCESS && ach[0]) { PathRemoveBlanks(ach); PathRemoveArgs(ach); dwFlags |= SHCF_ICON_DOCICON; } // Check if this is a per-instance icon if (lstrcmp(ach, c_szPercentOne) == 0 || lstrcmp(ach, c_szPercentOneInQuotes) == 0) { dwFlags &= ~SHCF_ICON_DOCICON; dwFlags |= SHCF_ICON_PERINSTANCE; } else if (ach[0]) { iIcon = PathParseIconLocation(ach); if (dwFlags & SHCF_ICON_DOCICON) iImage = Shell_GetCachedImageIndex(ach, iIcon, GIL_SIMULATEDOC); else iImage = Shell_GetCachedImageIndex(ach, iIcon, 0); if (iImage == -1) { int iIcon; if (dwFlags & SHCF_ICON_DOCICON) iIcon = II_DOCUMENT; else iIcon = II_DOCNOASSOC; iImage = Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0); } Assert(SHCF_ICON_INDEX & 1); Assert((iImage & ~SHCF_ICON_INDEX) == 0); dwFlags |= iImage; } else { int iIcon; // nothing is in the registry default to folder or generic doc if (FS_IsFolder(pidf)) iIcon = II_FOLDER; // default: folder else iIcon = II_DOCNOASSOC; // default: document icon dwFlags |= Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0); dwFlags |= SHCF_ICON_DOCICON; // make dwFlags non-zero } } done: SHCloseClassKey(hkey); #ifdef FULL_DEBUG dwType = LookupFileClass(szClass); if (dwType != 0) { if (dwType != dwFlags) { DebugMsg(DM_TRACE, TEXT("****** the file class cache is out 'o sync %s %08X %08X"), szClass, dwType, dwFlags); Assert(0); } return dwType; } #endif #ifdef FULL_DEBUG { TCHAR szTmp[MAX_PATH]; FS_CopyName(FS_FindLastID((LPITEMIDLIST)pidf),szTmp,ARRAYSIZE(szTmp)); DebugMsg(DM_TRACE, TEXT("SHGetClassFlags(%s) '%s' %08lX"), szTmp, szClass, dwFlags); } if (dwFlags & SHCF_UNKNOWN ) ach[0]=iIcon=0; if (dwFlags & SHCF_UNKNOWN ) DebugMsg(DM_TRACE, TEXT(" is unknown type ")); if (dwFlags & SHCF_ICON_PERINSTANCE ) DebugMsg(DM_TRACE, TEXT(" icon is per instance")); if (!(dwFlags & SHCF_ICON_PERINSTANCE)) DebugMsg(DM_TRACE, TEXT(" icon is per class %s,%d (%d)"), ach, iIcon, dwFlags&SHCF_ICON_INDEX); if (dwFlags & SHCF_ALWAYS_SHOW_EXT ) DebugMsg(DM_TRACE, TEXT(" always show extension ")); if (dwFlags & SHCF_NEVER_SHOW_EXT ) DebugMsg(DM_TRACE, TEXT(" never show extension ")); if (dwFlags & SHCF_IS_LINK ) DebugMsg(DM_TRACE, TEXT(" is a link ")); if (dwFlags & SHCF_IS_JUNCTION ) DebugMsg(DM_TRACE, TEXT(" is a junction ")); if (dwFlags & SHCF_HAS_VERBS ) DebugMsg(DM_TRACE, TEXT(" has VERBS ")); if (dwFlags & SHCF_HAS_ICONHANDLER ) DebugMsg(DM_TRACE, TEXT(" has ICONHANDLER ")); if (dwFlags & SHCF_HAS_DATAHANDLER ) DebugMsg(DM_TRACE, TEXT(" has DATAHANDLER ")); if (dwFlags & SHCF_HAS_DROPHANDLER ) DebugMsg(DM_TRACE, TEXT(" has DROPHANDLER ")); #endif Assert(dwFlags != 0); AddFileClass(szClass, dwFlags); return dwFlags; } //=========================================================================== // CFSFolder : Constructor //=========================================================================== HRESULT CFSFolder_CreateFromIDList(LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut) { HRESULT hres = E_OUTOFMEMORY; LPFSFOLDER pfsf = (void*)LocalAlloc(LPTR, SIZEOF(CFSFolder)); if (pfsf) { pfsf->sf.lpVtbl = &c_FSFolderVtbl; pfsf->si.lpVtbl = &c_FSFolderIconVtbl; pfsf->pf.lpVtbl = &c_FSFolderPFVtbl; pfsf->cRef = 1; pfsf->pidl = ILClone(pidl); pfsf->wSpecialFID = CSIDL_NOTCACHED; if (pfsf->pidl) { hres = pfsf->sf.lpVtbl->QueryInterface(&pfsf->sf, riid, ppvOut); } pfsf->sf.lpVtbl->Release(&pfsf->sf); } return hres; } TCHAR const c_szClassInfo[] = STRINI_CLASSINFO; // // This function retrieves the CLSID from a filename // file.{GUID} or file.XXX{GUID} are valid // BOOL _GetFileCLSID(LPCTSTR pszFile, CLSID* pclsid) { BOOL fSuccess = FALSE; LPTSTR szExt = PathFindExtension(pszFile); if (szExt[0] == TEXT('.')) { // // handle the file.{GUID} case // if (szExt[1] == CH_GUIDFIRST) { if (SUCCEEDED(SHCLSIDFromString(szExt+1, pclsid))) { DebugMsg(DM_TRACE, TEXT("sh TR - found CLSID (%s)"), pszFile); fSuccess = TRUE; } } #if 0 // // handle the file.XXX{GUID} case so these files look ok on // a non-LFN system. // if (szExt[1] && szExt[2] && szExt[3] && szExt[4] == CH_GUIDFIRST) { if (SUCCEEDED(SHCLSIDFromString(szExt+4, pclsid))) { DebugMsg(DM_TRACE, TEXT("sh TR - found CLSID (%s)"), pszFile); fSuccess = TRUE; } } #endif } return fSuccess; } // // This function retrieves the CLSID from desktop.ini file. // BOOL _GetFolderCLSID(LPCTSTR pszParent, LPCTSTR pszFolder, LPTSTR pszProvider, CLSID* pclsid, LPCTSTR pszKey) { BOOL fSuccess = FALSE; BOOL fCombined, fExists; TCHAR szPath[MAX_PATH]; TCHAR szClassName[40]; // REVIEW: This len should be in a header fCombined = (PathCombine(szPath, pszParent, pszFolder) && PathCombine(szPath, szPath, c_szDesktopIni)); // CHECK for PathFileExists BEFORE calling to GetPrivateProfileString // because if the file isn't there (which is the majority of cases) // GetPrivateProfileString hits the disk twice looking for the file if (pszProvider && *pszProvider) { NETRESOURCE nr; LPTSTR lpSystem; DWORD dwRes,dwcch = SIZEOF(szClassName); nr.dwType = RESOURCETYPE_ANY; nr.lpRemoteName = szPath; nr.lpProvider = pszProvider; dwRes = WNetGetResourceInformation( &nr, szClassName, &dwcch, &lpSystem); fExists = ((dwRes==WN_MORE_DATA) || (dwRes==WN_SUCCESS)); } else { fExists = PathFileExists(szPath); } if (fCombined && fExists && GetPrivateProfileString(c_szClassInfo, pszKey, szNULL, szClassName, ARRAYSIZE(szClassName), szPath)) { if (SUCCEEDED(SHCLSIDFromString(szClassName, pclsid))) { FullDebugMsg(DM_TRACE, TEXT("sh TR - found CLSID (%s in %s)"), szClassName, szPath); fSuccess = TRUE; } } return fSuccess; } // // This one return non-task-allocated pidl // LPIDFOLDER CFSFolder_FillIDFolder(WIN32_FIND_DATA *lpfd, LPCTSTR pszParent, LPARAM lParam) { UINT cbFileName, cbAltFileName, cb; UINT cchFileName; #ifdef UNICODE UINT cbUnicodeFileName; CHAR szFileName[MAX_PATH]; WCHAR szTemp[MAX_PATH]; BOOL fUnicode; #endif LPIDFOLDER pidf; BYTE bFlags; CLSID clsid; BOOL fPrettyName; TCHAR szPrettyName[MAX_PATH]; LPTSTR lpFileName; cchFileName = lstrlen(lpfd->cFileName) + 1; cbAltFileName= lstrlen(lpfd->cAlternateFileName) + 1; // Size of ansi part of id lpFileName = lpfd->cFileName; fPrettyName = FALSE; // If we have only a short name, make it pretty. if (cchFileName <= (8+1+3+1) && ((cbAltFileName == 1) || lstrcmp(lpfd->cFileName, lpfd->cAlternateFileName) == 0)) { lstrcpy(szPrettyName,lpfd->cFileName); if (PathMakePretty(szPrettyName)) { lpFileName = szPrettyName; fPrettyName = TRUE; } } #ifdef UNICODE // BUGBUGBC What if this changes the ANSI size? WideCharToMultiByte(CP_ACP, 0, lpFileName, cchFileName, szFileName, ARRAYSIZE(szFileName), NULL, NULL); MultiByteToWideChar(CP_ACP, 0, szFileName, -1, szTemp, ARRAYSIZE(szTemp)); if (lstrcmp(lpFileName,szTemp) != 0) { // Have to create a complete unicode idl cbFileName = cchFileName * SIZEOF(WCHAR); fUnicode = TRUE; } else { // Ok to create an ansi idl cbFileName = cchFileName; fUnicode = FALSE; } #else cbFileName = cchFileName; #endif cb = FIELDOFFSET(IDFOLDER, fs.cFileName) + cbFileName + cbAltFileName; // // Get the appropriate bFlags // if (lpfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { bFlags = SHID_FS_DIRECTORY; } else { bFlags = SHID_FS_FILE; } #ifdef UNICODE if ( fUnicode ) { bFlags |= SHID_FS_UNICODE; } #endif // // check for a junction point, junctions are either // // a directory with a clsid as a extension, or // a system directory, with the clsid stored in desktop.ini // if (_GetFileCLSID(lpfd->cFileName, &clsid)) { bFlags |= SHID_JUNCTION; cb += SIZEOF(CLSID); // cache the CLSID! } // // We treat a READONLY directory as a potential junction // point as well as a SYSTEM directory. This mechanism // allows us to make a Briefcase directory visible from // old Win3.1 apps which hide SYSTEM folders. // else if ((bFlags == SHID_FS_DIRECTORY || bFlags == SHID_FS_DIRUNICODE) && pszParent!=NULL && (lpfd->dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_READONLY))) { if (_GetFolderCLSID(pszParent, lpfd->cFileName, NULL, &clsid, c_szCLSID)) { bFlags |= SHID_JUNCTION; cb += SIZEOF(CLSID); // cache the CLSID! } } // Add enough to NULL terminate the list pidf = (LPIDFOLDER)_ILCreate(cb + SIZEOF(USHORT)); // BUGBUG (DavePl) use size of the struct member, not its type if (!pidf) return(NULL); if (lParam) CDefEnum_SetReturn(lParam, (LPITEMIDLIST)pidf); // We pack the 2 string in pidf->cb = cb; if (lpfd->nFileSizeHigh != 0) pidf->fs.dwSize = 0xFFFFFFFF; // A method of encoding the fact else // that we know it won't fit. pidf->fs.dwSize = lpfd->nFileSizeLow; pidf->fs.wAttrs = (WORD)lpfd->dwFileAttributes; if ( fPrettyName ) { pidf->fs.wAttrs |= FSTREEX_ATTRIBUTE_NOLFN; } { // Since the idl entry is not aligned, we cannot just send the address // of one of its members blindly into FileTimeToDosDateTime. WORD dateModified = * ((UNALIGNED WORD *) &pidf->fs.dateModified); WORD timeModified = * ((UNALIGNED WORD *) &pidf->fs.timeModified); // Note the COFSFolder doesn't provide any times _but_ LastWrite FileTimeToDosDateTime(&lpfd->ftLastWriteTime, &dateModified, &timeModified); * ((UNALIGNED WORD *) &pidf->fs.dateModified) = dateModified; * ((UNALIGNED WORD *) &pidf->fs.timeModified) = timeModified; } #ifdef UNICODE if (fUnicode) { ualstrcpy(pidf->fs.cFileName, lpFileName); } else { lstrcpyA((LPSTR)pidf->fs.cFileName, szFileName ); } WideCharToMultiByte(CP_ACP, 0, lpfd->cAlternateFileName, -1, (LPSTR)pidf->fs.cFileName+cbFileName, cbAltFileName, NULL, NULL ); #else lstrcpy(pidf->fs.cFileName, lpFileName); lstrcpy((LPSTR)pidf->fs.cFileName + cbFileName, lpfd->cAlternateFileName); #endif pidf->bFlags = bFlags; // If this is a junction point, save the CLSID. if (bFlags & SHID_JUNCTION) { UNALIGNED CLSID * pclsid = (UNALIGNED CLSID *)FS_GetCLSID(pidf); *pclsid= clsid; #ifdef DEBUG { TCHAR szTmp[MAX_PATH]; LPBYTE lpEnd = (LPBYTE)pidf + FIELD_OFFSET(IDFOLDER,fs.cFileName); FS_CopyName(pidf,szTmp,ARRAYSIZE(szTmp)); if ((FS_GetType(pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE) { lpEnd += (lstrlen(szTmp) + 1) * SIZEOF(TCHAR); } else { lpEnd += (lstrlen(szTmp) + 1); } FS_CopyAltName(pidf,szTmp,ARRAYSIZE(szTmp)); lpEnd += (lstrlen(szTmp) + 1); Assert((LPBYTE)pclsid == lpEnd); } #endif Assert(((LPBYTE)pidf)+cb==((LPBYTE)pclsid)+SIZEOF(CLSID) ); } return pidf; } // // This function returns a relative pidl for the specified file/directory. // // READ THIS: *ppidlOut MUST be freed by ILFree or SHFree! // HRESULT CFSFolder_CreateIDForItem(LPCTSTR pszPath, LPITEMIDLIST * ppidlOut, BOOL fTaskAlloc) { HRESULT hres = E_OUTOFMEMORY; WIN32_FIND_DATA finddata; HANDLE hfind; LPIDFOLDER pidf; TCHAR szParent[MAX_PATH]; *ppidlOut = NULL; // assume error hfind = FindFirstFileRetry(HWND_DESKTOP, pszPath, &finddata, NULL); if (hfind == INVALID_HANDLE_VALUE) { // We should not return E_INVLAIDARG. return E_FAIL; } FindClose(hfind); Assert(!PathIsRoot(pszPath)); lstrcpy(szParent, pszPath); PathRemoveFileSpec(szParent); pidf = CFSFolder_FillIDFolder(&finddata, szParent, 0); if (pidf) { // REVIEW: Do we need this? Change it to Assert. // NULL terminate the IDLIST *(UNALIGNED USHORT *)(((LPBYTE)pidf) + pidf->cb) = 0; if (fTaskAlloc) { hres = SHILClone((LPITEMIDLIST)pidf, ppidlOut); ILFree((LPITEMIDLIST)pidf); } else { *ppidlOut = (LPITEMIDLIST)pidf; hres = S_OK; } } return hres; } #ifdef USE_OLEDB // Allow external caller to create drop target without knowing explicitly about the vtable // (oledbshl) HRESULT CIDLDropTarget_CreateFromPidl(HWND hwnd, LPITEMIDLIST pidl, LPDROPTARGET * ppvOut) { return CIDLDropTarget_Create(hwnd, &c_CFSDropTargetVtbl, pidl, ppvOut); } #ifdef CAIRO_DS HRESULT CDS_IDLDropTarget_CreateFromPidl(HWND hwnd, LPITEMIDLIST pidl, LPDROPTARGET * ppvOut) { return CDS_IDLDropTarget_Create(hwnd, &cDS_IDLDropTargetVtbl, pidl, ppvOut); } #endif #endif // // This function returns a real IDLIST for a file system object from // a simple version of IDLIST. // // Returns: // E_INVALIDARG if the folder is not a file system object. // NOERORR, if successfully done (including NULL in pidlReal, which indicates // the object does not exist. // HRESULT FS_GetRealIDL(LPSHELLFOLDER psf, LPCITEMIDLIST pidlSimple, LPITEMIDLIST *ppidlReal) { // BUGBUG, we should check to see if it's already a real id, and blow off hitting the disk.. if (psf->lpVtbl == &c_FSFolderVtbl || psf->lpVtbl == &c_FSBrfFolderVtbl) { HRESULT hres; LPFSFOLDER this = IToClass(CFSFolder, sf, psf); TCHAR szPath[MAX_PATH]; SHGetPathFromIDList(this->pidl, szPath); FSFolder_CombinePath(pidlSimple, szPath, FALSE); // fAltName=FALSE hres = CFSFolder_CreateIDForItem(szPath, ppidlReal, FALSE); Assert(hres != E_INVALIDARG); return hres; } return E_INVALIDARG; } //=========================================================================== // CFSFolder : Members //=========================================================================== // // QueryInterface // HRESULT STDMETHODCALLTYPE CFSFolder_QueryInterface(LPSHELLFOLDER psf, REFIID riid, LPVOID * ppvObj) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IShellFolder)) { *ppvObj = psf; psf->lpVtbl->AddRef(psf); return S_OK; } if (IsEqualIID(riid, &IID_IShellIcon)) { *ppvObj = &this->si; psf->lpVtbl->AddRef(psf); return S_OK; } if (IsEqualIID(riid, &IID_IPersistFolder)) { *ppvObj = &this->pf; psf->lpVtbl->AddRef(psf); return S_OK; } *ppvObj = NULL; return(E_NOINTERFACE); } // // AddRef // ULONG STDMETHODCALLTYPE CFSFolder_AddRef(LPSHELLFOLDER psf) { register LPFSFOLDER this = IToClass(CFSFolder, sf, psf); InterlockedIncrement(&this->cRef); return this->cRef; } // // Release // ULONG STDMETHODCALLTYPE CFSFolder_Release(LPSHELLFOLDER psf) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); ULONG ulTmp = this->cRef; if (InterlockedDecrement(&this->cRef) != 0) return ulTmp - 1; if (this->pidl) { ILFree(this->pidl); } LocalFree((HLOCAL)this); return 0; } static const TCHAR c_chBS = TEXT('\\'); // This function may recurse, but does not use much stack STDMETHODIMP CFSFolder_ParseDisplayName(LPSHELLFOLDER psf, HWND hwndOwner, LPBC pbc, LPOLESTR pwszDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidlOut, DWORD* pdwAttributes) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); HRESULT hres = E_INVALIDARG; ULONG chEaten; IShellFolder *psfFolder; LPOLESTR pBS; LPITEMIDLIST pidlOut; TCHAR szPath[MAX_PATH]; LPTSTR pEnd; *ppidlOut = NULL; // assume error if (!pwszDisplayName) return hres; // Look for NULL or '\\' for (pBS = pwszDisplayName; *pBS && *pBS != c_chBS; pBS++); // do nothing // Use the pidf passed in to save on stack space SHGetPathFromIDList(this->pidl, szPath); pEnd = PathAddBackslash(szPath); // just convert chars up to the end or slash pEnd += OleStrToStrN(pEnd, ARRAYSIZE(szPath) - (pEnd - szPath), pwszDisplayName, pBS - pwszDisplayName); *pEnd = 0; // we need to terminate ourselves // Create a relative pidl for the specified file/path. hres = CFSFolder_CreateIDForItem(szPath, &pidlOut, TRUE); if (FAILED(hres)) return hres; // Check if there are any subdirs // If the name ends in '\\', we will stop here if (pBS[0] && pBS[1]) { LPITEMIDLIST pidlSubDir; // REVIEW: We can avoid some Alloc's by iterating down the // list rather than recursing, but since we are hitting the // disk this doesn't seem like such a big deal hres = CFSFolder_BindToObject(psf, pidlOut, pbc, &IID_IShellFolder, &psfFolder); if (FAILED(hres)) { ILFree(pidlOut); return hres; } hres = psfFolder->lpVtbl->ParseDisplayName(psfFolder, hwndOwner, pbc, pBS + 1, &chEaten, &pidlSubDir, pdwAttributes); if (SUCCEEDED(hres)) { LPITEMIDLIST pidlOld=pidlOut; hres = SHILCombine(pidlOut, pidlSubDir, &pidlOut); ILFree(pidlOld); ILFree(pidlSubDir); } else { ILFree(pidlOut); pidlOut = NULL; } psfFolder->lpVtbl->Release(psfFolder); } else { // // No subfolder. (Notes: pidlOut is simple) // if (pdwAttributes) { CFSFolder_GetAttributesOf(psf, 1, &pidlOut, pdwAttributes); } } *ppidlOut = pidlOut; return hres; } // check to see if this link is known in the MRU list. BOOL FindLinkInRecentDocsMRU(LPCTSTR lpszFileName) { // BUGBUG (Davepl) CS100 Guideline #1: No hardcoded constants. Whats 2048 all about? HANDLE hmru; int i; LPTSTR lpBuffer; BOOL fReturn = FALSE; TCHAR szTmp[MAX_PATH]; hmru = OpenRecentDocMRU(); if (!hmru) return FALSE; lpBuffer = (void*)LocalAlloc(LPTR, 2048); if (lpBuffer) { for (i = EnumMRUList(hmru, -1, NULL, 0) - 1; i >= 0; i--) { if (EnumMRUList(hmru, i, lpBuffer, 2048) != -1) { LPCITEMIDLIST pidlLink; pidlLink = (LPITEMIDLIST)(lpBuffer + lstrlen(lpBuffer) + 1); FS_CopyName((LPIDFOLDER)pidlLink,szTmp,ARRAYSIZE(szTmp)); if ( ! lstrcmpi(szTmp, lpszFileName)) { fReturn = TRUE; break; } } } CloseRecentDocMRU(); LocalFree((HLOCAL)lpBuffer); } return fReturn; } // // HRESULT CALLBACK CFSFolder_EnumCallBack(LPARAM lParam, LPVOID pvData, UINT ecid, UINT index) { HRESULT hres = S_OK; EnumFiles * pefile = (EnumFiles *)pvData; if (ecid == ECID_SETNEXTID) { // // 2nd Filter // for (; pefile->fNext; pefile->fNext = FindNextFile(pefile->hfind, &pefile->finddata)) { // if pefile->fNext == 42 we already processed the current folder if (pefile->fNext == (BOOL)42) continue; // // We want to ignore the files . and .. // #define pszFileName pefile->finddata.cFileName if (pszFileName[0] == TEXT('.') && (pszFileName[1] == TEXT('\0') || (pszFileName[1] == TEXT('.') && pszFileName[3] == TEXT('\0')))) continue; #undef pszFileName { ULARGE_INTEGER uli; uli.LowPart = pefile->finddata.nFileSizeLow; uli.HighPart = pefile->finddata.nFileSizeHigh; pefile->cbSize += uli.QuadPart; } // // skip non-folders, if we are just enumerating folders. // We need to do this filtering in addition to that 56504347 hack, // because that hack does not work on network cases. (bug?) // if (pefile->finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!(pefile->grfFlags & SHCONTF_FOLDERS)) { pefile->cHiddenFiles++; continue; } // is this the directory we want to hide (even though the hide bit isn't set) if (pefile->pidfHide) { // we know pidfHide is a full pidl so we can compare short names TCHAR szTmp[MAX_PATH]; FS_CopyName(pefile->pidfHide,szTmp,ARRAYSIZE(szTmp)); if (!ualstrcmpi(pefile->finddata.cFileName, szTmp)) { pefile->cHiddenFiles++; continue; } } } else { if (!(pefile->grfFlags & SHCONTF_NONFOLDERS)) { #ifndef WINNT #ifdef DEBUG if (pefile->grfFlags == SHCONTF_FOLDERS) DebugMsg(DM_WARNING,TEXT("wn FindFirstFile Hack 56504347 should prevent this")); #endif #else // On NT, the 56504347 hack doesn't work. We are thinking // that we probably don't need to make it work. The // speed gain to too small to notice. If we discover that // there is a large speed difference between NT and Win95, // then we might go around FindFirst/FindNext and just // use NtQueryDirectory with a large buffer and do our own // appropriate filtering. If even this is too slow, then // we might have to add the support to the filesystem for // it. // // But for now, just treat the file as hidden. #endif pefile->cHiddenFiles++; continue; } } // // If we are NOT requested to return hidden items, check for // hidden flag and excluding file specs. // if (!(pefile->grfFlags & SHCONTF_INCLUDEHIDDEN)) { // // We should hide hidden files. // if (pefile->finddata.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { pefile->cHiddenFiles++; continue; } if (pefile->grfFlags & SHCONTF_RECENTDOCSDIR) { if (!FindLinkInRecentDocsMRU(pefile->finddata.cFileName)) { pefile->cHiddenFiles++; continue; } } if (!(pefile->finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && _SHFindExcludeExt(pefile->finddata.cFileName) >= 0) { pefile->cHiddenFiles++; continue; } } break; } hres = S_FALSE; // assume no more items if (pefile->fNext) { LPIDFOLDER pidf = CFSFolder_FillIDFolder(&pefile->finddata, pefile->szFolder, lParam); if (pidf) hres = S_OK; else hres = E_OUTOFMEMORY; pefile->fNext = (BOOL)42; } else { if (pefile->pfsf) { // only stash these if we completed the enum pefile->pfsf->cHiddenFiles = pefile->cHiddenFiles; pefile->pfsf->cbSize = pefile->cbSize; } } } else if (ecid == ECID_RELEASE) { if (pefile->hfind != INVALID_HANDLE_VALUE) { FindClose(pefile->hfind); pefile->hfind = INVALID_HANDLE_VALUE; } if (pefile->grfFlags & SHCONTF_RECENTDOCSDIR) CloseRecentDocMRU(); if (pefile->pidfHide) ILFree((LPITEMIDLIST)pefile->pidfHide); if (pefile->pfsf) pefile->pfsf->sf.lpVtbl->Release(&pefile->pfsf->sf); LocalFree((HLOCAL)pefile); } return hres; } // BUGBUG: this comment sounds like it's off in space // The list of excluded extensions must be in the same order as the // list of string ID's // HACK: NEVER put 'cpl' in this list -- it breaks Asymetrix Compel // the rocket scientists over there never heard of control panels TCHAR const c_szDefExclude[] = TEXT("dll sys vxd 386 drv pnf "); TCHAR const c_szExclude[] = TEXT("Exclude"); // If you are worried about the string being too long, then check the // returned length, and if it is too close to cbSize, try again with a longer // buffer. void _SHGetExcludeFileExts(LPTSTR szExcludeFileExts, UINT cchSize) { LPTSTR pszNext; DWORD dwType; DWORD cbSize; TCHAR szTemp[128]; VDATEINPUTBUF(szExcludeFileExts, TCHAR, cchSize); lstrcpyn(szExcludeFileExts, c_szDefExclude, cchSize); pszNext = szExcludeFileExts + lstrlen(szExcludeFileExts); cchSize -= pszNext - szExcludeFileExts; wsprintf(szTemp, c_szSSlashS, c_szShellState, c_szExclude); // Let the registry add more extensions to our list cbSize = cchSize * SIZEOF(TCHAR); if (RegQueryValueEx(HKEY_CURRENT_USER, szTemp, NULL, &dwType, (LPBYTE)pszNext, &cbSize) != ERROR_SUCCESS) { *pszNext = TEXT('\0'); } } // Make a unique DWORD/LONGLONG for every sequence of 3 or less characters EXTKEY _ExtToEXTKEY(LPCTSTR psz) { union { EXTKEY key; TCHAR c[4]; } unRet; int i; unRet.key = *(UNALIGNED EXTKEY *)psz; for (i=0; ; ) { TCHAR cTemp = unRet.c[i]; if (cTemp <= TEXT(' ')) { // Stop on the first control character or space break; } if (IsDBCSLeadByte(cTemp)) { ++i; } ++i; if (i > 3) { // This is an invalid extension return(0); } } for ( ; i<4; ++i) { unRet.c[i] = TEXT('\0'); } // Make sure to upper case so we are case-insensitive #if (defined(DBCS) || (defined(FE_SB) && !defined(UNICODE))) AnsiUpperNoDBCS(unRet.c); #else CharUpper(unRet.c); #endif return(unRet.key); } UINT _ParseExtsToEXTKEYs(LPCTSTR pszNext, EXTKEY *pExtKeys, UINT cExts) { UINT cSaveExts = cExts; Assert(cExts != 0); for ( ; ; ) { if (*pszNext <= TEXT(' ')) { // skip all spaces and control characters, which cannot // be DBCS lead bytes, and cannot be in old MS-DOS // extensions if (!*pszNext) { // We got to the end of the string break; } ++pszNext; continue; } // *pszNext must now point to a character greater than ' ' *pExtKeys = _ExtToEXTKEY(pszNext); if (*pExtKeys) { // Don't save invalid extensions --cExts; ++pExtKeys; } if (cExts == 0) { // We only save so many extensions break; } for ( ; *pszNext>TEXT(' '); pszNext=CharNext(pszNext)) { // skip to the next space // HACKHACK: note we are checking for greater than // space, so we will stop at control characters or // NULL, which will be dealt with at the beginning of // the next iteration of this loop } } return(cSaveExts - cExts); } void _InitializeExcludeFileExts(void) { TCHAR szExcludeFileExts[MAX_PATH]; // We only call this in _Initialize_SharedData, so no need to // protect this function // BUGBUG: If the registry changes, we will not update, // but we don't really expect that to happen very often // so no big deal // We need to be certain the first DWORDs are exactly what we think // they are _SHGetExcludeFileExts(szExcludeFileExts, ARRAYSIZE(szExcludeFileExts)); s_ExcludeFileExts.cExts = _ParseExtsToEXTKEYs(szExcludeFileExts, s_ExcludeFileExts.ExtKeys, ARRAYSIZE(s_ExcludeFileExts.ExtKeys)); // Also we are tagging onto this function the reading in of the // "File" and "Folder class names as a clean place to do it... LoadString(HINST_THISDLL, IDS_FOLDERTYPENAME, g_szFolderTypeName, ARRAYSIZE(g_szFolderTypeName)); // For Files LoadString(HINST_THISDLL, IDS_FILETYPENAME, g_szFileTypeName, ARRAYSIZE(g_szFileTypeName)); // Load the template for untyped files, so we can generate a suitable string LoadString(HINST_THISDLL, IDS_EXTTYPETEMPLATE, g_szFileTemplate, ARRAYSIZE(g_szFileTemplate)); } int _SHFindExcludeExt(LPCTSTR pszFileName) { DWORD dwEDI; BYTE fFound; LPCTSTR pszExt = PathFindExtension(pszFileName); if (pszExt[0] == 0) return -1; // initialize this stuff if necessary if (s_ExcludeFileExts.cExts == 0) { _InitializeExcludeFileExts(); Assert(s_ExcludeFileExts.cExts != 0); } #ifdef WINNT { ULONG i; EXTKEY SearchTarget; EXTKEY * pSearchTarget = &SearchTarget; *pSearchTarget = _ExtToEXTKEY(pszExt+1); for (i=0; iszFolder)) { // // NOTES: We assume SHGetpathFromIDList fails only bacause // the path is too long in this context. If not, we should // change SHGetPathFromIDList so that it returns HRESULT // instead of BOOL. // if (hwndOwner) { ShellMessageBox(HINST_THISDLL, hwndOwner, MAKEINTRESOURCE(IDS_ENUMERR_PATHTOOLONG), NULL, // get the title from hwndOwner MB_OK | MB_ICONHAND); } LocalFree((HLOCAL)pefile); *ppenumUnknown = NULL; return E_INVALIDARG; } // See if the name mapper is interested in this one NPTRegisterNameToPidlTranslation(pefile->szFolder, pidlEnum); pefile->grfFlags = grfFlags; if (this) { #ifndef USE_OLEDB // BUGBUG This assert is not valid when we've got a // COFSFolder (JonBe) Assert(this->sf.lpVtbl->AddRef == CFSFolder_AddRef); #endif CFSFolder_AddRef(&this->sf); } pefile->pfsf = this; // BUGBUG: we should do this for other values of grfFlags too if (grfFlags == SHCONTF_FOLDERS) { // use mask to only find folders, mask is in the hi byte of dwFileAttributes // algorithm: (((attrib_on_disk & mask) ^ mask) == 0) pefile->finddata.dwFileAttributes = (FILE_ATTRIBUTE_DIRECTORY << 8) | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY; // signature to tell kernel to use the attribs specified pefile->finddata.dwReserved0 = 0x56504347; // sshh.. secret } do { TryAgain: idErrorRes = IDS_ENUMERR_FSTEMPLATE; dwFlags = MB_RETRYCANCEL | MB_ICONHAND; if (!PathCombine(pefile->szFolder, pefile->szFolder, c_szStarDotStar)) { // // Path is too long. // if (hwndOwner) { ShellMessageBox(HINST_THISDLL, hwndOwner, MAKEINTRESOURCE(IDS_ENUMERR_PATHTOOLONG), NULL, // get the title from hwndOwner MB_OK | MB_ICONHAND); } hres = E_INVALIDARG; break; } pefile->hfind = FindFirstFileRetry(hwndOwner, pefile->szFolder, &pefile->finddata, &fIsNet); PathRemoveFileSpec(pefile->szFolder); if (pefile->hfind != INVALID_HANDLE_VALUE) { pefile->fNext = TRUE; hres = SHCreateEnumObjects(hwndOwner, pefile, CFSFolder_EnumCallBack, ppenumUnknown); break; // from do-while } else { err = GetLastError(); switch (err) { case ERROR_NO_MORE_FILES: // what dos returns case ERROR_FILE_NOT_FOUND: // win32 compatible // an empty folder (probalby a root) // This is not an error. We should create an empty object. pefile->fNext = FALSE; Assert(pefile->hfind == INVALID_HANDLE_VALUE); hres = SHCreateEnumObjects(hwndOwner, pefile, CFSFolder_EnumCallBack, ppenumUnknown); goto Done; case ERROR_GEN_FAILURE: { int drive = PathGetDriveNumber(pefile->szFolder); // general failue (disk not formatted) if (PathIsRemovable(pefile->szFolder)) { if (ShellMessageBox(HINST_THISDLL, hwndOwner, MAKEINTRESOURCE(IDS_UNFORMATTED), NULL, MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_YESNO, (DWORD)(drive + TEXT('A'))) == IDYES) { switch (SHFormatDrive(hwndOwner, drive, SHFMT_ID_DEFAULT, 0)) { case SHFMT_ERROR: case SHFMT_NOFORMAT: ShellMessageBox(HINST_THISDLL, hwndOwner, MAKEINTRESOURCE(IDS_NOFMT), NULL, MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK, (DWORD)(drive + TEXT('A'))); hres = HRESULT_FROM_WIN32(err); goto Done; case SHFMT_CANCEL: hres = HRESULT_FROM_WIN32(ERROR_CANCELLED); goto Done; default: goto TryAgain; // Disk should now be formatted, verify } } else { hres = HRESULT_FROM_WIN32(ERROR_CANCELLED); goto Done; } } else { goto DoDefault; } break; } case ERROR_PATH_NOT_FOUND: idErrorRes = IDS_ENUMERR_PATHNOTFOUND; dwFlags = MB_OK | MB_ICONHAND; // fall through... default: DoDefault: hres = HRESULT_FROM_WIN32(err); if (fIsNet) { // BillV: No retry for network error dwFlags = MB_OK | MB_ICONHAND; } break; } } } while (SHEnumErrorMessageBox(hwndOwner, idErrorRes, err, pefile->szFolder, fIsNet, dwFlags)==IDRETRY); Done: if (FAILED(hres)) { if (pefile->pfsf) { pefile->pfsf->sf.lpVtbl->Release(&pefile->pfsf->sf); } LocalFree((HLOCAL)pefile); *ppenumUnknown = NULL; } else { LPCITEMIDLIST pidlRecent; ENTERCRITICAL; pidlRecent = GetSpecialFolderIDList(NULL, CSIDL_RECENT, TRUE); if (pidlRecent && ILIsEqual(pidlRecent, pidlEnum)) { pefile->grfFlags |= SHCONTF_RECENTDOCSDIR; // open it now so that each open within the enum is fast. // close at release time OpenRecentDocMRU(); } if (!(pefile->grfFlags & SHCONTF_INCLUDEHIDDEN)) { LPCITEMIDLIST pidlDesktopDir = GetSpecialFolderIDList(NULL, CSIDL_DESKTOPDIRECTORY, TRUE); if (pidlDesktopDir && ILIsParent(pidlEnum, pidlDesktopDir, TRUE)) { pefile->pidfHide = (LPIDFOLDER)ILClone(ILFindLastID(pidlDesktopDir)); } } LEAVECRITICAL; } } else { hres = E_OUTOFMEMORY; *ppenumUnknown = NULL; } return hres; } STDMETHODIMP CFSFolder_EnumObjects(LPSHELLFOLDER psf, HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST *ppenumUnknown) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); return FS_EnumObjects(this, hwndOwner, this->pidl, grfFlags, ppenumUnknown); } STDMETHODIMP CFSFolder_BindToObject(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, LPVOID *ppvOut) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); if (FS_IsValidID(pidl)) { LPCIDFOLDER pidf = (LPCIDFOLDER)pidl; // // We don't support binding to non-folder/junction. // if (!FS_IsFolder(pidf) && !FS_IsJunction(pidf) && (FS_GetType(pidf) != SHID_FS) && (FS_GetType(pidf) != SHID_FS_UNICODE)) { return E_NOINTERFACE; } return FSBindToObject( (psf->lpVtbl==&c_FSBrfFolderVtbl) ? &CLSID_Briefcase : &CLSID_NULL, this->pidl, pidl, pbc, riid, ppvOut); } return E_INVALIDARG; } // in: // pszName file spec part // pszDir path part of name to know how to limit the long name... // // out: // pszLinkName - Full path to link name (May fit in 8.3...) // void _BuildLinkName(LPTSTR pszLinkName, LPCTSTR pszName, LPCTSTR pszDir, BOOL fLinkTo) { TCHAR szLinkTo[40]; // "Link to %s.lnk" TCHAR szTemp[MAX_PATH + 40]; if (fLinkTo) { // check to see if we're in the "don't ever say 'shortcut to' mode" LoadUseLinkPrefixCount(); if (!g_iUseLinkPrefix) { fLinkTo = FALSE; } else if (g_iUseLinkPrefix > 0) { if (g_iUseLinkPrefix < MAXLINKPREFIXCOUNT) { g_iUseLinkPrefix += SHORTCUT_PREFIX_INCR; SaveUseLinkPrefixCount(); } } } if (!fLinkTo) { // Generate the title of this link ("XX.lnk") LoadString(HINST_THISDLL, IDS_LINKEXTENSION, szLinkTo, ARRAYSIZE(szLinkTo)); wsprintf(szTemp, szLinkTo, pszName); PathCleanupSpec(pszDir, szTemp); // get rid of illegal chars lstrcpyn(pszLinkName, szTemp, MAX_PATH); } else { // Generate the title of this link ("Shortcut to XX.lnk") int iMax; int cch; LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo)); wsprintf(szTemp, szLinkTo, pszName); PathCleanupSpec(pszDir, szTemp); // get rid of illegal chars cch = lstrlen(szTemp); // BUGBUG:: Should find out the max component length for the volume // as their may be LFN valumes whoes max is less than 255. iMax = MAX_PATH - lstrlen(pszDir) - 2; if (iMax > 255) iMax = 255; // Note we can not simply do a lstrcpyn as it would remove // the extension... if (cch <= iMax) lstrcpy(pszLinkName, szTemp); else { // Find the last . which should be 3 chars from end... LPCTSTR pszExt = PathFindExtension(szTemp); lstrcpyn(pszLinkName, szTemp, iMax-5); // make sure room lstrcat(pszLinkName, pszExt); } } Assert(PathIsLink(pszLinkName)); } // get the name and flags of an absolute IDlist HRESULT _SHGetNameAndFlags(LPCITEMIDLIST pidl, DWORD dwGDNFlags, LPTSTR pszName, UINT cchName, LONG *pFlags) { HRESULT hres; IShellFolder *psf; VDATEINPUTBUF(pszName, TCHAR, cchName); if (pszName) *pszName = 0; hres = SHBindToIDListParent(pidl, &IID_IShellFolder, &psf, &pidl); if (SUCCEEDED(hres)) { STRRET str; hres = psf->lpVtbl->GetDisplayNameOf(psf, pidl, dwGDNFlags, &str); if (SUCCEEDED(hres)) { if (pszName) StrRetToStrN(pszName, cchName, &str, pidl); if (pFlags) { *pFlags = SFGAO_FILESYSTEM|SFGAO_LINK; hres = psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, pFlags); } } psf->lpVtbl->Release(psf); } return hres; } // return a new destination path for a link // // in: // fErrorSoTryDesktop we are called because there was an error saving // the shortcut and we want to prompt to see if the // desktop should be used. // // out: // ppszPath caller must free, returned path // // returns: // // IDYES user said yes to creating a link at new place // IDNO user said no to creating a link at new place // -1 error // int _PromptTryDesktopLinks(HWND hwnd, LPTSTR *ppszPath, BOOL fErrorSoTryDesktop) { TCHAR szPath[MAX_PATH]; int idOk; if (!SHGetSpecialFolderPath(hwnd, szPath, CSIDL_DESKTOPDIRECTORY, FALSE)) return -1; // fail no desktop dir if (fErrorSoTryDesktop) { // Fail, if *ppszPath already points to the desktop directory. if (*ppszPath && lstrcmpi(szPath, *ppszPath) == 0) return -1; idOk = ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_TRYDESKTOPLINK), MAKEINTRESOURCE(IDS_LINKTITLE), MB_YESNO | MB_ICONHAND); } else { ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_MAKINGDESKTOPLINK), MAKEINTRESOURCE(IDS_LINKTITLE), MB_OK | MB_ICONASTERISK); idOk = IDYES; } if (idOk == IDYES) { LPTSTR pszTemp = (LPTSTR)LocalAlloc(LPTR, MAX_PATH*SIZEOF(TCHAR)); if (pszTemp) { lstrcpy(pszTemp, szPath); *ppszPath = pszTemp; } } return idOk; // return yes or no } // // BOOL WINAPI SHGetNewLinkInfo(LPCTSTR pszpdlLinkTo, LPCTSTR pszDir, LPTSTR pszName, BOOL * pfMustCopy, UINT uFlags) { BOOL fDosApp=FALSE; BOOL fLongFileNames = IsLFNDrive(pszDir); SHFILEINFO sfi; *pfMustCopy = FALSE; if (uFlags & SHGNLI_PIDL) { if (FAILED(_SHGetNameAndFlags((LPITEMIDLIST)pszpdlLinkTo, SHGDN_NORMAL, pszName, MAX_PATH, &(sfi.dwAttributes)))) { return(FALSE); } } else { if (SHGetFileInfo(pszpdlLinkTo, 0, &sfi, SIZEOF(sfi), SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES | ((uFlags & SHGNLI_PIDL) ? SHGFI_PIDL : 0))) { lstrcpy(pszName, sfi.szDisplayName); } else { return(FALSE); } } if (PathCleanupSpec(pszDir, pszName) & PCS_FATAL) return FALSE;; // // WARNING: From this point on, sfi.szDisplayName may be re-used to // contain the file path of the PIDL we are linking to. Don't rely on // it containing the display name. // if (sfi.dwAttributes & SFGAO_FILESYSTEM) { LPTSTR pszPathSrc; if (uFlags & SHGNLI_PIDL) { pszPathSrc = sfi.szDisplayName; SHGetPathFromIDList((LPCITEMIDLIST)pszpdlLinkTo, pszPathSrc); } else { pszPathSrc = (LPTSTR)pszpdlLinkTo; } fDosApp = (lstrcmpi(PathFindExtension(pszPathSrc), c_szDotPif) == 0) || (LOWORD(GetExeType(pszPathSrc)) == 0x5A4D); // 'MZ' if (sfi.dwAttributes & SFGAO_LINK) { *pfMustCopy = TRUE; lstrcpy(pszName, PathFindFileName(pszPathSrc)); } else { // // when making a link to a drive root. special case a few things // // if we are not on a LFN drive, dont use the full name, just // use the drive letter. "C.LNK" not "Label (C).LNK" // // if we are making a link to removable media, we dont want the // label as part of the name, we want the media type. // // CD-ROM drives are currently the only removable media we // show the volume label for, so we only need to special case // cdrom drives here. // if (PathIsRoot(pszPathSrc) && !PathIsUNC(pszPathSrc)) { if (!fLongFileNames) { lstrcpy(pszName, pszPathSrc); } else if (IsCDRomDrive(DRIVEID(pszPathSrc))) { LoadString(HINST_THISDLL, IDS_DRIVES_CDROM, pszName, MAX_PATH); } } } if (fLongFileNames && fDosApp) { int hPif = PifMgr_OpenProperties(pszPathSrc, NULL, 0, OPENPROPS_INHIBITPIF); if (hPif) { PROPPRG PP; BOOL fGotProps; _fmemset(&PP, 0, SIZEOF(PP)); fGotProps = PifMgr_GetProperties(hPif,(LPCSTR)MAKEINTATOM(GROUP_PRG), &PP, SIZEOF(PP), 0); PifMgr_CloseProperties(hPif, 0); if (fGotProps && ((PP.flPrgInit & PRGINIT_INFSETTINGS) || ((PP.flPrgInit & (PRGINIT_NOPIF | PRGINIT_DEFAULTPIF)) == 0))) { #ifdef UNICODE MultiByteToWideChar(CP_ACP, 0, PP.achTitle, -1, pszName, MAX_PATH); #else lstrcpy(pszName, PP.achTitle); #endif } } } } if (!(*pfMustCopy)) { // create full dest path name. only use template iff long file names // can be created and the caller requested it. _BuildLinkName will // truncate files on non-lfn drives and clean up any invalid chars. _BuildLinkName(pszName, pszName, pszDir, (!(*pfMustCopy) && fLongFileNames && (uFlags & SHGNLI_PREFIXNAME))); } if (fDosApp) { PathRenameExtension(pszName, c_szDotPif); } // make sure the name is unique PathYetAnotherMakeUniqueName(pszName, pszDir, pszName, pszName); return(TRUE); } #ifdef UNICODE BOOL WINAPI SHGetNewLinkInfoA(LPCSTR pszpdlLinkTo, LPCSTR pszDir, LPSTR pszName, BOOL * pfMustCopy, UINT uFlags) { ThunkText * pThunkText; if (uFlags & SHGNLI_PIDL) { // 1 string (pszpdlLinkTo is a pidl) pThunkText = ConvertStrings(2, NULL, pszDir); pThunkText->m_pStr[0] = (LPWSTR)pszpdlLinkTo; } else { // 2 strings pThunkText = ConvertStrings(2, pszpdlLinkTo, pszDir); } if (pThunkText) { WCHAR wszName[MAX_PATH] = L""; BOOL bResult = SHGetNewLinkInfoW(pThunkText->m_pStr[0], pThunkText->m_pStr[1], wszName, pfMustCopy, uFlags); LocalFree(pThunkText); if (bResult) { BOOL fDefUsed; // Thunk the output result string back to ANSI. If the conversion fails, // or if the default char is used, we fail the API call. // BUGBUG (DavePl) returns irreversibly mapped chars without warning/error if (0 == WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, wszName, -1, pszName, MAX_PATH, "_", &fDefUsed) || fDefUsed) { SetLastError((DWORD)E_FAIL); // BUGBUG - need better error value return FALSE; } } } else { return FALSE; } } #else // BUGBUG (DavePl) Move to shlunimp BOOL WINAPI SHGetNewLinkInfoW(LPCWSTR pszpdlLinkTo, LPCWSTR pszDir, LPWSTR pszName, BOOL * pfMustCopy, UINT uFlags) { return FALSE; } #endif // // in: // pidlTo HRESULT CreateLinkToPidl(LPCITEMIDLIST pidlTo, IShellLink *psl, LPCTSTR pszDir, LPITEMIDLIST *ppidl, BOOL fUseLinkTemplate) { HRESULT hres = E_FAIL; TCHAR szPathDest[MAX_PATH]; BOOL fCopyLnk; if (SHGetNewLinkInfo((LPTSTR)pidlTo, pszDir, szPathDest, &fCopyLnk, fUseLinkTemplate ? SHGNLI_PIDL | SHGNLI_PREFIXNAME : SHGNLI_PIDL)) { TCHAR szPathSrc[MAX_PATH]; BOOL fPath = SHGetPathFromIDList(pidlTo, szPathSrc); // get source if (fCopyLnk) { Assert(fPath); DebugMsg(DM_TRACE, TEXT("Link to Link calling CopyFile('%s','%s')"), szPathSrc, szPathDest); if (CopyFile(szPathSrc, szPathDest, TRUE)) { hres = S_OK; SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szPathDest, NULL); SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPathDest, NULL); } else { DebugMsg(DM_TRACE, TEXT("****copy failed (%d)"),GetLastError()); } } else { IPersistFile *ppf; psl->lpVtbl->SetIDList(psl, pidlTo); // // make sure the working directory is set to the same // directory as the app (or document). // // dont do this for non-FS pidls (ie control panel) // // what about a UNC directory? we go ahead and set // it, wont work for a WIn16 app. // if (fPath && !PathIsDirectory(szPathSrc)) { Assert(!PathIsRelative(szPathSrc)); PathRemoveFileSpec(szPathSrc); psl->lpVtbl->SetWorkingDirectory(psl, szPathSrc); } hres = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf); if (SUCCEEDED(hres)) { WCHAR wszPath[ARRAYSIZE(szPathDest)]; StrToOleStr(wszPath, szPathDest); hres = ppf->lpVtbl->Save(ppf, wszPath, TRUE); ppf->lpVtbl->Release(ppf); } } } if (ppidl) { *ppidl = SUCCEEDED(hres) ? SHSimpleIDListFromPath(szPathDest) : NULL; } return hres; } // out: // ppszDir caller must free this, returned new destination of link // HRESULT _CreateLinkWithRetry(HWND hwnd, LPCITEMIDLIST pidlTo, IShellLink *psl, LPTSTR *ppszDir, UINT fFlags, LPITEMIDLIST *ppidl) { HRESULT hres; if (ppidl) *ppidl = NULL; // assume error if (*ppszDir && (fFlags & SHCL_CONFIRM)) { hres = CreateLinkToPidl(pidlTo, psl, *ppszDir, ppidl, (fFlags & SHCL_USETEMPLATE)); } else { hres = E_FAIL; } // if we were unable to save, ask user if they want us to // try it again but change the path to the desktop. if (FAILED(hres)) { int id; if (hres == STG_E_MEDIUMFULL) { DebugMsg(DM_TRACE, TEXT("failed to create link because disk is full")); id = IDYES; } else { id = _PromptTryDesktopLinks(hwnd, ppszDir, (fFlags & SHCL_CONFIRM)); if (id == IDYES && *ppszDir) { hres = CreateLinkToPidl(pidlTo, psl, *ppszDir, ppidl, (fFlags & SHCL_USETEMPLATE)); } } // // we failed to create the link complain to the user. // if (FAILED(hres) && id != IDNO) { ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CANNOTCREATELINK), MAKEINTRESOURCE(IDS_LINKTITLE), MB_OK | MB_ICONASTERISK); } } #ifdef DEBUG if (FAILED(hres) && ppidl) Assert(*ppidl == NULL); #endif return hres; } LPIDA DataObj_GetHIDA(LPDATAOBJECT pdtobj, STGMEDIUM *pmedium) { FORMATETC fmte = {g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; if (pmedium) { pmedium->pUnkForRelease = NULL; pmedium->hGlobal = NULL; } if (!pmedium) { if (SUCCEEDED(pdtobj->lpVtbl->QueryGetData(pdtobj, &fmte))) return (LPIDA)TRUE; else return (LPIDA)FALSE; } else if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, pmedium))) { return (LPIDA)GlobalLock(pmedium->hGlobal); } return NULL; } void HIDA_ReleaseStgMedium(LPIDA pida, STGMEDIUM *pmedium) { if (pmedium->hGlobal && (pmedium->tymed==TYMED_HGLOBAL)) { #ifdef DEBUG if (pida) { LPIDA pidaT = (LPIDA)GlobalLock(pmedium->hGlobal); Assert(pidaT == pida); GlobalUnlock(pmedium->hGlobal); } #endif GlobalUnlock(pmedium->hGlobal); } else { Assert(0); } SHReleaseStgMedium(pmedium); } UINT DataObj_GetHIDACount(IDataObject *pdtobj) { STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { UINT count = pida->cidl; Assert(pida->cidl == HIDA_GetCount(medium.hGlobal)); HIDA_ReleaseStgMedium(pida, &medium); return count; } return 0; } // // This function creates links to the stuff in the IDataObject // // Arguments: // hwnd for any UI // pszDir target directory (where to create links) // pDataObj data object describing files (array of idlist) // ppidl pointer to an array that receives pidls pointing to the new links // or Null if not interested HRESULT WINAPI SHCreateLinks(HWND hwnd, LPCTSTR pszDir, IDataObject *pDataObj, UINT fFlags, LPITEMIDLIST* ppidl) { IShellLink *psl; LPTSTR pszNewDir = (LPTSTR)pszDir; // may change below, const -> non const DECLAREWAITCURSOR; // HACK: call constructor directly (should call CoCreateInstance) HRESULT hres = CShellLink_CreateInstance(NULL, &IID_IShellLink, &psl); SetWaitCursor(); if (!(fFlags & SHCL_USEDESKTOP)) fFlags |= SHCL_CONFIRM; if (SUCCEEDED(hres)) { STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pDataObj, &medium); if (pida) { UINT i; for (i = 0; i < pida->cidl; i++) { LPITEMIDLIST pidlTo = IDA_ILClone(pida, i); if (pidlTo) { hres = _CreateLinkWithRetry(hwnd, pidlTo, psl, &pszNewDir, fFlags, ppidl ? &ppidl[i] : NULL); ILFree(pidlTo); if (FAILED(hres)) break; } } HIDA_ReleaseStgMedium(pida, &medium); } else { hres = E_OUTOFMEMORY; } psl->lpVtbl->Release(psl); } SHChangeNotifyHandleEvents(); if (pszNewDir != pszDir) LocalFree((HLOCAL)pszNewDir); ResetWaitCursor(); return hres; } void FS_PositionFileFromDrop(HWND hwnd, LPCTSTR pszFile) { LPITEMIDLIST pidlNew; if (SUCCEEDED(FSTree_SimpleIDListFromPath(PathFindFileName(pszFile), &pidlNew))) { SFM_SAP sap; SHChangeNotifyHandleEvents(); hwnd = DV_HwndMain2HwndView(hwnd); // // HACK ALERT: // // Note that we need to ask the defview to give us the drop // point in defview's screen coordinate (instead of usint // the pt parameter to IDropTarget::Drop). // See DefView_GetAnchorPoint to know how it works // (pdv->bDropAnchor should be TRUE at this point). // SendMessage(hwnd, SVM_GETANCHORPOINT, FALSE, (LPARAM)&sap.pt); sap.fMove = TRUE; sap.pidl = pidlNew; sap.uSelectFlags = SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED; SendMessage(hwnd, SVM_SELECTANDPOSITIONITEM, 1, (LPARAM)&sap); ILFree(pidlNew); } } void FS_FreeMoveCopyList(LPITEMIDLIST *ppidl, UINT cidl) { UINT i; // free everything for (i = 0; i < cidl; i++) { ILFree(ppidl[i]); } LocalFree(ppidl); } void FS_PositionItems(HWND hwndOwner, UINT cidl, const LPITEMIDLIST *ppidl, IDataObject *pdtobj, POINT *pptOrigin, BOOL fMove) { FORMATETC fmte = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; HWND hwnd; UINT i, cxItem, cyItem; int xMul, yMul, xDiv, yDiv; STGMEDIUM medium; POINT *pptItems = NULL; POINT pt; SFM_SAP *psap; ITEMSPACING is; if (!ppidl || !IsWindow(hwndOwner)) return; if (!(psap = GlobalAlloc(GPTR, SIZEOF(SFM_SAP) * cidl))) { return; } // select those objects; // this had better not fail hwnd = DV_HwndMain2HwndView(hwndOwner); if (fMove) { if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium)) && medium.hGlobal) { pptItems = (POINT *)GlobalLock(medium.hGlobal); pptItems++; // The first point is the anchor } else { // By default, drop at (-g_cxIcon/2, -g_cyIcon/2), and increase // x and y by icon dimension for each icon pt.x = ((-3 * g_cxIcon) / 2) + pptOrigin->x; pt.y = ((-3 * g_cyIcon) / 2) + pptOrigin->y; medium.hGlobal = NULL; } if (ShellFolderView_GetItemSpacing(hwndOwner, &is)) { xDiv = is.cxLarge; yDiv = is.cyLarge; xMul = is.cxSmall; yMul = is.cySmall; cxItem = is.cxSmall; cyItem = is.cySmall; } else { xDiv = yDiv = xMul = yMul = 1; cxItem = g_cxIcon; cyItem = g_cyIcon; } } for (i = 0; i < cidl; i++) { if (ppidl[i]) { psap[i].pidl = ILFindLastID(ppidl[i]); psap[i].fMove = fMove; if (fMove) { if (pptItems) { psap[i].pt.x = ((pptItems[i].x * xMul) / xDiv) + pptOrigin->x; psap[i].pt.y = ((pptItems[i].y * yMul) / yDiv) + pptOrigin->y; } else { pt.x += cxItem; pt.y += cyItem; psap[i].pt = pt; } } // do regular selection from all of the rest of the items psap[i].uSelectFlags = SVSI_SELECT; } } // do this special one for the first only psap[0].uSelectFlags = SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED; SendMessage(hwnd, SVM_SELECTANDPOSITIONITEM, cidl, (LPARAM)psap); GlobalFree(psap); if (fMove && medium.hGlobal) { GlobalUnlock(medium.hGlobal); SHReleaseStgMedium(&medium); } } void FS_MapName(LPVOID hNameMappings, LPTSTR pszPath) { int i; LPSHNAMEMAPPING pNameMapping; if (!hNameMappings) return; for (i = 0; (pNameMapping = SHGetNameMappingPtr(hNameMappings, i)) != NULL; i++) { if (lstrcmpi(pszPath, pNameMapping->pszOldPath) == 0) { lstrcpy(pszPath, pNameMapping->pszNewPath); break; } } } // convert null separated/terminated file list to array of pidls int FileListToPidlList(LPCTSTR lpszFiles, LPVOID hNameMappings, LPITEMIDLIST **pppidl) { int nItems = CountFiles(lpszFiles); LPITEMIDLIST * ppidl; TCHAR szPath[MAX_PATH]; int i = 0; ppidl = (void*)LocalAlloc(LPTR, nItems * SIZEOF(LPITEMIDLIST)); if (!ppidl) return 0; *pppidl = ppidl; while (*lpszFiles) { lstrcpy(szPath, lpszFiles); FS_MapName(hNameMappings, szPath); ppidl[i] = SHSimpleIDListFromPath(szPath); lpszFiles += lstrlen(lpszFiles) + 1; i++; } return i; } // create the pidl array that contains the destination file names. this is // done by taking the source file names, and translating them through the // name mapping returned by the copy engine. // // // in: // pszDir desitination of operation // pdtobj HIDA containg data object // hNameMappings used to translate names // // out: // *pppidl id array of length return value // # of items in pppida int FS_CreateMoveCopyList(IDataObject *pdtobj, LPVOID hNameMappings, LPITEMIDLIST **pppidl) { // // We use HDROP instead of HIDA for following two reasons: // (1) The source may not offer HIDA (such as the fonts folder) // (2) It requires less memory allocation. // #if 1 int nItems = 0; STGMEDIUM medium; FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; HRESULT hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium); if (SUCCEEDED(hres)) { HDROP hDrop = medium.hGlobal; nItems = DragQueryFile(hDrop, (UINT)-1, NULL, 0); *pppidl = (void*)LocalAlloc(LPTR, nItems * SIZEOF(LPITEMIDLIST)); if (*pppidl) { int i; for (i=nItems-1; i >= 0; i--) { TCHAR szPath[MAX_PATH]; DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)); FS_MapName(hNameMappings, szPath); (*pppidl)[i] = SHSimpleIDListFromPath(szPath); } } SHReleaseStgMedium(&medium); } #else int nItems; STGMEDIUM medium; LPIDA pida; nItems = 0; *pppidl = NULL; pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { LPITEMIDLIST *ppidl; nItems = pida->cidl; ppidl = (void*)LocalAlloc(LPTR, nItems * SIZEOF(LPITEMIDLIST)); if (ppidl) { int i; *pppidl = ppidl; // return this... for (i = nItems - 1; i >= 0; i--) { LPITEMIDLIST pidlAbs = IDA_ILClone(pida, i); if (pidlAbs) { TCHAR szPath[MAX_PATH]; SHGetPathFromIDList(pidlAbs, szPath); FS_MapName(hNameMappings, szPath); ppidl[i] = SHSimpleIDListFromPath(szPath); ILFree(pidlAbs); } } } else nItems = 0; HIDA_ReleaseStgMedium(pida, &medium); } #endif return nItems; } // // in: // pszPath destination path of the operation // pszFiles source files list, may be NULL // hNameMappings name mappings result from the copy operaton // void FS_MoveSelectIcons(LPFSTHREADPARAM pfsthp, LPVOID hNameMappings, LPCTSTR pszFiles, BOOL fMove) { LPITEMIDLIST *ppidl = NULL; int cidl; if (pszFiles) { cidl = FileListToPidlList(pszFiles, hNameMappings, &ppidl); } else { cidl = FS_CreateMoveCopyList(pfsthp->pDataObj, hNameMappings, &ppidl); } if (ppidl) { FS_PositionItems(pfsthp->pfsdtgt->hwndOwner, cidl, ppidl, pfsthp->pDataObj, &pfsthp->ptDrop, fMove); FS_FreeMoveCopyList(ppidl, cidl); } } // this is the ILIsEqual which matches up the desktop with the desktop directory. BOOL FSILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { LPITEMIDLIST pidlUse1, pidlUse2; BOOL fSame; pidlUse1 = SHLogILFromFSIL(pidl1); if (pidlUse1) pidl1 = pidlUse1; pidlUse2 = SHLogILFromFSIL(pidl2); if (pidlUse2) pidl2 = pidlUse2; fSame = ILIsEqual(pidl1, pidl2); if (pidlUse1) ILFree(pidlUse1); if (pidlUse2) ILFree(pidlUse2); return fSame; } // this is the ILIsParent which matches up the desktop with the desktop directory. BOOL FSILIsParent(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { LPITEMIDLIST pidlUse1, pidlUse2; BOOL fSame; pidlUse1 = SHLogILFromFSIL(pidl1); if (pidlUse1) pidl1 = pidlUse1; pidlUse2 = SHLogILFromFSIL(pidl2); if (pidlUse2) pidl2 = pidlUse2; fSame = ILIsParent(pidl1, pidl2, TRUE); if (pidlUse1) ILFree(pidlUse1); if (pidlUse2) ILFree(pidlUse2); return fSame; } // in: // pszDestDir destination dir for new file names // pszDestSpecs double null list of destination specs // // returns: // double null list of fully qualified destination file names to be freed // with LocalFree() // LPTSTR RemapDestNames(LPCTSTR pszDestDir, LPCTSTR pszDestSpecs) { UINT cbDestSpec = lstrlen(pszDestDir) * SIZEOF(TCHAR) + SIZEOF(TCHAR); LPCTSTR pszTemp; LPTSTR pszRet; UINT cbAlloc = SIZEOF(TCHAR); // for double NULL teriminaion of entire string // compute length of buffer to aloc for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlen(pszTemp) + 1) { // +1 for null teriminator cbAlloc += cbDestSpec + lstrlen(pszTemp) * SIZEOF(TCHAR) + SIZEOF(TCHAR); } pszRet = LocalAlloc(LPTR, cbAlloc); if (pszRet) { LPTSTR pszDest = pszRet; for (pszTemp = pszDestSpecs; *pszTemp; pszTemp += lstrlen(pszTemp) + 1) { PathCombine(pszDest, pszDestDir, pszTemp); pszDest += lstrlen(pszDest) + 1; Assert((UINT)((LPBYTE)pszDest - (LPBYTE)pszRet) < cbAlloc); Assert(*pszDest == 0); // zero init alloc } Assert((LPTSTR)((LPBYTE)pszRet + cbAlloc - SIZEOF(TCHAR)) >= pszDest); Assert(*pszDest == 0); // zero init alloc } return pszRet; } #ifdef SYNC_BRIEFCASE BOOL HandleSneakernetDrop(LPFSTHREADPARAM pfsthp, LPCITEMIDLIST pidlParent, LPCTSTR pszTarget); #endif // SYNC_BRIEFCASE void _HandleMoveOrCopy(LPFSTHREADPARAM pfsthp, HDROP hDrop, LPCTSTR pszPath) { DRAGINFO di; di.uSize = SIZEOF(di); DragQueryInfo(hDrop, &di); switch (pfsthp->dwEffect) { case DROPEFFECT_MOVE: if (pfsthp->fSameHwnd) { FS_MoveSelectIcons(pfsthp, NULL, NULL, TRUE); break; } // fall through... case DROPEFFECT_COPY: { SHFILEOPSTRUCT fo = { pfsthp->pfsdtgt->hwndOwner, (pfsthp->dwEffect == DROPEFFECT_COPY) ? FO_COPY : FO_MOVE, di.lpFileList, pszPath, FOF_WANTMAPPINGHANDLE | FOF_ALLOWUNDO }; LPTSTR pszDestNames = NULL; STGMEDIUM medium; FORMATETC fmte = {g_cfFileNameMap, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; // if they are in the same hwnd or to and from // the same directory, turn on the automatic rename on collision flag if (pfsthp->fSameHwnd) { fo.fFlags |= FOF_RENAMEONCOLLISION; } else { LPIDA pida = DataObj_GetHIDA(pfsthp->pDataObj, &medium); if (medium.hGlobal) { LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1); // make sure stuff under the desktop compares ok // to stuff under the desktop directory if (pidlParent) { INT i; BOOL fMoveToSame = FALSE; for (i = 0; i < (INT)pida->cidl; i++) { LPCITEMIDLIST pidlAbs; LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, (UINT)i); pidlAbs = ILCombine (pidlParent, pidl); if (!pidlAbs) continue; // if we're doing keyboard cut/copy/paste // to and from the same directories if (FSILIsParent(pfsthp->pfsdtgt->pidl, pidlAbs)) { if (pfsthp->dwEffect == DROPEFFECT_MOVE) { // if they're the same, do nothing on move fMoveToSame = TRUE; } else { // do rename on collision for copy; fo.fFlags |= FOF_RENAMEONCOLLISION; } } } if (fMoveToSame) { goto DoneWithData; } #ifdef SYNC_BRIEFCASE // Handle sneaker-net for briefcase; did briefcase // handle it? if (HandleSneakernetDrop(pfsthp, pidlParent, pszPath)) { // Yes; don't do anything DebugMsg(DM_TRACE, TEXT("Briefcase handled drop")); HIDA_ReleaseStgMedium(pida, &medium); goto Exit; } #endif } DoneWithData: HIDA_ReleaseStgMedium(pida, &medium); } } // see if there is a rename mapping from recycle bin (or someone else) Assert(fmte.cfFormat == g_cfFileNameMap); if (pfsthp->pDataObj->lpVtbl->GetData(pfsthp->pDataObj, &fmte, &medium) == S_OK) { DebugMsg(DM_TRACE, TEXT("Got rename mapping")); pszDestNames = RemapDestNames(pszPath, (LPTSTR)GlobalLock(medium.hGlobal)); if (pszDestNames) { fo.pTo = pszDestNames; fo.fFlags |= FOF_MULTIDESTFILES; // HACK, this came from the recycle bin, don't allow undo fo.fFlags &= ~FOF_ALLOWUNDO; #ifdef DEBUG { UINT cFrom = 0, cTo = 0; LPCTSTR pszTemp; for (pszTemp = fo.pTo; *pszTemp; pszTemp += lstrlen(pszTemp) + 1) cTo++; for (pszTemp = fo.pFrom; *pszTemp; pszTemp += lstrlen(pszTemp) + 1) cFrom++; AssertMsg(cFrom == cTo, TEXT("dest count does not equal source")); } #endif } GlobalUnlock(medium.hGlobal); SHReleaseStgMedium(&medium); } // Check if there were any errors if (SHFileOperation(&fo) == 0 && !fo.fAnyOperationsAborted) { if (pfsthp->fBkDropTarget) ShellFolderView_SetRedraw(pfsthp->pfsdtgt->hwndOwner, 0); SHChangeNotifyHandleEvents(); // force update now if (pfsthp->fBkDropTarget) { FS_MoveSelectIcons(pfsthp, fo.hNameMappings, pszDestNames ? pszDestNames : NULL, pfsthp->fDragDrop); ShellFolderView_SetRedraw(pfsthp->pfsdtgt->hwndOwner, TRUE); } } if (fo.hNameMappings) SHFreeNameMappings(fo.hNameMappings); if (pszDestNames) { LocalFree((HLOCAL)pszDestNames); // HACK, this usually comes from the bitbucket // but in our shell, we don't handle the moves from the source if (pfsthp->dwEffect == DROPEFFECT_MOVE) BBCheckRestoredFiles(di.lpFileList); } } break; } Exit: SHFree(di.lpFileList); } // // This is the entry of "drop thread" // DWORD CALLBACK CFSDropTarget_DropThreadInit(LPVOID pv) { LPFSTHREADPARAM pfsthp = (LPFSTHREADPARAM)pv; HRESULT hres; STGMEDIUM medium; TCHAR szPath[MAX_PATH]; LPITEMIDLIST *ppidl; int i; FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; // // If the link is the only choice and this is a default drag & drop, // and it is not forced by the user, we should tell the user. // if (((pfsthp->pfsdtgt->grfKeyStateLast & (MK_LBUTTON|MK_CONTROL|MK_SHIFT)) == MK_LBUTTON) && pfsthp->fLinkOnly) { // // Note that we can not pass hwndOwner, because it might // not be activated. // UINT idMBox = ShellMessageBox(HINST_THISDLL, pfsthp->pfsdtgt->hwndOwner, MAKEINTRESOURCE(IDS_WOULDYOUCREATELINK), MAKEINTRESOURCE(IDS_LINKTITLE), MB_YESNO | MB_ICONQUESTION); Assert(pfsthp->dwEffect == DROPEFFECT_LINK); if (idMBox != IDYES) pfsthp->dwEffect = 0; } SHGetPathFromIDList(pfsthp->pfsdtgt->pidl, szPath); // destination switch (pfsthp->dwEffect) { case DROPEFFECT_MOVE: case DROPEFFECT_COPY: // asking for CF_HDROP hres = pfsthp->pDataObj->lpVtbl->GetData(pfsthp->pDataObj, &fmte, &medium); if (SUCCEEDED(hres)) { _HandleMoveOrCopy(pfsthp, (HDROP)medium.hGlobal, szPath); SHReleaseStgMedium(&medium); } break; case DROPEFFECT_LINK: if (pfsthp->fBkDropTarget) { i = DataObj_GetHIDACount(pfsthp->pDataObj); ppidl = (void*)LocalAlloc(LPTR, SIZEOF(LPITEMIDLIST) * i); } else ppidl = NULL; // passing ppidl == NULL is correct in failure case hres = SHCreateLinks(pfsthp->pfsdtgt->hwndOwner, szPath, pfsthp->pDataObj, pfsthp->pfsdtgt->grfKeyStateLast ? SHCL_USETEMPLATE : 0, ppidl); if (ppidl) { FS_PositionItems(pfsthp->pfsdtgt->hwndOwner, i, ppidl, pfsthp->pDataObj, &pfsthp->ptDrop, TRUE); FS_FreeMoveCopyList(ppidl, i); } break; } SHChangeNotifyHandleEvents(); // force update now pfsthp->pDataObj->lpVtbl->Release(pfsthp->pDataObj); pfsthp->pfsdtgt->dropt.lpVtbl->Release(&pfsthp->pfsdtgt->dropt); #ifdef DEBUG { extern UINT g_cRefExtra; g_cRefExtra--; } #endif LocalFree((HLOCAL)pfsthp); return 0; } BOOL AreTheyAllExe(HDROP hDrop) { UINT i; TCHAR szPath[MAX_PATH]; for (i = 0; DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)); i++) { // // PathIsBinaryExe() returns TRUE for .exe, .com // PathIsExe() returns TRUE for .exe, .com, .bat, .cmd, .pif // // we dont want to treat .pif files as EXE files, because the // user sees them as links. // if (!PathIsBinaryExe(szPath)) return FALSE; } return TRUE; } // // This function returns TRUE, if the default operation should be LINK. // // Algorithm: // If the source is a root drive (such as C:\), return TRUE. // If the sources are programs // And If the source and dest are on the same drive, return TRUE. // If either source or dest is on a removeable media, return TRUE. // otherwise return FALSE. // BOOL FS_IsLinkDefault(LPCTSTR szFolder, HDROP hDrop, LPCTSTR pszFirst, BOOL fSameRoot) { if (PathIsRoot(pszFirst)) { return TRUE; } if (AreTheyAllExe(hDrop)) { if (fSameRoot) { return TRUE; } if (!PathIsRemovable(szFolder) && !PathIsRemovable(pszFirst)) { return TRUE; } } return FALSE; } BOOL FS_IsBriefcaseRoot(LPCITEMIDLIST pidl) { CLSID clsid; BOOL bRet = FALSE; // Is this a system directory? if ((SIL_GetType(pidl) & (SHID_FS_DIRECTORY | SHID_JUNCTION)) == (SHID_FS_DIRECTORY | SHID_JUNCTION)) { // Yes; does it have the briefcase CLSID? const UNALIGNED CLSID * pclsid = FS_GetCLSID((LPCIDFOLDER)pidl); if (pclsid) { clsid = *pclsid; if (IsEqualCLSID(&clsid, &CLSID_Briefcase)) { // Yes bRet = TRUE; } } } return bRet; } #ifdef SYNC_BRIEFCASE // Is pidl a file-system object? (This returns TRUE also if pidl is the desktop.) BOOL IsFSObject(LPCITEMIDLIST pidl) { BOOL bRet = FALSE; LPITEMIDLIST pidlLast; pidlLast = ILFindLastID(pidl); if (pidl != pidlLast) { DWORD dwAttr = SFGAO_FILESYSTEM; IShellFolder * psfParent; LPITEMIDLIST pidlClone = ILClone(pidl); if (pidlClone) { // Let's verify that this is a file sytem object. // if (SUCCEEDED(SHBindToIDListParent(pidlClone, &IID_IShellFolder, &psfParent, NULL))) { if (SUCCEEDED(psfParent->lpVtbl->GetAttributesOf(psfParent, 1, &pidlLast, &dwAttr)) && (dwAttr & SFGAO_FILESYSTEM) == SFGAO_FILESYSTEM ) { bRet = TRUE; } psfParent->lpVtbl->Release(psfParent); } ILFree(pidlClone); } } else { // It is the desktop. Consider it part of the file-system. bRet = TRUE; } return bRet; } BOOL IsBriefcaseRoot(IDataObject *pDataObj) { BOOL bRet = FALSE; STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pDataObj, &medium); if (pida) { // Is there a briefcase root in this pDataObj? if (0 < pida->cidl) { LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1); // Is this a file-system source? if (IsFSObject(pidlParent)) { // Yes int i; for (i = pida->cidl - 1; i >= 0; i--) { bRet = FS_IsBriefcaseRoot(IDA_GetIDListPtr(pida, i)); if (bRet) break; } } } HIDA_ReleaseStgMedium(pida, &medium); } return bRet; } // // Returns: // If the data object does NOT contain HDROP -> "none" // else if the source is root or exe -> "link" // else if this is within a volume -> "move" // else if this is a briefcase -> "move" // else -> "copy" // DWORD _PickDefFSOperation(LPIDLDROPTARGET this) { FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; DWORD dwDefEffect = 0; // assume no HDROP STGMEDIUM medium; if (SUCCEEDED(this->pdtobj->lpVtbl->GetData(this->pdtobj, &fmte, &medium))) { HDROP hDrop = medium.hGlobal; TCHAR szPath[MAX_PATH]; TCHAR szFolder[MAX_PATH]; BOOL fSameRoot; dwDefEffect = DROPEFFECT_COPY; SHGetPathFromIDList(this->pidl, szFolder); // // Note that we pick the first one (focused one) to decide // the operation. // DragQueryFile(hDrop, 0, szPath, ARRAYSIZE(szPath)); fSameRoot = PathIsSameRoot(szPath, szFolder); // // Determine the default operation depending on the item. // if (FS_IsLinkDefault(szFolder, hDrop, szPath, fSameRoot)) { dwDefEffect = DROPEFFECT_LINK; } else if (fSameRoot) { dwDefEffect = DROPEFFECT_MOVE; } #ifdef SYNC_BRIEFCASE // Is a briefcase root getting dropped? else if (IsBriefcaseRoot(this->pdtobj)) { // Yes; default to "move" even if across volumes DebugMsg(DM_TRACE, TEXT("sh TR - FS::Drop the object is the briefcase")); dwDefEffect = DROPEFFECT_MOVE; } #endif SHReleaseStgMedium(&medium); } else // if (SUCCEEDED(...)) { // // GetData failed. Let's see if QueryGetData failed or not. // if (SUCCEEDED(this->pdtobj->lpVtbl->QueryGetData(this->pdtobj, &fmte))) { // // Succeeded. It means this data object has HDROP but can't // provide it until it is dropped. Let's assume we are copying. // dwDefEffect = DROPEFFECT_COPY; } } return dwDefEffect; } // // make sure that the default effect is among the allowed effects // DWORD _LimitDefaultEffect(DWORD dwDefEffect, DWORD dwEffectsAllowed) { if (dwDefEffect & dwEffectsAllowed) return dwDefEffect; if (dwEffectsAllowed & DROPEFFECT_COPY) return DROPEFFECT_COPY; if (dwEffectsAllowed & DROPEFFECT_MOVE) return DROPEFFECT_MOVE; if (dwEffectsAllowed & DROPEFFECT_LINK) return DROPEFFECT_LINK; return DROPEFFECT_NONE; } // // This function returns the default effect. // This function also modified *pdwEffect to indicate "available" operation. // DWORD CFSIDLDropTarget_GetDefaultEffect(LPIDLDROPTARGET this, DWORD grfKeyState, LPDWORD pdwEffectInOut, UINT *pidMenu) { DWORD dwDefEffect; UINT idMenu = POPUP_NONDEFAULTDD; DWORD dwEffectAvail = 0; // // First try file system operation (HDROP). // if (this->dwData & DTID_HDROP) { // // If HDROP exists, ignore the rest of formats. // dwEffectAvail |= DROPEFFECT_COPY | DROPEFFECT_MOVE; // // We don't support 'links' from HDROP (only from HIDA). // This is a known limitation and we have no plan to implement // it for Win95. // if (this->dwData & DTID_HIDA) dwEffectAvail |= DROPEFFECT_LINK; dwDefEffect = _PickDefFSOperation(this); // // BUGBUG (in OLE): We'll hit this assert because OLE doesn't marshal // IDataObject correctly when we are dragging over. // if (dwDefEffect == 0) { Assert(0); dwDefEffect = DROPEFFECT_MOVE; } } else { BOOL fContents = ((this->dwData & (DTID_CONTENTS | DTID_FDESCA)) == (DTID_CONTENTS | DTID_FDESCA) || (this->dwData & (DTID_CONTENTS | DTID_FDESCW)) == (DTID_CONTENTS | DTID_FDESCW)); if (fContents || (this->dwData & DTID_HIDA)) { if (this->dwData & DTID_HIDA) { dwEffectAvail = DROPEFFECT_LINK; dwDefEffect = DROPEFFECT_LINK; } if (fContents) { // // HACK: if there is a preferred drop effect and no HIDA // then just take the preferred effect as the available effects // this is because we didn't actually check the FD_LINKUI bit // back when we assembled dwData! (performance) // if ((this->dwData & (DTID_PREFERREDEFFECT | DTID_HIDA)) == DTID_PREFERREDEFFECT) { dwEffectAvail = this->dwEffectPreferred; // dwDefEffect will be set below } else if (this->dwData & DTID_FD_LINKUI) { dwEffectAvail = DROPEFFECT_LINK; dwDefEffect = DROPEFFECT_LINK; } else { dwEffectAvail |= DROPEFFECT_COPY | DROPEFFECT_MOVE; dwDefEffect = DROPEFFECT_COPY; } idMenu = POPUP_FILECONTENTS; } } } // // BUGBUG this should be moved to OLE's clipboard/dataobject code // (ie anybody provides these formats and OLE provides FILECONTENTS) // this will be more important as random containers get implemented // if (!dwEffectAvail) { // // Try scrap and doc-shortcut // if (this->dwData & DTID_OLELINK) { dwEffectAvail |= DROPEFFECT_LINK; dwDefEffect = DROPEFFECT_LINK; idMenu = POPUP_SCRAP; } if (this->dwData & DTID_OLEOBJ) { dwEffectAvail |= DROPEFFECT_COPY | DROPEFFECT_MOVE; dwDefEffect = DROPEFFECT_COPY; idMenu = POPUP_SCRAP; } } *pdwEffectInOut &= dwEffectAvail; // // Alter the default effect depending on modifier keys. // switch(grfKeyState & (MK_CONTROL | MK_SHIFT)) { case MK_CONTROL: dwDefEffect = DROPEFFECT_COPY; break; case MK_SHIFT: dwDefEffect = DROPEFFECT_MOVE; break; case MK_SHIFT|MK_CONTROL: dwDefEffect = DROPEFFECT_LINK; break; default: // // no modifier keys: // if the data object contains a preferred drop effect, try to use it // if (this->dwData & DTID_PREFERREDEFFECT) { DWORD dwPreferred = this->dwEffectPreferred & dwEffectAvail; if (dwPreferred) { if (dwPreferred & DROPEFFECT_MOVE) { dwDefEffect = DROPEFFECT_MOVE; } else if (dwPreferred & DROPEFFECT_COPY) { dwDefEffect = DROPEFFECT_COPY; } else if (dwPreferred & DROPEFFECT_LINK) { dwDefEffect = DROPEFFECT_LINK; } } } break; } if (pidMenu) *pidMenu = idMenu; DebugMsg(DM_TRACE, TEXT("CFSDT::GetDefaultEffect dwD=%x, *pdw=%x, idM=%d"), dwDefEffect, *pdwEffectInOut, idMenu); return _LimitDefaultEffect(dwDefEffect, *pdwEffectInOut); } // // CFSIDLDropTarget::DragEnter // STDMETHODIMP CFSIDLDropTarget_DragEnter(IDropTarget *pdropt, IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt); DWORD dwDefault; // let the base-class process it first. CIDLDropTarget_DragEnter(pdropt, pdtobj, grfKeyState, pt, pdwEffect); dwDefault = CFSIDLDropTarget_GetDefaultEffect(this, grfKeyState, pdwEffect, NULL); #if 1 // The cursor always indicates the default action. *pdwEffect = dwDefault; #else // The cursor indicates the default action only if left-dragged. if (grfKeyState & MK_LBUTTON) { *pdwEffect = dwDefault; } #endif this->dwEffectLastReturned = *pdwEffect; return S_OK; } BOOL IsInsideBriefcase(LPCITEMIDLIST pidlIn) { BOOL bRet = FALSE; LPITEMIDLIST pidl = ILClone(pidlIn); CLSID clsid, * pclsid; const UNALIGNED CLSID * uapclsid; Assert(IsFSObject(pidlIn)); if (pidl) { do { uapclsid = FS_GetCLSID((LPCIDFOLDER)ILFindLastID(pidl)); if (uapclsid) { clsid = *uapclsid; pclsid = &clsid; } else { pclsid = NULL; } // Is the folder a briefcase root? if (pclsid && IsEqualCLSID(pclsid, &CLSID_Briefcase)) { // Yes bRet = TRUE; break; } } while (ILRemoveLastID(pidl) && FS_IsFolder((LPIDFOLDER)ILFindLastID(pidl))); ILFree(pidl); } return bRet; } /*---------------------------------------------------------- Purpose: Determines if pidl listed in the hida is a briefcase on removable media Returns: TRUE if the above is true Cond: -- */ BOOL IsFromSneakernetBriefcase(LPCITEMIDLIST pidlSource, LPCITEMIDLIST pidlTarget) { BOOL bRet = FALSE; // Are the source and target file-system objects? if (IsFSObject(pidlSource) && IsFSObject(pidlTarget)) { LPITEMIDLIST pidlLast = ILFindLastID(pidlSource); // Yes; is the source not the desktop? // (Special case the desktop to prevent GP faults) if (pidlSource != pidlLast) { // Yes; is the source parent on removable media? LPITEMIDLIST pidlDrive = ILGetNext(pidlSource); // (pidlSource is a fully qualified pidl) BYTE type = SIL_GetType(pidlDrive); if (SHID_COMPUTER_REMOVABLE == type || SHID_COMPUTER_DRIVE525 == type || SHID_COMPUTER_DRIVE35 == type) { // Yes; is the target the desktop? // (Special case the desktop to prevent GP faults) pidlLast = ILFindLastID(pidlTarget); if (pidlTarget == pidlLast) { // Yes bRet = IsInsideBriefcase(pidlSource); } else { // No; is the target below the desktop? type = SIL_GetType(pidlTarget) & SHID_TYPEMASK; if (SHID_FS_DIRECTORY == type) { // Yes bRet = IsInsideBriefcase(pidlSource); } else { // No; is the target fixed media? pidlDrive = ILGetNext(pidlTarget); type = SIL_GetType(pidlDrive); if (SHID_COMPUTER_FIXED == type || SHID_COMPUTER_REMOTE == type || SHID_COMPUTER_RAMDISK == type || SHID_COMPUTER_NETDRIVE == type) { // Yes bRet = IsInsideBriefcase(pidlSource); } } } } } } return bRet; } BOOL HandleSneakernetDrop(LPFSTHREADPARAM pfsthp, LPCITEMIDLIST pidlParent, LPCTSTR pszTarget) { BOOL bRet = FALSE; Assert(pidlParent); Assert(pszTarget); // Is it being dragged from a mobile briefcase? if ((DROPEFFECT_COPY == pfsthp->dwEffect) && pfsthp->bSyncCopy) { // Yes LPBRIEFCASESTG pbrfstg; // Perform a sneakernet addition to the briefcase if (SUCCEEDED(BrfStg_CreateInstance(pidlParent, pfsthp->pfsdtgt->hwndOwner, &pbrfstg))) { // (Even if AddObject fails, return TRUE to prevent caller // from handling this) bRet = (S_FALSE != pbrfstg->lpVtbl->AddObject(pbrfstg, pfsthp->pDataObj, pszTarget, (DDIDM_SYNCCOPYTYPE == pfsthp->idCmd) ? AOF_FILTERPROMPT : AOF_DEFAULT, pfsthp->pfsdtgt->hwndOwner)); pbrfstg->lpVtbl->Release(pbrfstg); } } return bRet; } void SneakernetHook(IDataObject *pdtobj, LPCITEMIDLIST pidl, UINT *pidMenu, BOOL *pbSyncCopy) { // Is this the sneakernet case? STGMEDIUM medium; LPIDA pida; // (Default: leave *pidMenu as it is passed in) *pbSyncCopy = FALSE; // Default pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { LPCITEMIDLIST pidlParent = IDA_GetIDListPtr(pida, (UINT)-1); if (pidlParent) { if (IsFromSneakernetBriefcase(pidlParent, pidl)) { // Yes; show the non-default briefcase cm FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM mediumT; if (SUCCEEDED(pdtobj->lpVtbl->GetData(pdtobj, &fmte, &mediumT))) { extern BOOL DroppingAnyFolders(HDROP hDrop); // Are any folders being dropped? if (DroppingAnyFolders(mediumT.hGlobal)) *pidMenu = POPUP_BRIEFCASE_FOLDER_NONDEFAULTDD; // Yes else *pidMenu = POPUP_BRIEFCASE_NONDEFAULTDD; // No *pbSyncCopy = TRUE; SHReleaseStgMedium(&mediumT); } } } HIDA_ReleaseStgMedium(pida, &medium); } } #endif // BUGBUG: really should put up progress dialog HRESULT DataObj_SaveToFile(IDataObject *pdtobj, UINT cf, LONG lindex, LPCTSTR pszFile, DWORD dwFileSize) { STGMEDIUM medium; FORMATETC fmte; HRESULT hres; fmte.cfFormat = cf; fmte.ptd = NULL; fmte.dwAspect = DVASPECT_CONTENT; fmte.lindex = lindex; fmte.tymed = TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE; hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium); if (SUCCEEDED(hres)) { switch (medium.tymed) { case TYMED_HGLOBAL: { #ifdef UNICODE HFILE hfile = (HFILE)CreateFile( pszFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL); #else HFILE hfile = _lcreat(pszFile, 0); #endif if (hfile != (HFILE)INVALID_HANDLE_VALUE) { _hwrite(hfile, GlobalLock(medium.hGlobal), dwFileSize ? dwFileSize : GlobalSize(medium.hGlobal)); GlobalUnlock(medium.hGlobal); _lclose(hfile); } break; } case TYMED_ISTREAM: { IStream *pstm = OpenFileStream(pszFile, OF_CREATE | OF_WRITE | OF_SHARE_DENY_WRITE); if (pstm) { const ULARGE_INTEGER ul = {(UINT)-1, (UINT)-1}; // the whole thing hres = medium.pstm->lpVtbl->CopyTo(medium.pstm, pstm, ul, NULL, NULL); DebugMsg(DM_TRACE, TEXT("IStream::CopyTo() -> %x"), hres); pstm->lpVtbl->Release(pstm); } break; } case TYMED_ISTORAGE: { WCHAR wszNewFile[MAX_PATH]; IStorage *pstg; DebugMsg(DM_TRACE, TEXT("got IStorage")); StrToOleStr(wszNewFile, pszFile); hres = SHXStgCreateDocfile(wszNewFile, STGM_DIRECT | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, &pstg); if (SUCCEEDED(hres)) { hres = medium.pstg->lpVtbl->CopyTo(medium.pstg, 0, NULL, NULL, pstg); DebugMsg(DM_TRACE, TEXT("IStorage::CopyTo() -> %x"), hres); pstg->lpVtbl->Commit(pstg, STGC_OVERWRITE); pstg->lpVtbl->Release(pstg); } } break; default: AssertMsg(FALSE, TEXT("got typed that I didn't ask for %d"), medium.tymed); } if (SUCCEEDED(hres)) { SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, pszFile, NULL); SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, pszFile, NULL); } SHReleaseStgMedium(&medium); } return hres; } HRESULT FS_CreateFileFromClip(LPIDLDROPTARGET this, IDataObject *pdtobj, POINTL pt, DWORD *pdwEffect) { HRESULT hres; STGMEDIUM medium; FORMATETC fmteA = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; #ifdef UNICODE FORMATETC fmteW = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; BOOL fUnicode = FALSE; #endif // We should have only one bit set. Assert(*pdwEffect==DROPEFFECT_COPY || *pdwEffect==DROPEFFECT_LINK || *pdwEffect==DROPEFFECT_MOVE); // Try for UNICODE group descriptor first. If that succeeds, we won't bother trying to // ASCII since UNICODE is the "preferred" format. For ANSI builds, we only try for ANSI #ifdef UNICODE hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteW, &medium); if (SUCCEEDED(hres)) { fUnicode = TRUE; } else { hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteA, &medium); } #else hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteA, &medium); #endif if (SUCCEEDED(hres)) { int i; FILEGROUPDESCRIPTORA * pfgdA = (FILEGROUPDESCRIPTORA *)GlobalLock(medium.hGlobal); #ifdef UNICODE FILEGROUPDESCRIPTORW * pfgdW = (FILEGROUPDESCRIPTORW *) pfgdA; #endif DECLAREWAITCURSOR; SetWaitCursor(); for (i = 0; i < (int)pfgdA->cItems; i++) { TCHAR szPath[MAX_PATH]; SHGetPathFromIDList(this->pidl, szPath); #ifdef UNICODE { // BUGBUG (Davepl) Should this be in a try/except in case of bogus clipboard data? if (FALSE == fUnicode) { WCHAR szTemp[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, pfgdA->fgd[i].cFileName, -1, szTemp, ARRAYSIZE(szTemp)); PathAppend(szPath, szTemp); } else { PathAppend(szPath, pfgdW->fgd[i].cFileName); } } #else PathAppend(szPath, pfgdA->fgd[i].cFileName); #endif PathYetAnotherMakeUniqueName(szPath, szPath, NULL, NULL); if ((pfgdA->fgd[i].dwFlags & FD_ATTRIBUTES) && (pfgdA->fgd[i].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if (SHCreateDirectory(this->hwndOwner, szPath)) { *pdwEffect = 0; // failure break; } } else { hres = DataObj_SaveToFile(pdtobj, g_cfFileContents, i, szPath, pfgdA->fgd[i].dwFlags & FD_FILESIZE ? pfgdA->fgd[i].nFileSizeLow : 0); if (FAILED(hres)) { *pdwEffect = 0; break; } } if (SUCCEEDED(hres)) FS_PositionFileFromDrop(this->hwndOwner, szPath); } ResetWaitCursor(); GlobalUnlock(medium.hGlobal); SHReleaseStgMedium(&medium); } return S_OK; } STDMETHODIMP CFSIDLDropTarget_DragOver(LPDROPTARGET pdropt, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt); if (this->grfKeyStateLast != grfKeyState) { DWORD dwDefault; this->grfKeyStateLast = grfKeyState; dwDefault = CFSIDLDropTarget_GetDefaultEffect(this, grfKeyState, pdwEffect, NULL); #if 1 // The cursor always indicates the default action. *pdwEffect = dwDefault; #else // The cursor indicates the default action only if left-dragged. if (grfKeyState & MK_LBUTTON) { *pdwEffect = dwDefault; } #endif this->dwEffectLastReturned = *pdwEffect; } else { *pdwEffect = this->dwEffectLastReturned; } return S_OK; } STDMETHODIMP CFSIDLDropTarget_Drop(IDropTarget *pdropt, IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt); DWORD dwDefEffect; HRESULT hres; HKEY hkeyBaseProgID; HKEY hkeyProgID; UINT idMenu = POPUP_NONDEFAULTDD; BOOL fLinkOnly = FALSE; DRAGDROPMENUPARAM ddm; BOOL bSyncCopy; DWORD dwEffectPerformed = 0; IDataObject *pDroppedDataObject = pdtobj; // Remember for SetPerformedEffect. // // Notes: OLE will give us a different data object (fully marshalled) // from the one we've got on DragEnter. // if (pdtobj != this->pdtobj) { // // Since it might be a new, different data object, we need to release // our reference to the old one and take a reference to the new one. // The dobj is guaranteed to have a ref >= 2 in this case, so we don't // need to work through a temp copy // this->pdtobj->lpVtbl->Release(this->pdtobj); this->pdtobj = pdtobj; this->pdtobj->lpVtbl->AddRef(this->pdtobj); } // note, that on the drop the mouse buttons are not down so the grfKeyState // is not what we saw on the DragOver/DragEnter, thus we need to cache // the grfKeyState to detect left vs right drag // // Assert(this->grfKeyStateLast == grfKeyState); // BUGBUG: we really should check for "FileName" too... dwDefEffect = CFSIDLDropTarget_GetDefaultEffect(this, grfKeyState, pdwEffect, &idMenu); if (dwDefEffect == DROPEFFECT_NONE) { // have no clue what this is... DebugMsg(DM_TRACE, TEXT("Drop of unknown data")); *pdwEffect = DROPEFFECT_NONE; DAD_SetDragImage(NULL, NULL); hres = S_OK; goto DragLeaveAndReturn; } // Get the hkeyProgID and hkeyBaseProgID SHGetClassKey((LPIDFOLDER)this->pidl, &hkeyProgID, NULL, FALSE); SHGetBaseClassKey((LPIDFOLDER)this->pidl, &hkeyBaseProgID); #ifdef SYNC_BRIEFCASE SneakernetHook(pdtobj, this->pidl, &idMenu, &bSyncCopy); #endif // // Set fLinkOnly if the only option is link and the source hasn't // explicitly told us that it wants only a link created. // fLinkOnly = ((*pdwEffect == DROPEFFECT_LINK) && (!(this->dwData & DTID_PREFERREDEFFECT) || (this->dwEffectPreferred != DROPEFFECT_LINK))); // // this doesn't actually do the menu if (grfKeyState MK_LBUTTON) // ddm.dwDefEffect = dwDefEffect; ddm.pdtobj = pdtobj; ddm.pt = pt; ddm.pdwEffect = pdwEffect; ddm.hkeyProgID = hkeyProgID; ddm.hkeyBase = hkeyBaseProgID; ddm.idMenu = idMenu; ddm.grfKeyState = grfKeyState; hres = CIDLDropTarget_DragDropMenuEx(this, &ddm); SHCloseClassKey(hkeyProgID); SHCloseClassKey(hkeyBaseProgID); if (hres == S_FALSE) { LPFSTHREADPARAM pfsthp; // // special case filecontents only if we don't have something better // (ie allow HIDA and FILECONTENTS to be in the same drop and work) // if (idMenu == POPUP_FILECONTENTS && !((*pdwEffect & DROPEFFECT_LINK) && (this->dwData & DTID_HIDA))) { hres = FS_CreateFileFromClip(this, pdtobj, pt, pdwEffect); goto DragLeaveAndReturn; } else if (idMenu == POPUP_SCRAP) { // Ole link source. hres = FS_CreateBookMark(this, pdtobj, pt, pdwEffect); goto DragLeaveAndReturn; } pfsthp = (LPFSTHREADPARAM)LocalAlloc(LPTR, SIZEOF(FSTHREADPARAM)); if (pfsthp) { BOOL fIsOurs = CIDLData_IsOurs(pdtobj); pdtobj->lpVtbl->AddRef(pdtobj); // // If this is copy or move operation (i.e., file operation) // clone the data object with appropriate formats and force // secondary thread (CIDLData_IsOurs will succeed). This will // solve thread-lock problem AND scrap-left-open probelm. // (SatoNa) // if (!fIsOurs && (*pdwEffect==DROPEFFECT_MOVE || *pdwEffect==DROPEFFECT_COPY)) { LPDATAOBJECT pdtobjClone; if (SUCCEEDED(CIDLData_CloneForMoveCopy(pdtobj, &pdtobjClone))) { pdtobj->lpVtbl->Release(pdtobj); pdtobj = pdtobjClone; fIsOurs = TRUE; } } pdropt->lpVtbl->AddRef(pdropt); Assert(pdropt == &this->dropt); pfsthp->pfsdtgt = this; pfsthp->pDataObj = pdtobj; pfsthp->dwEffect = *pdwEffect; pfsthp->fLinkOnly = fLinkOnly; pfsthp->fSameHwnd = ShellFolderView_IsDropOnSource(this->hwndOwner, &pfsthp->pfsdtgt->dropt); pfsthp->fDragDrop = ShellFolderView_GetDropPoint(this->hwndOwner, &pfsthp->ptDrop); pfsthp->fBkDropTarget = ShellFolderView_IsBkDropTarget(this->hwndOwner, &pfsthp->pfsdtgt->dropt); #ifdef SYNC_BRIEFCASE pfsthp->bSyncCopy = bSyncCopy; pfsthp->idCmd = ddm.idCmd; #endif // // If this data object is our own (it means it is from our own // drag&drop loop), create a thread and do it asynchronously. // Otherwise (it means this is from OLE), do it synchronously. // if (fIsOurs) { // create another thread to avoid blocking the source thread. DWORD idThread; HANDLE hthread = CreateThread(NULL, 0, CFSDropTarget_DropThreadInit, pfsthp, 0, &idThread); if (hthread) { #ifdef DEBUG extern UINT g_cRefExtra; g_cRefExtra++; #endif // We don't need to communicate with this thread any more. // Close the handle and let it run and terminate itself. // // Notes: In this case, pszCopy will be freed by the thread. // CloseHandle(hthread); hres = S_OK; } else { // Thread creation failed, we should release thread parameter. pdtobj->lpVtbl->Release(pdtobj); pdropt->lpVtbl->Release(pdropt); LocalFree((HLOCAL)pfsthp); hres = E_OUTOFMEMORY; } } else { // // Process it synchronously. // CFSDropTarget_DropThreadInit(pfsthp); } // if we optimized the move we need to make sure the source // doesn't try to do delete the data too (it's already gone) if (SUCCEEDED(hres) && (*pdwEffect == DROPEFFECT_MOVE)) { dwEffectPerformed = *pdwEffect; *pdwEffect = 0; } } } DragLeaveAndReturn: CIDLDropTarget_DragLeave(pdropt); if (SUCCEEDED(hres)) { if (*pdwEffect) dwEffectPerformed = *pdwEffect; if (dwEffectPerformed) DataObj_SetPerformedEffect(pDroppedDataObject, dwEffectPerformed); } return hres; } // Arguments: // hwnd -- Specifies the owner window for the message/dialog box // void _TransferDelete(HWND hwnd, HDROP hDrop, UINT fOptions) { FILEOP_FLAGS fFileop; DRAGINFO di; di.uSize = SIZEOF(DRAGINFO); if (!DragQueryInfo(hDrop, &di)) { // This shouldn't happen unless there is a bug somewhere else Assert(FALSE); return; } if (fOptions & SD_SILENT) { fFileop = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_ALLOWUNDO; } else { fFileop = ((GetAsyncKeyState(VK_SHIFT) < 0) ? 0 : FOF_ALLOWUNDO); if (!(fOptions & SD_USERCONFIRMATION)) fFileop |= FOF_NOCONFIRMATION; } { SHFILEOPSTRUCT fo = { hwnd, FO_DELETE, di.lpFileList, NULL, fFileop, } ; SHFileOperation(&fo); } SHFree(di.lpFileList); } IDropTargetVtbl c_CFSDropTargetVtbl = { CIDLDropTarget_QueryInterface, CIDLDropTarget_AddRef, CIDLDropTarget_Release, CFSIDLDropTarget_DragEnter, CFSIDLDropTarget_DragOver, CIDLDropTarget_DragLeave, CFSIDLDropTarget_Drop, }; // // CFSIDLDropTarget::Drop // STDMETHODIMP CExeIDLDropTarget_Drop(LPDROPTARGET pdropt, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt); STGMEDIUM medium; LPIDA pida; if (!(this->grfKeyStateLast & MK_LBUTTON)) { HMENU hmenu = _LoadPopupMenu(POPUP_DROPONEXE); if (hmenu) { BOOL _TrackPopupMenu(HMENU hmenu, UINT wFlags, int x, int y, int wReserved, HWND hwndOwner, LPCRECT lprc); UINT idCmd = _TrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, pt.x, pt.y, 0, this->hwndOwner, NULL); DestroyMenu(hmenu); if (idCmd != DDIDM_OPENWITH) { *pdwEffect = 0; // canceled } } } *pdwEffect &= DROPEFFECT_COPY; // required call in DropTarget::Drop CDefView_UnlockWindow(); if (*pdwEffect && (NULL != (pida = DataObj_GetHIDA(pDataObj, &medium)))) { UINT i; LPITEMIDLIST pidl; int cchParam; TCHAR szPath[MAX_PATH]; // // Calculate the size of parameter buffer // for (i = 0, pidl=NULL, cchParam=0; i < pida->cidl; i++) { pidl = HIDA_FillIDList(medium.hGlobal, i, pidl); if (pidl) { if (SHGetPathFromIDListEx(pidl, szPath, GPFIDL_ALTNAME)) { cchParam += lstrlen(szPath) + 1; } } } if (cchParam) { LPTSTR pszParam = LocalAlloc(LPTR, cchParam*SIZEOF(TCHAR)); if (pszParam) { // // Fill the parameter buffer // SHELLEXECUTEINFO ExecInfo; for (i = 0; i < pida->cidl; i++) { pidl = HIDA_FillIDList(medium.hGlobal, i, pidl); if (pidl) { extern void lstrcatN(LPTSTR pszDest, LPCTSTR pszSrc, UINT cchMax); if (SHGetPathFromIDListEx(pidl, szPath, GPFIDL_ALTNAME)) { if (*pszParam) { lstrcatN(pszParam, c_szSpace, cchParam); } lstrcatN(pszParam, szPath, cchParam); } } } SHGetPathFromIDList(this->pidl, szPath); FillExecInfo(ExecInfo, NULL, NULL, szPath, pszParam, NULL, SW_SHOWNORMAL); ShellExecuteEx(&ExecInfo); LocalFree((HLOCAL)pszParam); } } if (pidl) { ILFree(pidl); } HIDA_ReleaseStgMedium(pida, &medium); } CIDLDropTarget_DragLeave(pdropt); return S_OK; } // // CExeIDLDropTarget::DragEnter // STDMETHODIMP CExeIDLDropTarget_DragEnter(LPDROPTARGET pdropt, LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt); // let the base-class process it, first. CIDLDropTarget_DragEnter(pdropt, pDataObj, grfKeyState, pt, pdwEffect); if (this->dwData & DTID_HDROP) *pdwEffect &= DROPEFFECT_COPY; else *pdwEffect = 0; this->dwEffectLastReturned = *pdwEffect; return S_OK; // Notes: we should NOT return hres as it. } IDropTargetVtbl c_CExeDropTargetVtbl = { CIDLDropTarget_QueryInterface, CIDLDropTarget_AddRef, CIDLDropTarget_Release, CExeIDLDropTarget_DragEnter, CIDLDropTarget_DragOver, CIDLDropTarget_DragLeave, CExeIDLDropTarget_Drop, }; void View_SelectAndEdit(HWND hwndOwner, LPCITEMIDLIST pidl, BOOL fEdit) { HWND hwndView = DV_HwndMain2HwndView(hwndOwner); if (hwndView) { POINT pt; // // If hwndView or its child does not have input focus, // we should set the hwndView before starting the edit mode. // HWND hwndFocus = GetFocus(); if (fEdit && (!hwndFocus || !IsChild(hwndView, hwndFocus))) { SetFocus(hwndView); } SHChangeNotifyHandleEvents(); if (SendMessage(hwndView, SVM_GETANCHORPOINT, TRUE, (LPARAM)&pt)) { ShellFolderView_SetItemPos(hwndOwner, pidl, pt.x, pt.y); } if (fEdit) SendMessage(hwndView, SVM_SELECTITEM, SVSI_EDIT, (LPARAM)pidl); } } // // check for a app to run to finish the "new" operation // // .lnk // ShellNew // Command = apptorunfornew.exe %1 // BOOL RunNewApp(HWND hwnd, LPITEMIDLIST pidl) { HKEY hkey; HKEY hkeyNew; DWORD cb; DWORD dwType; BOOL f = FALSE; TCHAR szFile[MAX_PATH]; TCHAR szNewApp[MAX_PATH]; TCHAR szCommand[MAX_PATH]; SHGetPathFromIDList(pidl,szFile); if (RegOpenKey(HKEY_CLASSES_ROOT, PathFindExtension(szFile), &hkey) == ERROR_SUCCESS) { if (RegOpenKey(hkey, c_szShellNew, &hkeyNew) == ERROR_SUCCESS) { cb = ARRAYSIZE(szNewApp); if (RegQueryValueEx(hkeyNew, c_szCommand, 0, &dwType, (LPBYTE)szNewApp, &cb) == ERROR_SUCCESS && dwType == REG_SZ) { ReplaceParameters(szCommand, ARRAYSIZE(szCommand), szFile, szNewApp, c_szNULL, 0, NULL, TRUE, NULL, NULL); f = ShellExecCmdLine(hwnd, szCommand, NULL, SW_SHOWNORMAL, NULL, 0); } RegCloseKey(hkeyNew); } RegCloseKey(hkey); } return f; } void CreateEmptyLink(LPCITEMIDLIST pidlParent, HWND hwndOwner) { TCHAR szFileName[MAX_PATH]; TCHAR szPath[MAX_PATH]; TCHAR szFullName[MAX_PATH]; TCHAR szShortName[15]; LPITEMIDLIST pidl; SHGetPathFromIDList(pidlParent, szPath); LoadString(HINST_THISDLL, IDS_NEWLINK, szFileName, ARRAYSIZE(szFileName)); LoadString(HINST_THISDLL, IDS_LINK, szShortName, ARRAYSIZE(szShortName)); _BuildLinkName(szShortName, szShortName, szPath, FALSE); _BuildLinkName(szFileName, szFileName, szPath, FALSE); PathYetAnotherMakeUniqueName(szFullName, szPath, szShortName, szFileName); CreateWriteCloseFile(hwndOwner, szFullName, NULL, 0); if (NULL != (pidl = SHSimpleIDListFromPath(szFullName))) { BOOL fEdit = !RunNewApp(hwndOwner, pidl); View_SelectAndEdit(hwndOwner, ILFindLastID(pidl), fEdit); ILFree(pidl); } } void CFSFolder_HandleNewOther(LPCITEMIDLIST pidlParent, HWND hwndOwner) { LPITEMIDLIST pidlNew; if (S_OK == NewObjMenu_DoItToMe(hwndOwner, pidlParent, &pidlNew)) { BOOL fEdit = !RunNewApp(hwndOwner, pidlNew); View_SelectAndEdit(hwndOwner, ILFindLastID(pidlNew), fEdit); ILFree(pidlNew); } } void CleanupRegMenu() { if (g_hmenuRegMenu) { NewObjMenu_Destroy(g_hmenuRegMenu, 3); DeleteMenu(g_hmenuRegMenu, 2, MF_BYPOSITION); g_hmenuRegMenu = NULL; } } // // To be called back from within CDefFolderMenu // // Returns: // S_OK, if successfully processed. // S_FALSE, if default code should be used. // HRESULT CALLBACK CFSFolder_DFMCallBackBG(LPSHELLFOLDER psf, HWND hwndOwner, LPDATAOBJECT pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); HRESULT hres = S_OK; HMENU hmenu; UINT id; switch(uMsg) { // BUGBUG: this could be combined with the one from ultrootx case DFM_WM_MEASUREITEM: #define lpmis ((LPMEASUREITEMSTRUCT)lParam) if (lpmis->itemID == (wParam + FSIDM_NEWOTHER)) { NewObjMenu_MeasureItem(lpmis); } break; #undef lpmis case DFM_WM_DRAWITEM: #define lpdis ((LPDRAWITEMSTRUCT)lParam) if (lpdis->itemID == (wParam + FSIDM_NEWOTHER)) { NewObjMenu_DrawItem(lpdis); } #undef lpdis break; case DFM_WM_INITMENUPOPUP: hmenu = (HMENU)wParam; id = GetMenuItemID(hmenu, 0); if (id == (UINT)(lParam + FSIDM_NEWFOLDER)) { if (NewObjMenu_InitMenuPopup(hmenu, 3)) { CleanupRegMenu(); g_hmenuRegMenu = hmenu; } } break; case DFM_RELEASE: CleanupRegMenu(); break; case DFM_MERGECONTEXTMENU: CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_FSVIEW_BACKGROUND, POPUP_FSVIEW_POPUPMERGE, (LPQCMINFO)lParam); break; case DFM_GETHELPTEXT: LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));; break; case DFM_GETHELPTEXTW: LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));; break; case DFM_VALIDATECMD: switch (wParam) { case DFM_CMD_NEWFOLDER: break; default: hres = S_FALSE; } break; case DFM_INVOKECOMMAND: switch(wParam) { case FSIDM_SORTBYNAME: case FSIDM_SORTBYSIZE: case FSIDM_SORTBYTYPE: case FSIDM_SORTBYDATE: ShellFolderView_ReArrange(hwndOwner, FSSortIDToICol(wParam)); break; case FSIDM_NEWFOLDER: case DFM_CMD_NEWFOLDER: CFSFolder_CreateFolder(hwndOwner, this->pidl); break; case FSIDM_NEWLINK: CreateEmptyLink(this->pidl, hwndOwner); break; case FSIDM_NEWOTHER: CFSFolder_HandleNewOther(this->pidl, hwndOwner); break; case FSIDM_PROPERTIESBG: hres = SHPropertiesForPidl(hwndOwner, this->pidl, (LPCTSTR)lParam); break; default: // This is one of view menu items, use the default code. hres = S_FALSE; break; } break; default: hres = E_NOTIMPL; break; } return hres; } void FSGetDiskFreeSpace(PFSSELCHANGEINFO pfssci, int idDrive) { TCHAR szPath[10]; DWORD dwSPC, dwBPS, dwFC, dwC; PathBuildRoot(szPath, idDrive); if (GetDiskFreeSpace(szPath, &dwSPC, &dwBPS, &dwFC, &dwC)) { pfssci->cbFree = (__int64)dwFC * (__int64)dwSPC * (__int64)dwBPS; } } void FSShowNoSelectionState(HWND hwndOwner, PFSSELCHANGEINFO pfssci) { // BUGBUG: 30 is arbitrary - AddCommas assumes it! TCHAR szTemp[30]; TCHAR szTempHidden[30]; TCHAR szTemplate[80]; TCHAR szStatus[128]; TCHAR szBytes[128]; LPTSTR lpsz[] = { szStatus, szBytes }; if (pfssci) { LoadString(HINST_THISDLL, (pfssci->cHiddenFiles) ? IDS_FSSTATUSTEMPLATE : IDS_FSSTATUSNOHIDDENTEMPLATE, szTemplate, ARRAYSIZE(szTemplate)); wsprintf(szStatus, szTemplate, AddCommas(pfssci->cFiles, szTemp), AddCommas(pfssci->cHiddenFiles, szTempHidden)); ShortSizeFormat64(pfssci->cbSize, szBytes); // tack on the freespace info if we're needing it (idDrive != -1) if (pfssci->idDrive != -1) { if (pfssci->cbFree == -1) { FSGetDiskFreeSpace(pfssci, pfssci->idDrive); } // cbFree couldstill be -1 if GetDiskFreeSpace didn't get any info if (pfssci->cbFree != -1) { TCHAR szFreeSpace[80]; LoadString(HINST_THISDLL, IDS_FREESPACE, szTemplate, ARRAYSIZE(szTemplate)); ShortSizeFormat64(pfssci->cbFree, szTemp); wsprintf(szFreeSpace, szTemplate, szTemp); lstrcat(szBytes, szFreeSpace); } } FSSetStatusText(hwndOwner, lpsz, 0, 1); } } void FSShowSelectionState(HWND hwndOwner, PFSSELCHANGEINFO pfssci) { TCHAR szTemp[20]; TCHAR szTemplate[80]; TCHAR szStatus[128]; TCHAR szBytes[30]; LPTSTR lpsz[] = { szStatus, szBytes }; LoadString(HINST_THISDLL, IDS_FSSTATUSSELECTED, szTemplate, ARRAYSIZE(szTemplate)); wsprintf(szStatus, szTemplate, AddCommas(pfssci->nItems, szTemp)); if (pfssci->cNonFolders) ShortSizeFormat64(pfssci->cbBytes, szBytes); else szBytes[0] = 0; FSSetStatusText(hwndOwner, lpsz, 0, 1); } void FSOnSelChange(LPCITEMIDLIST pidlParent, PDVSELCHANGEINFO pdvsci) { PFSSELCHANGEINFO pfssci; LPIDFOLDER pidf; int iMul = -1; ULONGLONG cbSize; pfssci = *((PFSSELCHANGEINFO*)pdvsci->plParam); pidf = (LPIDFOLDER)pdvsci->lParamItem; if (!pfssci || !pidf) { return; } // Update selection count if (pdvsci->uNewState & LVIS_SELECTED) { iMul = 1; } else { Assert(0 != pfssci->nItems); } // assert that soemthing changed Assert((pdvsci->uOldState & LVIS_SELECTED) != (pdvsci->uNewState & LVIS_SELECTED)); pfssci->nItems += iMul; FS_GetSize(pidlParent, pidf, &cbSize); pfssci->cbBytes += (iMul * cbSize); if (!FS_IsFolder(pidf)) pfssci->cNonFolders += iMul; } void FSUpdateStatusBar(HWND hwndOwner, PFSSELCHANGEINFO pfssci) { if (pfssci && pfssci->nItems) { FSShowSelectionState(hwndOwner, pfssci); } else { FSShowNoSelectionState(hwndOwner, pfssci); } } void FSOnInsertDeleteItem(LPCITEMIDLIST pidlParent, PDVSELCHANGEINFO pdvsci, int iMul) { PFSSELCHANGEINFO pfssci; LPIDFOLDER pidf; pfssci = *((PFSSELCHANGEINFO*)pdvsci->plParam); if (pfssci) { pidf = (LPIDFOLDER)pdvsci->lParamItem; if (pidf) { ULONGLONG ull; FS_GetSize(pidlParent, pidf, &ull); pfssci->cFiles += iMul; pfssci->cbSize += iMul * ull; if (pfssci->cFiles == 0) pfssci->cbSize = 0; } else { // means a delete all pfssci->cFiles = 0; pfssci->cbSize = 0; pfssci->nItems = 0; pfssci->cbBytes = 0; pfssci->cNonFolders = 0; pfssci->cHiddenFiles = 0; } } } LRESULT FSSelectAllWarning(HWND hwndOwner, PFSSELCHANGEINFO pfssci) { LRESULT lres = S_OK; if (pfssci && (pfssci->cHiddenFiles > 0)) { if (ShellMessageBox(HINST_THISDLL, hwndOwner, MAKEINTRESOURCE(IDS_SELECTALLBUTHIDDEN), MAKEINTRESOURCE(IDS_SELECTALL), MB_OKCANCEL | MB_SETFOREGROUND | MB_ICONWARNING, pfssci->cHiddenFiles) == IDCANCEL) lres = ResultFromScode(S_FALSE); } return lres; } TCHAR const c_szFSCols[] = TEXT("DirectoryCols"); void IFSInitializeStatus(HWND hwndOwner, LPFSFOLDER this, PDVSELCHANGEINFO pdvsci) { int idDrive; TCHAR szPath[MAX_PATH]; SHGetPathFromIDList(this->pidl, szPath); idDrive = PathGetDriveNumber(szPath); FSInitializeStatus(hwndOwner, idDrive, pdvsci); } // // Callback from SHCreateShellFolderViewEx // HRESULT CALLBACK FS_FNVCallBack(LPSHELLVIEW psvOuter, LPSHELLFOLDER psf, HWND hwndOwner, UINT uMsg, WPARAM wParam, LPARAM lParam) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); HRESULT hres = S_OK; // assume no error HMENU hmenu; UINT id; switch(uMsg) { case DVM_SUPPORTSIDENTITY: break; case DVM_MERGEMENU: CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, POPUP_FSVIEW_POPUPMERGE, (LPQCMINFO)lParam); break; case DVM_UNMERGEMENU: CleanupRegMenu(); break; case DVM_INITMENUPOPUP: hmenu = (HMENU)lParam; id = GetMenuItemID(hmenu, 0); if (id == (UINT)(LOWORD(wParam) + FSIDM_NEWFOLDER)) { // BUGBUG: RC ignores the last MF_SEPARATOR CleanupRegMenu(); if (GetMenuItemCount(hmenu)==2) { AppendMenu(hmenu, MF_SEPARATOR, (UINT)-1, NULL); AppendMenu(hmenu, MF_STRING, (UINT)LOWORD(wParam)+FSIDM_NEWOTHER, c_szSpace); Assert(GetMenuItemCount(hmenu)==4); if (NewObjMenu_InitMenuPopup(hmenu, 3)) { g_hmenuRegMenu = hmenu; } } } break; case DVM_MEASUREITEM: #define lpmis ((LPMEASUREITEMSTRUCT)lParam) DebugMsg(DM_TRACE, TEXT("sh TR - FS_FNVCallBack: DVM_MEASUREITEM (%d, %d)"), lpmis->itemID, wParam); if (lpmis->itemID == (wParam + FSIDM_NEWOTHER)) { NewObjMenu_MeasureItem(lpmis); } #undef lpmis break; case DVM_DRAWITEM: #define lpdis ((LPDRAWITEMSTRUCT)lParam) if (lpdis->itemID == (wParam + FSIDM_NEWOTHER)) { NewObjMenu_DrawItem(lpdis); } #undef lpdis break; case DVM_EXITMENULOOP: DebugMsg(DM_TRACE, TEXT("sh TR - FSFSNCallBack DVN_EXITMENULOOP")); CleanupRegMenu(); break; case DVM_INVOKECOMMAND: DebugMsg(DM_TRACE, TEXT("sh TR - FS_FSNCallBack DVN_INVOKECOMMAND (id=%x)"), wParam); switch(wParam) { case FSIDM_SORTBYNAME: case FSIDM_SORTBYSIZE: case FSIDM_SORTBYTYPE: case FSIDM_SORTBYDATE: ShellFolderView_ReArrange(hwndOwner, FSSortIDToICol(wParam)); break; case FSIDM_NEWFOLDER: CFSFolder_CreateFolder(hwndOwner, this->pidl); break; case FSIDM_NEWLINK: CreateEmptyLink(this->pidl, hwndOwner); break; case FSIDM_NEWOTHER: CFSFolder_HandleNewOther(this->pidl, hwndOwner); break; } break; case DVM_GETCCHMAX: { TCHAR szPath[MAX_PATH]; int *pcchMax = (int *)lParam; DWORD dwMaxLength; LPCIDFOLDER pidf=(LPCIDFOLDER)wParam; Assert(pcchMax); // // Get the maximum file name length. // MAX_PATH - ('\\' + '\0' + 1) - lstrlen(szParent) // And of course if the file system does not support // long file names we should also restrict it somemore. // Plus subtract enough off to make it possible to append \*.* // SHGetPathFromIDList(this->pidl, szPath); *pcchMax = MAX_PATH-3-lstrlen(szPath)-4; // Now make sure that that size is valid for the // type of drive that we are talking to PathStripToRoot(szPath); if (GetVolumeInformation(szPath, NULL, 0, NULL, &dwMaxLength, NULL, NULL, 0)) { if (*pcchMax > (int)dwMaxLength) *pcchMax = (int)dwMaxLength; } else dwMaxLength = 256; // Assume LFN for now... #ifdef WINNT if (FS_IsFolder(pidf) && *pcchMax > 12) { // On NT, a directory must be able to contain an // 8.3 name and STILL be less than MAX_PATH. The // "12" below is the length of an 8.3 name (8+1+3). *pcchMax -= 12; } #endif // // Adjust the cchMax if we are hiding the extension // if (pidf) { int cchCur; FS_CopyName(pidf,szPath,ARRAYSIZE(szPath)); cchCur = lstrlen(szPath); // If our code above restricted smaller than current size reset // back to current size... if (*pcchMax < cchCur) *pcchMax = cchCur; if (!FS_ShowExtension(pidf, FALSE)) { *pcchMax -= lstrlen(PathFindExtension(szPath)); if ((dwMaxLength <= 12) && (*pcchMax > 8)) *pcchMax = 8; } Assert(*pcchMax>0); } } break; case DVM_GETHELPTEXT: // GetCmdString(LOWORD(wParam), (LPSTR)lParam, HIWORD(wParam)); LoadString(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPTSTR)lParam, HIWORD(wParam)); break; case DVM_WINDOWCREATED: IFSInitializeStatus(hwndOwner, this, (PDVSELCHANGEINFO)lParam); break; case DVM_INSERTITEM: case DVM_DELETEITEM: { PDVSELCHANGEINFO pdvsci = (PDVSELCHANGEINFO)lParam; PFSSELCHANGEINFO pfssci; pfssci = *((PFSSELCHANGEINFO*)pdvsci->plParam); if (!pfssci) { IFSInitializeStatus(hwndOwner, this, pdvsci); } FSOnInsertDeleteItem(this->pidl, (PDVSELCHANGEINFO)lParam, uMsg == DVM_INSERTITEM ? 1 : -1); break; } case DVM_SELCHANGE: FSOnSelChange(this->pidl, (PDVSELCHANGEINFO)lParam); break; case DVM_UPDATESTATUSBAR: // if initializing, set cbFree to -1 if (wParam && lParam) { ((PFSSELCHANGEINFO)lParam)->cbFree = (ULONGLONG)-1; } FSUpdateStatusBar(hwndOwner, (PFSSELCHANGEINFO)lParam); break; case DVM_REFRESH: if (lParam) { ((PFSSELCHANGEINFO)lParam)->cHiddenFiles = this->cHiddenFiles; ((PFSSELCHANGEINFO)lParam)->cbSize = this->cbSize; } break; case DVM_SELECTALL: return FSSelectAllWarning(hwndOwner, (PFSSELCHANGEINFO)lParam); case DVM_RELEASE: if (lParam) { LocalFree((HLOCAL)lParam); } break; case DVM_GETWORKINGDIR: SHGetPathFromIDList(this->pidl, (LPTSTR)lParam); break; case DVM_GETDETAILSOF: #define pdi ((DETAILSINFO *)lParam) return(FS_GetDetailsOf(this->pidl, pdi->pidl, wParam, (LPSHELLDETAILS)&pdi->fmt)); #undef pdi case DVM_COLUMNCLICK: return(FS_ColumnClick(hwndOwner, wParam)); case DVM_GETCOLSAVESTREAM: *(LPSTREAM *)lParam = OpenRegStream(HKEY_CURRENT_USER, c_szRegExplorer, c_szFSCols, wParam); return(lParam ? S_OK : E_FAIL); case DVM_DIDDRAGDROP: { #if 0 // _SetData will do the appropriate delete item // (the below code caused compatibility problems // [deleteing files too often]) DWORD dwEffect = (DWORD)wParam; LPDATAOBJECT pdtobj = (LPDATAOBJECT)lParam; if (dwEffect == DROPEFFECT_MOVE) { // // the target wants us to complete the move by deleting the objects // Assert(pdtobj); // // pass SD_SILENT since we are/were dragging and since many apps // lie and return DROPEFFECT_MOVE when they don't mean it!!! // return CFSFolder_DeleteItems(this, hwndOwner, pdtobj, SD_SILENT); } #endif return S_OK; } default: hres = E_FAIL; } return hres; } // BUGBUG WARNING: The oledbshl project currently depends on this string; it should // be moved out to a standard header file TCHAR const c_szCLSIDView[] = TEXT("UICLSID"); STDMETHODIMP CFSFolder_CreateViewObject(LPSHELLFOLDER psf, HWND hwnd, REFIID riid, LPVOID * ppvOut) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); if (IsEqualIID(riid, &IID_IShellView) || IsEqualIID(riid, &IID_IDropTarget)) { // // Cache the view CLSID if not cached. // if (!this->fCachedCLSID) { LPIDFOLDER pidf = (LPIDFOLDER)ILFindLastID(this->pidl); if (pidf && (pidf->fs.wAttrs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) { TCHAR szPath[MAX_PATH]; TCHAR szProvider[MAX_PATH]; LPTSTR pszProvider = NULL; SHGetPathFromIDList(this->pidl, szPath); if (PathIsUNC(szPath)) { NET_CopyProviderNameRelative( this->pidl, szProvider, ARRAYSIZE(szProvider) ); pszProvider = szProvider; } this->fHasCLSID = _GetFolderCLSID(szPath, NULL, pszProvider, &this->clsidView, c_szCLSIDView ); } this->fCachedCLSID = TRUE; } // // Use the view handler if it exists. // if (this->fHasCLSID) { LPPERSISTFOLDER ppf; HRESULT hres = SHCoCreateInstance(NULL, &this->clsidView, NULL, &IID_IPersistFolder, &ppf); DebugMsg(DM_TRACE, TEXT("sh TR CFSFolder::CreateViewObject created a view instance for a CLSID (%x)"), hres); if (SUCCEEDED(hres)) { hres = ppf->lpVtbl->Initialize(ppf, this->pidl); if (SUCCEEDED(hres)) { hres = ppf->lpVtbl->QueryInterface(ppf, riid, ppvOut); } ppf->lpVtbl->Release(ppf); } if (SUCCEEDED(hres)) { DebugMsg(DM_TRACE, TEXT("external code supplied IShellView")); return hres; } } if (IsEqualIID(riid, &IID_IDropTarget)) { return CIDLDropTarget_Create(hwnd, &c_CFSDropTargetVtbl, this->pidl, (LPDROPTARGET *)ppvOut); } else { CSFV csfv = { SIZEOF(CSFV), // cbSize psf, // pshf NULL, // psvOuter this->pidl, // pidl SHCNE_DISKEVENTS | SHCNE_ASSOCCHANGED | SHCNE_NETSHARE | SHCNE_NETUNSHARE, FS_FNVCallBack, // pfnCallback 0, }; LPSHELLBROWSER psb = FileCabinet_GetIShellBrowser(hwnd); HWND hwndTree; // // if in explorer mode, we want to register for freespace changes too // psb->lpVtbl->GetControlWindow(psb, FCW_TREE, &hwndTree); if (hwndTree) { csfv.lEvents |= SHCNE_FREESPACE; } return SHCreateShellFolderViewEx(&csfv, (LPSHELLVIEW *)ppvOut); } } else if (IsEqualIID(riid, &IID_IContextMenu)) { // do background menu. return CDefFolderMenu_Create(this->pidl, hwnd, 0, NULL, psf, CFSFolder_DFMCallBackBG, NULL, NULL, (LPCONTEXTMENU *)ppvOut); } return E_NOINTERFACE; } STDMETHODIMP FS_CompareItemIDs(LPCSHITEMID pmkid1, LPCSHITEMID pmkid2) { LPCIDFOLDER pidf1 = (LPCIDFOLDER)pmkid1; LPCIDFOLDER pidf2 = (LPCIDFOLDER)pmkid2; HRESULT hres = E_INVALIDARG; TCHAR szName1[MAX_PATH]; TCHAR szName2[MAX_PATH]; if (!FS_IsValidID((LPITEMIDLIST)pidf1) || !FS_IsValidID((LPITEMIDLIST)pidf2)) { return(hres); } FS_CopyName(pidf1,szName1,ARRAYSIZE(szName1)); FS_CopyName(pidf2,szName2,ARRAYSIZE(szName2)); hres = ResultFromShort( lstrcmpi(szName1, szName2) ); // // This block of code is added to support pseudo IDList // which does not have the alternate name. If only one // of idlists is (or at least looks like) such a IDList, // we compare its name with the alternate of the other. // if (hres!=ResultFromShort(0)) { Assert(FS_IsRealID(pidf1) || FS_IsRealID(pidf2)); // if one or the other but not both are real ids if (FS_IsRealID(pidf1) ^ FS_IsRealID(pidf2)) { // try the alternate name on the real id if (FS_IsRealID(pidf1)) { FS_CopyAltName(pidf1,szName1,ARRAYSIZE(szName1)); } else { FS_CopyAltName(pidf2,szName2,ARRAYSIZE(szName2)); } if (lstrcmpi(szName1, szName2)==0) { hres = ResultFromShort(0); } } } else if (FS_IsRealID(pidf1) && FS_IsRealID(pidf2)) { // If both are real and if they compared the same in the case // INsensitive search, try case sensitive search hres = ResultFromShort(lstrcmp(szName1, szName2)); } return hres; } short _CompareFileTypes (LPSHELLFOLDER psf, LPIDFOLDER pidf1, LPIDFOLDER pidf2) { LPCTSTR szName1, szName2; short result=0; ENTERCRITICAL szName1 = _GetTypeName(pidf1); szName2 = _GetTypeName(pidf2); if (szName1 != szName2) result = lstrcmpi(szName1, szName2); LEAVECRITICAL return result; } HRESULT FS_CompareModifiedDate(LPIDFOLDER pidf1, LPIDFOLDER pidf2) { if ((DWORD)MAKELONG(pidf1->fs.timeModified, pidf1->fs.dateModified) > (DWORD)MAKELONG(pidf2->fs.timeModified, pidf2->fs.dateModified)) { return ResultFromShort(-1); } if ((DWORD)MAKELONG(pidf1->fs.timeModified, pidf1->fs.dateModified) < (DWORD)MAKELONG(pidf2->fs.timeModified, pidf2->fs.dateModified)) { return ResultFromShort(1); } return ResultFromShort(0); } HRESULT FS_CompareNames(LPIDFOLDER pidf1, LPIDFOLDER pidf2) { TCHAR szName1[MAX_PATH]; // We need to have a subfunction TCHAR szName2[MAX_PATH]; // because of this large stack usage // Sort it based on the primary (long) name -- ignore case. FS_CopyName(pidf1,szName1,ARRAYSIZE(szName1)); FS_CopyName(pidf2,szName2,ARRAYSIZE(szName2)); return ResultFromShort(lstrcmpi(szName1,szName2)); } HRESULT FS_CompareAttribs(LPIDFOLDER pidf1, LPIDFOLDER pidf2) { DWORD mask = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_COMPRESSED; // // Calculate value of desired bits in attribute DWORD. // DWORD dwValueA = pidf1->fs.wAttrs & mask; DWORD dwValueB = pidf2->fs.wAttrs & mask; if (dwValueA != dwValueB) { // // If the values are not equal, // sort alphabetically based on string representation. // int diff = 0; TCHAR szTempA[NUM_ATTRIB_CHARS + 1]; TCHAR szTempB[NUM_ATTRIB_CHARS + 1]; // // Create attribute string for objects A and B. // BuildAttributeString(pidf1->fs.wAttrs, szTempA, ARRAYSIZE(szTempA)); BuildAttributeString(pidf2->fs.wAttrs, szTempB, ARRAYSIZE(szTempB)); // // Compare attribute strings and determine difference. // diff = lstrcmp(szTempA, szTempB); if (diff > 0) return ResultFromShort(1); if (diff < 0) return ResultFromShort(-1); } return ResultFromShort(0); } STDMETHODIMP CFSFolder_CompareIDs(LPSHELLFOLDER psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); HRESULT hres; short nCmp; LPIDFOLDER pidf1 = (LPIDFOLDER)pidl1; LPIDFOLDER pidf2 = (LPIDFOLDER)pidl2; BOOL fUniqueComparison = FALSE; Assert( pidf1 ); Assert( pidf2 ); // The high bit on means to compare absolutely, ie: even if only filetimes // are different, we rule file pidls to be different if ( ((DWORD)lParam) & 0x80000000) { lParam = (LPARAM) ( ((DWORD)lParam) & 0x7FFFFFFF ); fUniqueComparison = TRUE; } if (!FS_IsValidID(pidl1) || !FS_IsValidID(pidl2)) { return E_INVALIDARG; } #define SORT_FOLDERS_FIRST #ifdef SORT_FOLDERS_FIRST // // We should ignore type, if one of ID has unknown type (SHID_FS). // if (FS_GetType(pidf1) != SHID_FS && FS_GetType(pidf2) != SHID_FS && FS_GetType(pidf1) != SHID_FS_UNICODE && FS_GetType(pidf2) != SHID_FS_UNICODE) { // Always put the folders first if (FS_IsFolder(pidf1)) { if (!FS_IsFolder(pidf2)) { return ResultFromShort(-1); } } else if (FS_IsFolder(pidf2)) { return ResultFromShort(1); } } #endif switch (lParam) { case FS_ICOL_SIZE: { ULONGLONG ull1; ULONGLONG ull2; FS_GetSize(this->pidl, pidf1, &ull1); FS_GetSize(this->pidl, pidf2, &ull2); if (ull1 < ull2) return ResultFromShort(-1); if (ull1 > ull2) return ResultFromShort(1); } goto DoDefault; case FS_ICOL_TYPE: nCmp = _CompareFileTypes(psf, pidf1, pidf2); if (nCmp) return ResultFromShort(nCmp); goto DoDefault; case FS_ICOL_MODIFIED: #if 0 // Note we cannot just subtract 2 DWORD's, since the result // gets truncated when cast to a short (effectively ignoring // the HIWORD part). Similarly, we cannot just use the HIWORD // of the difference, since it could be 0 meaningg greater // than OR equal to. nCmp = (short)HIWORD((MAKELONG(pidf1->fs.timeModified, pidf1->fs.dateModified) - MAKELONG(pidf2->fs.timeModified, pidf2->fs.dateModified))); if (!nCmp) goto DoDefault; else return ResultFromShort(nCmp); #else hres = FS_CompareModifiedDate(pidf1, pidf2); if (!hres) goto DoDefault; break; #endif case FS_ICOL_NAME: // We need to treat this differently from others bacause // pidf1/2 might not be simple. hres = FS_CompareItemIDs((LPSHITEMID)pidf1, (LPSHITEMID)pidf2); // REVIEW: (Possible performance gain with some extra code) // We should probably aviod bindings by walking down // the IDList here instead of calling this helper function. // if (hres == ResultFromShort(0)) { // // Note that the first pidl to ILCompareRelIDs must // not be a simple pidl (otherwise, the binding will // fail). // if (FS_GetType(pidf1) != SHID_FS && FS_GetType(pidf1) != SHID_FS_UNICODE) { // pidl1 is not simple hres = ILCompareRelIDs(psf, pidl1, pidl2); // if both of these are NOT simple, we do the date // modified compare. otherwise take what we've got and return if (FS_GetType(pidf2) != SHID_FS && FS_GetType(pidf2) != SHID_FS_UNICODE) goto DoDefaultModification; } else { // pidl2 should not be simple Assert(FS_GetType(pidf2) != SHID_FS && FS_GetType(pidf2) != SHID_FS_UNICODE) ; hres = ILCompareRelIDs(psf, pidl2, pidl1); if (SUCCEEDED(hres)) { // reverse the result and return. short i=(short)SCODE_CODE(hres); hres = ResultFromShort(0-(int)i); } } } break; case FS_ICOL_ATTRIB: { hres = FS_CompareAttribs(pidf1, pidf2); if (hres) { return hres; } goto DoDefault; } default: DoDefault: hres = FS_CompareNames(pidf1,pidf2); DoDefaultModification: if (hres || FALSE == fUniqueComparison) return hres; // // Must sort by modified date to pick up any file changes! // hres = FS_CompareModifiedDate(pidf1, pidf2); if (!hres) { hres = FS_CompareAttribs(pidf1, pidf2); } } return hres; } // // REVIEW: This code must be in ultrootx.c // BOOL CFSFolder_IsLocal(LPCITEMIDLIST pidlAbs) { // this is the desktop if (ILIsEmpty(pidlAbs)) return TRUE; if (CDesktop_IsMyComputer(pidlAbs)) { LPCITEMIDLIST pidlDrive = _ILNext(pidlAbs); Assert(!ILIsEmpty(pidlDrive)); return (SIL_GetType(pidlDrive) != SHID_COMPUTER_NETDRIVE); } return FALSE; } #ifdef WINNT BOOL CFSFolder_IsDfsJP(LPCITEMIDLIST pidlFolder, LPCIDFOLDER pidfSubFolder) { TCHAR szPath[MAX_PATH]; WCHAR wszPath[MAX_PATH]; NTSTATUS status; UNICODE_STRING str; HANDLE fh; BOOL bIsJP = FALSE; LPITEMIDLIST pidlAbs = NULL; // // Check pidlAbs. If non null and it is not the desktop, examine it // for JP if ( pidlFolder != NULL && (pidlAbs = FS_Combine(pidlFolder, pidfSubFolder)) != NULL && !ILIsEmpty(pidlAbs)) { // Get the path from the pidl SHGetPathFromIDList(pidlAbs, szPath); StrToOleStr(wszPath, szPath); if (RtlDosPathNameToNtPathName_U(wszPath,&str,NULL,NULL)) { IO_STATUS_BLOCK iosb; OBJECT_ATTRIBUTES oa; InitializeObjectAttributes( &oa, &str, OBJ_CASE_INSENSITIVE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); // // Do a generic read open. If it fails with STATUS_DFS_EXIT_PATH_FOUND, we // are dealing with a junction point. // status = NtCreateFile( &fh, FILE_GENERIC_READ | FILE_GENERIC_WRITE | SYNCHRONIZE | DELETE, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, pOpenIfJPEa, cbOpenIfJPEa ); if ( NT_SUCCESS(status) ) { NtClose(fh); } else if (status == STATUS_DFS_EXIT_PATH_FOUND) { bIsJP = TRUE; } RtlFreeUnicodeString(&str); } } if (pidlAbs != NULL) { ILFree(pidlAbs); } return bIsJP; } #endif // // This function returns the attributes (to be returned IShellFolder:: // GetAttributesOf) of the junction point specified by the class ID. // DWORD SHGetAttributesFromCLSID(const CLSID * pclsid, DWORD dwDefault) { DWORD dwAttr = dwDefault; TCHAR szClass[GUIDSTR_MAX+ARRAYSIZE(c_szShellFolder)]; HKEY hkeyCLSID; StringFromGUID2A(pclsid, szClass, ARRAYSIZE(szClass)); lstrcat(szClass,c_szShellFolder); if (g_hkcrCLSID && SHRegOpenKey(g_hkcrCLSID, szClass, &hkeyCLSID)==ERROR_SUCCESS) { DWORD dwData, dwType; DWORD cbSize = SIZEOF(dwAttr); if (RegQueryValueEx(hkeyCLSID, (LPTSTR)c_szAttributes, NULL, &dwType, (LPBYTE)&dwData, &cbSize)==ERROR_SUCCESS && (dwType==REG_DWORD || (dwType==REG_BINARY && cbSize==SIZEOF(dwData)))) { FullDebugMsg(DM_TRACE, TEXT("sh TR - CFSFolder::GetAttr got attributes from registry (%x, %s)"),dwAttr, szClass); dwAttr = dwData; } else { DebugMsg(DM_TRACE, TEXT("sh TR - CFSFolder::GetAttr ReqQV failed (%s)"), szClass); } RegCloseKey(hkeyCLSID); } else { DebugMsg(DM_ERROR, TEXT("sh ER - SHGetAttributesFromCLSID RegOpenKey(%s) failed"), szClass); } return dwAttr; } STDMETHODIMP CFSFolder_GetAttributesOf(LPSHELLFOLDER psf, UINT cidl, LPCITEMIDLIST * apidl, ULONG * prgfInOut) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); TCHAR szPath[MAX_PATH]; // Put it here so that we can see the stack frame size // REVIEW: Use file attributes! ULONG rgfOut = SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANCOPY | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_DROPTARGET; if (cidl==1) { LPCIDFOLDER pidf = (LPCIDFOLDER)ILFindLastID(apidl[0]); // // Don't hit the disk unless we need to. // if (*prgfInOut & SFGAO_VALIDATE) { SHGetPathFromIDList(this->pidl, szPath); FSFolder_CombinePathI(pidf, szPath, TRUE); /* fAltName=TRUE */ if (!PathFileExists(szPath)) { return E_FAIL; } } if (*prgfInOut & SFGAO_COMPRESSED) { if (pidf->fs.wAttrs & FILE_ATTRIBUTE_COMPRESSED) { rgfOut |= SFGAO_COMPRESSED; } } if (FS_GetType(pidf) == SHID_FS_DIRECTORY || FS_GetType(pidf) == SHID_FS_DIRUNICODE ) rgfOut |= SFGAO_FOLDER; if (FS_IsJunction(pidf)) { CLSID clsid, *pclsid = NULL; const UNALIGNED CLSID * uapclsid = FS_GetCLSID(pidf); // // By default, all the junction points are: // expandable, non-droptarget and non-file system. // rgfOut &= ~(SFGAO_FILESYSTEM | SFGAO_DROPTARGET); if (uapclsid) { clsid = *uapclsid; pclsid = &clsid; } rgfOut |= SHGetAttributesFromCLSID(pclsid, SFGAO_HASSUBFOLDER); } // it can only have subfolders if we've first found it's a folder else if ((*prgfInOut & SFGAO_HASSUBFOLDER) && (rgfOut & SFGAO_FOLDER )) { if (!CFSFolder_IsLocal(this->pidl) #ifdef WINNT || CFSFolder_IsDfsJP(this->pidl, pidf) #endif ) { rgfOut |= SFGAO_HASSUBFOLDER; } else { LPITEMIDLIST pidlAbs = FS_Combine(this->pidl, pidf); if (pidlAbs) { LPENUMIDLIST peunk; if (SUCCEEDED(FS_EnumObjects(this, NULL, pidlAbs, SHCONTF_FOLDERS, &peunk))) { LPITEMIDLIST pidlT; ULONG celt; if (peunk->lpVtbl->Next(peunk, 1, &pidlT, &celt) == S_OK) { Assert(celt == 1); Assert(pidlT); rgfOut |= SFGAO_HASSUBFOLDER; SHFree(pidlT); } peunk->lpVtbl->Release(peunk); } ILFree(pidlAbs); } } } // // Don't call the server code unless we need to. // if ((*prgfInOut & SFGAO_REMOVABLE) || (*prgfInOut & SFGAO_SHARE) && FS_IsFolder(pidf)) { // // Notes: We must always pass non-altname to MSSHRUI. // SHGetPathFromIDList(this->pidl, szPath); if ((*prgfInOut & SFGAO_REMOVABLE) && PathIsRemovable(szPath)){ rgfOut |= SFGAO_REMOVABLE; } if ((*prgfInOut & SFGAO_SHARE) && FS_IsFolder(pidf)) { FSFolder_CombinePathI(pidf, szPath, FALSE); // fAltName=FALSE if (IsShared(szPath, FALSE)) rgfOut |= SFGAO_SHARE; } } if (*prgfInOut & SFGAO_LINK) { DWORD dwFlags; dwFlags = SHGetClassFlags(pidf, FALSE); if (dwFlags & SHCF_IS_LINK) rgfOut |= SFGAO_LINK; } } *prgfInOut = rgfOut; return S_OK; } HRESULT FSLoadHandler(LPCITEMIDLIST pidl, LPCTSTR szHandler, REFIID riid, IUnknown **ppunk) { HRESULT hres = E_FAIL; ULONG cbValue; TCHAR szHandlerCLSID[40]; // enough for CLSID WCHAR wszPath[MAX_PATH]; TCHAR szPath[MAX_PATH]; IPersistFile *ppf; HKEY hkeyProgID; *ppunk = NULL; SHGetClassKey((LPIDFOLDER)pidl, &hkeyProgID, NULL, FALSE); // // Check if the class has the handler // cbValue = SIZEOF(szHandlerCLSID); if (hkeyProgID && RegQueryValue(hkeyProgID, szHandler, szHandlerCLSID, &cbValue) == ERROR_SUCCESS) { // // Yes, create an instance. // #if 0 // // it would be real nice to do it this way so the handler knowns why // it is getting loaded, but our own IShellLink code does not work like this! // hres = SHCoCreateInstance(szHandlerCLSID, NULL, NULL, riid, ppunk); if (SUCCEEDED(hres)) { // // Then, initialize it. // IUnknown *punk = *ppunk; hres = punk->lpVtbl->QueryInterface(punk, &IID_IPersistFile, &ppf); if (SUCCEEDED(hres)) { SHGetPathFromIDList(pidl, szPath); StrToOleStr(wszPath, szPath); hres = ppf->lpVtbl->Load(ppf, wszPath, STGM_READ); ppf->lpVtbl->Release(ppf); } } #else hres = SHCoCreateInstance(szHandlerCLSID, NULL, NULL, &IID_IPersistFile, &ppf); if (SUCCEEDED(hres)) { // // Then, initialize it. // SHGetPathFromIDList(pidl, szPath); StrToOleStr(wszPath, szPath); hres = ppf->lpVtbl->Load(ppf, wszPath, STGM_READ); if (SUCCEEDED(hres)) { hres = ppf->lpVtbl->QueryInterface(ppf, riid, ppunk); #ifdef UNICODE // Lovely, if we were looking for IID_IExtractIcon, we might // as well search for IID_IExtractIconA. This allows us to // avoid having to load handlers TWICE that support // IID_IExtractIconA but not IID_IExtractIconW (once looking // for IID_IExtractIconW and once looking for IID_IExtractIconA) // // Also, be warned that this routine (when passed IID_IExtractIcon) // will return either an IExtractIconW or an IExtractIconA so // some sort of QI should be done to get the right one. // -BobDay // if (FAILED(hres) && IsEqualIID(riid,&IID_IExtractIcon)) { hres = ppf->lpVtbl->QueryInterface(ppf, &IID_IExtractIconA, ppunk); } #endif } ppf->lpVtbl->Release(ppf); } #endif } SHCloseClassKey(hkeyProgID); return hres; } // // opens the CLSID key given the ProgID // // BUGBUG (DavePl) constant 6 HKEY SHOpenCLSID(HKEY hkeyProgID) { HKEY hkeyCLSID=NULL; TCHAR szCLSID[MAX_CLASS]; DWORD cb; lstrcpy(szCLSID, c_szCLSIDSlash); Assert(lstrlen(c_szCLSIDSlash) == 6); cb = SIZEOF(szCLSID)-6; if (RegQueryValue(hkeyProgID, c_szCLSID, szCLSID+6, &cb) == ERROR_SUCCESS) { SHRegOpenKey(HKEY_CLASSES_ROOT, szCLSID, &hkeyCLSID); } return hkeyCLSID; } // // This function creates a default IExtractIcon object for either // a file or a junction point. We should not supposed to call this function // for a non-junction point directory (we don't want to hit the disk!). // HRESULT CFSFolder_CreateDefExtIcon(LPCITEMIDLIST pidlFolder, UINT wSpecialFID, LPCIDFOLDER pidf, LPEXTRACTICON * ppxicon) { HRESULT hres = E_OUTOFMEMORY; LPITEMIDLIST pidlAbs; UINT iIcon; #ifdef CAIRO_DS BOOL fInDSFolder = FALSE; #endif // // special case for Folder. // // WARNING: don't replace this if-statement with FS_IsFolder(pidf))!!! // otherwise all junctions (like briefcase) will get the Folder icon. // if (FS_IsFileFolder(pidf)) { #ifdef CAIRO_DS fInDSFolder = CFSFolder_IsDSFolder (pidlFolder); if (!fInDSFolder) { #endif //CAIRO_DS #ifdef PROGMAN_ICON if (wSpecialFID == CSIDL_PROGRAMS) { iIcon = II_STSPROGS; } else if (wSpecialFID == CSIDL_COMMON_PROGRAMS) { iIcon = II_STCPROGS; } else { iIcon = II_FOLDER; } #else // PROGRAM_ICON iIcon = II_FOLDER; #endif // PROGRAM_ICON hres = SHCreateDefExtIcon(NULL, iIcon, // default II_FOLDEROPEN, // default (open) GIL_PERCLASS, ppxicon); return hres; #ifdef CAIRO_DS } #endif //CAIRO_DS } // // not a folder, get IExtractIcon and extract it. // (might be a ds folder) // pidlAbs = ILCombine(pidlFolder, (LPCITEMIDLIST)pidf); if (pidlAbs) { DWORD dwFlags; #ifdef CAIRO_DS if (fInDSFolder) dwFlags = SHGetClassFlags((LPCIDFOLDER)pidlAbs, TRUE); else #endif //CAIRO_DS dwFlags = SHGetClassFlags(pidf, FALSE); if (dwFlags & SHCF_ICON_PERINSTANCE) { if (dwFlags & SHCF_HAS_ICONHANDLER) { hres = FSLoadHandler(pidlAbs, c_szIconHandler, &IID_IExtractIcon, (IUnknown**)ppxicon); } else { DWORD uid = FS_GetUID(pidf); TCHAR szPath[MAX_PATH]; SHGetPathFromIDList(pidlAbs, szPath); hres = SHCreateDefExtIcon(szPath, uid, uid, GIL_PERINSTANCE|GIL_NOTFILENAME, ppxicon); } } else { iIcon = (dwFlags & SHCF_ICON_INDEX); hres = SHCreateDefExtIcon(c_szStar, iIcon, iIcon, GIL_PERCLASS|GIL_NOTFILENAME, ppxicon); } ILFree(pidlAbs); } return(hres); } // subclass member function to support CF_HDROP and CF_NETRESOURCE HRESULT CFSIDLData_QueryGetData(LPDATAOBJECT pdtobj, LPFORMATETC pformatetc) { if (pformatetc->cfFormat == CF_HDROP && (pformatetc->tymed & TYMED_HGLOBAL)) { return S_OK; // same as S_OK } else if (pformatetc->cfFormat == g_cfFileName && (pformatetc->tymed & TYMED_HGLOBAL)) { return S_OK; } #ifdef UNICODE else if (pformatetc->cfFormat == g_cfFileNameW && (pformatetc->tymed & TYMED_HGLOBAL)) { return S_OK; } #endif else if (pformatetc->cfFormat == g_cfNetResource && (pformatetc->tymed & TYMED_HGLOBAL)) { return CDesktopIDLData_GetNetResourceForFS(pdtobj, NULL); } return CIDLData_QueryGetData(pdtobj, pformatetc); } HRESULT CFSIDLData_SetData(IDataObject *pdtobj, FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) { DWORD dwDropEffect = 0; HRESULT hres; if ((pformatetc->cfFormat == g_cfPasteSucceeded) && (pformatetc->tymed == TYMED_HGLOBAL) && pmedium->hGlobal) { DWORD *pdw = (DWORD *)GlobalLock(pmedium->hGlobal); if (pdw) { dwDropEffect = *pdw; GlobalUnlock(pmedium->hGlobal); } } hres = CIDLData_SetData(pdtobj, pformatetc, pmedium, fRelease); if (dwDropEffect & DROPEFFECT_MOVE) { // // the target wants us to complete the move by deleting the objects // LPFSFOLDER psf = (LPFSFOLDER)CIDLData_GetFolder(pdtobj); if (psf) { // // specify SD_SILENT since it is really wierd to get a // "cannot delete blah" message during a paste... // hres = CFSFolder_DeleteItems(psf, NULL, pdtobj, SD_SILENT); } } return hres; } // // Creates a HDROP (Win 3.1 compatible file list) from HIDA. // // WARNING: This function is called from netviewx.c // HRESULT CFSIDLData_GetHDrop(IDataObject *pdtobj, STGMEDIUM *pmedium, BOOL fAltName) { HRESULT hres = E_OUTOFMEMORY; LPITEMIDLIST pidl = NULL; // realloced in HIDA_FillIDList STGMEDIUM medium; TCHAR szPath[MAX_PATH]; UINT i, cbAlloc = SIZEOF(DROPFILES) + SIZEOF(TCHAR); // header + null terminator LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); Assert(pida && pida->cidl); // we created this for (i = 0; i < pida->cidl; i++) { // HIDA_FillIDList may realloc pidl LPITEMIDLIST pidlTemp = HIDA_FillIDList(medium.hGlobal, i, pidl); if (pidlTemp == NULL) { // hres = E_OUTOFMEMORY; // already set break; } pidl = pidlTemp; // We may ask for the ALT name even if they did not ask for it in the // case where we failed to get the long name... if (!SHGetPathFromIDListEx(pidl, szPath, fAltName) && !(!fAltName && (SHGetPathFromIDListEx(pidl, szPath, TRUE)))) { // The path probably exceeds the max lenght, lets Bail... DebugMsg(DM_TRACE, TEXT("s.CFSIDLData_GetHDrop: SHGetPathFromIDList failed.")); hres = E_FAIL; goto Abort; } cbAlloc += lstrlen(szPath) * SIZEOF(TCHAR) + SIZEOF(TCHAR); } pmedium->hGlobal = GlobalAlloc(GPTR, cbAlloc); if (pmedium->hGlobal) { LPDROPFILES pdf = (LPDROPFILES)pmedium->hGlobal; LPTSTR pszFiles = (LPTSTR)(pdf + 1); pdf->pFiles = SIZEOF(DROPFILES); Assert(pdf->pt.x==0); Assert(pdf->pt.y==0); Assert(pdf->fNC==FALSE); Assert(pdf->fWide==FALSE); #ifdef UNICODE pdf->fWide = TRUE; #endif for (i = 0; i < pida->cidl; i++) { LPITEMIDLIST pidlTemp = HIDA_FillIDList(medium.hGlobal, i, pidl); Assert(pidl == pidlTemp); // Don't read directly into buffer as we my have been forced to use alternate name and the // total path we allocated may be smaller than we would tromp on which will screw up the heap. if (!SHGetPathFromIDListEx(pidl, szPath, fAltName)) SHGetPathFromIDListEx(pidl, szPath, TRUE); lstrcpy(pszFiles, szPath); pszFiles += lstrlen(pszFiles) + 1; Assert((UINT)((LPBYTE)pszFiles - (LPBYTE)pdf) < cbAlloc); } Assert((LPTSTR)((LPBYTE)pdf + cbAlloc - SIZEOF(TCHAR)) == pszFiles); Assert(*pszFiles == 0); // zero init alloc pmedium->tymed = TYMED_HGLOBAL; pmedium->pUnkForRelease = NULL; hres = S_OK; } Abort: HIDA_ReleaseStgMedium(pida, &medium); ILFree(pidl); return hres; } // subclass member function to support CF_HDROP and CF_NETRESOURCE HRESULT CFSIDLData_GetData(LPDATAOBJECT pdtobj, LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium) { HRESULT hres = E_INVALIDARG; if (pformatetcIn->cfFormat == CF_HDROP && (pformatetcIn->tymed & TYMED_HGLOBAL)) { // DVASPECT_SHORTNAME -> short name hres = CFSIDLData_GetHDrop(pdtobj, pmedium, pformatetcIn->dwAspect == DVASPECT_SHORTNAME); } else if ((pformatetcIn->cfFormat == g_cfFileName || #ifdef UNICODE pformatetcIn->cfFormat == g_cfFileNameW #else FALSE #endif ) && (pformatetcIn->tymed & TYMED_HGLOBAL)) { STGMEDIUM mediumT; // // NOTES: Since we don't know the destination, we should use // short name. New apps should use CF_HDROP anyway... // hres = CFSIDLData_GetHDrop(pdtobj, &mediumT, TRUE); if (SUCCEEDED(hres)) { TCHAR szPath[MAX_PATH]; if (DragQueryFile(mediumT.hGlobal, 0, szPath, ARRAYSIZE(szPath))) { UINT uSize; HGLOBAL hmem; uSize = lstrlen(szPath)+1; #ifdef UNICODE if (pformatetcIn->cfFormat == g_cfFileNameW) uSize *= sizeof(WCHAR); #endif hmem = GlobalAlloc(GPTR, uSize); if (hmem) { #ifdef UNICODE if (pformatetcIn->cfFormat == g_cfFileNameW) lstrcpy((LPWSTR)hmem, szPath); else WideCharToMultiByte(CP_ACP, 0, szPath, -1, (LPSTR)hmem, uSize, NULL, NULL); #else lstrcpy((LPTSTR)hmem, szPath); #endif pmedium->tymed = TYMED_HGLOBAL; pmedium->hGlobal = hmem; pmedium->pUnkForRelease =NULL; hres = S_OK; } else { hres = E_OUTOFMEMORY; } } else { hres = E_UNEXPECTED; } SHReleaseStgMedium(&mediumT); } } else if (pformatetcIn->cfFormat == g_cfNetResource && (pformatetcIn->tymed & TYMED_HGLOBAL)) { // // We should return HNRES if the selected file system objects // are in one of network folders. // hres = CDesktopIDLData_GetNetResourceForFS(pdtobj, pmedium); } else { hres = CIDLData_GetData(pdtobj, pformatetcIn, pmedium); } return hres; } IDataObjectVtbl c_CFSIDLDataVtbl = { CIDLData_QueryInterface, CIDLData_AddRef, CIDLData_Release, CFSIDLData_GetData, CIDLData_GetDataHere, CFSIDLData_QueryGetData, CIDLData_GetCanonicalFormatEtc, CFSIDLData_SetData, CIDLData_EnumFormatEtc, CIDLData_Advise, CIDLData_Unadvise, CIDLData_EnumAdvise }; // // This function returns TRUE only if all the IDLISTs points to file // system objects. // BOOL FS_AreTheyAllFSObjects(UINT cidl, LPCITEMIDLIST * apidl) { BOOL fFSOnly = TRUE; // Assume no non-file system object UINT iidl; // Check if there is any non-file system object. for (iidl=0; iidlfs.dateModified, pidfLast->fs.timeModified, &pfd->ftLastWriteTime); pfd->dwFileAttributes = pidfLast->fs.wAttrs & ~FSTREEX_ATTRIBUTE_MASK; pfd->nFileSizeLow = pidfLast->fs.dwSize; Assert(pfd->nFileSizeHigh == 0); FS_CopyName(pidfLast, pfd->cFileName, ARRAYSIZE(pfd->cFileName)); FS_CopyAltName(pidfLast, pfd->cAlternateFileName, ARRAYSIZE(pfd->cAlternateFileName)); } fRet = TRUE; } ILFree(pidl); } return fRet; } HRESULT CFSFolder_DeleteItems(LPFSFOLDER this, HWND hwndOwner, LPDATAOBJECT pdtobj, int fOptions) { STGMEDIUM medium; FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; HRESULT hres = S_OK; #ifdef CAIRO_DS TCHAR szDir[MAX_PATH]; BOOL fIsDSDataObject = FALSE; #endif hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium); #ifdef CAIRO_DS if (FAILED(hres)) { fmte.cfFormat = g_cfDS_HDROP; hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium); if (SUCCEEDED(hres)) { fIsDSDataObject = TRUE; } } #endif if (SUCCEEDED(hres)) { #ifdef CAIRO_DS if (fIsDSDataObject) { Assert (this->fIsDSFolder); if (SHGetPathFromIDList (this->pidl, szDir)) { _DSTransferDelete(hwndOwner, medium.hGlobal, szDir, fOptions); } } else #endif //CAIRO_DS _TransferDelete(hwndOwner, medium.hGlobal, fOptions); SHReleaseStgMedium(&medium); SHChangeNotifyHandleEvents(); } return 0L; } typedef struct { LPITEMIDLIST pidlParent; LPDATAOBJECT pdtobj; LPCTSTR pStartPage; } PROPSTUFF; DWORD CALLBACK _CFSFolder_PropertiesThread(PROPSTUFF * pps) { STGMEDIUM medium; LPITEMIDLIST pidl; TCHAR szPath[MAX_PATH]; LPIDA pida; #ifdef CAIRO_DS BOOL Is_DSObject = FALSE; #endif pida = DataObj_GetHIDA(pps->pdtobj, &medium); #ifdef CAIRO_DS if (!pida) // try the DS HIDA format, this might be a ds object { pida = DataObj_GetDS_HIDA(pps->pdtobj, &medium); if (pida) { Is_DSObject = TRUE; DSDataObj_EnableHDROP(pps->pdtobj); } } #endif // CAIRO_DS if (medium.hGlobal) { BOOL fAllocated; LPCIDFOLDER pidfFirst = (LPIDFOLDER)IDA_GetRelativeIDListPtr(pida, 0, &fAllocated); LPTSTR pszCaption; // Yes, do context menu. HKEY ahkeys[2] = { NULL, NULL }; #ifdef CAIRO_DS if (Is_DSObject) { pidl = ILCombine(pps->pidlParent, (LPITEMIDLIST)pidfFirst); if (pidl && SHGetPathFromIDList(pidl, szPath)) { SHGetClassKey((LPCIDFOLDER)pidl, &ahkeys[1], NULL, TRUE); SHGetBaseClassKey(pidfFirst, &ahkeys[0]); } } else { #endif // Get the hkeyProgID and hkeyBaseProgID from the first item. SHGetClassKey(pidfFirst, &ahkeys[1], NULL, FALSE); SHGetBaseClassKey(pidfFirst, &ahkeys[0]); #ifdef CAIRO_DS } #endif // REVIEW: psb? pszCaption = SHGetCaption(medium.hGlobal); #ifdef CAIRO_DS if (Is_DSObject) { SHOpenPropSheet(pszCaption, ahkeys, 2, NULL, pps->pdtobj, NULL, pps->pStartPage); } else { #endif SHOpenPropSheet(pszCaption, ahkeys, 2, &CLSID_ShellFileDefExt, pps->pdtobj, NULL, pps->pStartPage); #ifdef CAIRO_DS } #endif if (pszCaption) SHFree(pszCaption); SHCloseClassKey(ahkeys[0]); SHCloseClassKey(ahkeys[1]); pidl = ILCombine(pps->pidlParent, (LPITEMIDLIST)pidfFirst); if (pidl && SHGetPathFromIDList(pidl, szPath)) { if (lstrcmpi(PathFindExtension(szPath), c_szDotPif) == 0) { DebugMsg(DM_TRACE, TEXT("s.cSHCNRF_pt: DOS properties done, generating event.")); SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST, pidl, NULL); } ILFree(pidl); } if (fAllocated) { ILFree((LPITEMIDLIST)pidfFirst); } HIDA_ReleaseStgMedium(pida, &medium); } pps->pdtobj->lpVtbl->Release(pps->pdtobj); ILFree(pps->pidlParent); LocalFree((HLOCAL)pps); return 0; } HRESULT CFSFolder_Properties(LPFSFOLDER this, LPCITEMIDLIST pidlParent, LPDATAOBJECT pdtobj, LPCTSTR pStartPage) { HANDLE hThread; DWORD idThread; UINT cbStartPage = HIWORD(pStartPage) ? ((lstrlen(pStartPage)+1) * SIZEOF(TCHAR)) : 0 ; PROPSTUFF * pps = (void*)LocalAlloc(LPTR, SIZEOF(PROPSTUFF) + cbStartPage); if (pps) { pps->pdtobj = pdtobj; pdtobj->lpVtbl->AddRef(pdtobj); pps->pidlParent = ILClone(pidlParent); pps->pStartPage = pStartPage; if (HIWORD(pStartPage)) { pps->pStartPage = (LPTSTR)(pps+1); lstrcpy((LPTSTR)(pps->pStartPage), pStartPage); } hThread = CreateThread(NULL, 0, _CFSFolder_PropertiesThread, pps, 0, &idThread); if (hThread) { CloseHandle(hThread); return S_OK; } else { pdtobj->lpVtbl->Release(pdtobj); ILFree(pps->pidlParent); LocalFree((HLOCAL)pps); return E_UNEXPECTED; } } } BOOL CFSFolder_CreateFolder(HWND hwndOwner, LPCITEMIDLIST pidlAbs) { DWORD dwError; TCHAR szTemplate[8+1]; TCHAR szLongPlate[32]; TCHAR szPath[MAX_PATH * sizeof (WCHAR)]; LPTSTR pszErrorFileName; LoadString(HINST_THISDLL, IDS_FOLDERTEMPLATE, szTemplate, ARRAYSIZE(szTemplate)); LoadString(HINST_THISDLL, IDS_FOLDERLONGPLATE, szLongPlate, ARRAYSIZE(szLongPlate)); // // Make a unique directory name. // SHGetPathFromIDList(pidlAbs, szPath); // Add one for the '\\' between names; add 5 for the trailing number // (which could get large) plus a little slop // BUGBUG (Davepl) This is sort of arbitrary.. what happens on boundary conds with long paths? if (lstrlen(szPath) + 1 + lstrlen(szLongPlate) + 5 > MAX_PATH) { // The Path* functions assume this boundary is not crossed // BUGBUG: We should put a message in the user's face dwError = ERROR_FILENAME_EXCED_RANGE; // setup a path to display to the end user pszErrorFileName=szLongPlate; } else { PathYetAnotherMakeUniqueName(szPath, szPath, szTemplate, szLongPlate); if (SHCreateDirectory(hwndOwner, szPath) == 0) { LPITEMIDLIST pidl = ILCreateFromPath(szPath); if (pidl) { View_SelectAndEdit(hwndOwner, ILFindLastID(pidl), TRUE); ILFree(pidl); } return TRUE; } else { pszErrorFileName = PathFindFileName(szPath); dwError = GetLastError(); } } // We need to give an error message to the user. SHSysErrorMessageBox(hwndOwner, NULL, IDS_CANNOTCREATEFOLDER, dwError, pszErrorFileName, MB_OK | MB_ICONEXCLAMATION); return FALSE; } HMENU FindMenuBySubMenuID(HMENU hmenu, UINT id, LPINT pIndex) { int cMax; HMENU hmenuSub; HMENU hmenuReturn = NULL; MENUITEMINFO mii; mii.cbSize = SIZEOF(mii); mii.fMask = MIIM_ID; mii.cch = 0; // just in case... for (cMax = GetMenuItemCount(hmenu) - 1 ; cMax >= 0 ; cMax--) { hmenuSub = GetSubMenu(hmenu, cMax); if (hmenuSub && GetMenuItemInfo(hmenuSub, 0, TRUE, &mii)) { if (mii.wID == id) { // found it! hmenuReturn = hmenuSub; break; } } } if (hmenuReturn && pIndex) *pIndex = cMax; return hmenuReturn; } void DeleteMenuBySubMenuID(HMENU hmenu, UINT id) { int i; FindMenuBySubMenuID(hmenu, id, &i); DeleteMenu(hmenu, i, MF_BYPOSITION); } HMENU g_hmenuFileMenu = NULL; // to destroy, so we need to save it globally // Called by docfind HRESULT InvokeSendTo(HWND hwndOwner, IDataObject *pdtobj) { LPITEMIDLIST pidlFolder = NULL; LPITEMIDLIST pidlItem = NULL; HRESULT hres = E_FAIL; FileMenu_GetLastSelectedItemPidls(NULL, &pidlFolder, &pidlItem); if (pidlFolder && pidlItem) { IShellFolder *psf; IShellFolder *psfDesktop = Desktop_GetShellFolder(TRUE); hres = psfDesktop->lpVtbl->BindToObject(psfDesktop, pidlFolder, NULL, &IID_IShellFolder, &psf); if (SUCCEEDED(hres)) { IDropTarget *pdrop; hres = psf->lpVtbl->GetUIObjectOf(psf, hwndOwner, 1, &pidlItem, &IID_IDropTarget, 0, &pdrop); if (SUCCEEDED(hres)) { POINTL pt = { 0, 0 }; DWORD dwEffect = DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY; DWORD grfKeyState; if (GetAsyncKeyState(VK_SHIFT) < 0) grfKeyState = MK_SHIFT | MK_LBUTTON; // move else if (GetAsyncKeyState(VK_CONTROL) < 0) grfKeyState = MK_CONTROL | MK_LBUTTON; // copy else if (GetAsyncKeyState(VK_MENU) < 0) grfKeyState = MK_ALT | MK_LBUTTON; // link else grfKeyState = MK_LBUTTON; hres = pdrop->lpVtbl->DragEnter(pdrop, pdtobj, grfKeyState, pt, &dwEffect); if (dwEffect) { hres = pdrop->lpVtbl->Drop(pdrop, pdtobj, grfKeyState, pt, &dwEffect); } else { pdrop->lpVtbl->DragLeave(pdrop); } pdrop->lpVtbl->Release(pdrop); } psf->lpVtbl->Release(psf); } ILFree(pidlItem); ILFree(pidlFolder); } return hres; } // called by docfind... void InitSendToMenu(HMENU hmenu, UINT id) { LPCITEMIDLIST pidl; ENTERCRITICAL; if (!(pidl = GetSpecialFolderIDList(NULL, CSIDL_SENDTO, FALSE))) { // no sendto folder, remove sendto menu item. DeleteMenuBySubMenuID(hmenu, id); } LEAVECRITICAL; } void InitSendToMenuPopup(HMENU hmenu, UINT id) { if (!FileMenu_InitMenuPopup(hmenu)) { LPITEMIDLIST pidl; pidl = SHCloneSpecialIDList(NULL, CSIDL_SENDTO, FALSE); if (pidl) { // I'm not sure if this critical section is even needed, but let's be safe ENTERCRITICAL; if (g_hmenuFileMenu) { Assert(0); // Somebody forgot to call ReleaseSendToMenuPop FileMenu_Destroy(g_hmenuFileMenu); } g_hmenuFileMenu = hmenu; LEAVECRITICAL; DeleteMenu(hmenu, 0, MF_BYPOSITION); FileMenu_ReplaceUsingPidl(hmenu, id, (LPITEMIDLIST)pidl, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, NULL); ILFree(pidl); } } } void ReleaseSendToMenuPopup() { if (g_hmenuFileMenu) { FileMenu_DeleteAllItems(g_hmenuFileMenu); g_hmenuFileMenu = NULL; } } HRESULT FS_CreateLinks(HWND hwnd, IShellFolder *psf, IDataObject *pdtobj, LPCTSTR pszDir) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); HRESULT hres; TCHAR szPath[MAX_PATH]; int cItems; LPITEMIDLIST *ppidl; UINT fCreateLinkFlags; SHGetPathFromIDList(this->pidl, szPath); cItems = DataObj_GetHIDACount(pdtobj); ppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, SIZEOF(LPITEMIDLIST) * cItems); // passing ppidl == NULL is correct in failure case if ((pszDir == NULL) || (lstrcmpi(pszDir, szPath) == 0)) { // create the link in the current folder fCreateLinkFlags = SHCL_USETEMPLATE; } else { // this is a sys menu, ask to create on desktop fCreateLinkFlags = SHCL_USETEMPLATE | SHCL_USEDESKTOP; } DebugMsg(DM_TRACE, TEXT("FS_CreateLinks %x %s %s"), fCreateLinkFlags, pszDir ? pszDir : TEXT("(null)"), szPath); hres = SHCreateLinks(hwnd, szPath, pdtobj, fCreateLinkFlags, ppidl); if (ppidl) { int i; // select those objects; HWND hwndSelect = DV_HwndMain2HwndView(hwnd); // select the new links, but on the first one deselect all other selected things for (i = 0; i < cItems; i++) { if (ppidl[i]) { SendMessage(hwndSelect, SVM_SELECTITEM, i == 0 ? SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED : SVSI_SELECT, (LPARAM)ILFindLastID(ppidl[i])); ILFree(ppidl[i]); } } LocalFree((HLOCAL)ppidl); } return hres; } // // To be called back from within CDefFolderMenu // // Returns: // S_OK, if successfully processed. // S_FALSE, if default code should be used. // HRESULT CALLBACK CFSFolder_DFMCallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); HRESULT hres = S_OK; LPQCMINFO pqcm; UINT id; UINT idCmdBase; HMENU hmenu; switch (uMsg) { case DFM_WM_MEASUREITEM: #define lpmis ((LPMEASUREITEMSTRUCT)lParam) if (lpmis->itemID == (wParam + FSIDM_SENDTOFIRST)) { FileMenu_MeasureItem(NULL, lpmis); } break; #undef lpmis case DFM_WM_DRAWITEM: #define lpdis ((LPDRAWITEMSTRUCT)lParam) if (lpdis->itemID == (wParam + FSIDM_SENDTOFIRST)) { FileMenu_DrawItem(NULL, lpdis); } break; #undef lpdis case DFM_WM_INITMENUPOPUP: hmenu = (HMENU)wParam; id = GetMenuItemID(hmenu, 0); if (id == (UINT)(lParam + FSIDM_SENDTOFIRST)) { // Call common function used by us and docfind... InitSendToMenuPopup(hmenu, id); } break; case DFM_RELEASE: ReleaseSendToMenuPopup(); CleanupRegMenu(); break; case DFM_MERGECONTEXTMENU: // // We need to avoid adding SendTo // if (!(wParam & CMF_VERBSONLY)) { pqcm = (LPQCMINFO)lParam; // // This is a context menu. // idCmdBase = pqcm->idCmdFirst; // must be called before merge // treat all the menuitems (SentTo->..) as verbs in case of File. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_FSVIEW_ITEM, 0, pqcm); if (!(wParam &CMF_DEFAULTONLY)) InitSendToMenu(pqcm->hmenu, idCmdBase + FSIDM_SENDTOFIRST); } break; case DFM_GETHELPTEXT: LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));; break; case DFM_GETHELPTEXTW: LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));; break; case DFM_INVOKECOMMAND: // pdtobj should not be NULL. Assert(pdtobj); switch(wParam) { case FSIDM_SENDTOFIRST: hres = InvokeSendTo(hwndOwner, pdtobj); break; case DFM_CMD_LINK: hres = FS_CreateLinks(hwndOwner, psf, pdtobj, (LPCTSTR)lParam); break; case DFM_CMD_DELETE: hres = CFSFolder_DeleteItems(this, hwndOwner, pdtobj, SD_USERCONFIRMATION); break; case DFM_CMD_PROPERTIES: hres = CFSFolder_Properties(this, this->pidl, pdtobj, (LPCTSTR)lParam); break; default: // This is common menu items, use the default code. hres = S_FALSE; break; } break; default: hres = E_NOTIMPL; break; } return hres; } // // Return the special folder ID, if this folder is one of them. // At this point, we handle PROGRAMS folder only. // UINT CFSFolder_GetSpecialFID(LPFSFOLDER this) { // // Cache the special folder ID, if it is not cached yet. // if (this->wSpecialFID==CSIDL_NOTCACHED) { LPCITEMIDLIST pidlStartMenu, pidlCommonStartMenu; // // We need to get 'cached' one to avoid infinit recursion. // ENTERCRITICAL; pidlStartMenu= GetSpecialFolderIDList(NULL, CSIDL_STARTMENU, FALSE); pidlCommonStartMenu= GetSpecialFolderIDList(NULL, CSIDL_COMMON_STARTMENU, FALSE); if (pidlStartMenu && ILIsParent(pidlStartMenu, this->pidl, FALSE)) { this->wSpecialFID = CSIDL_PROGRAMS; } else if (pidlCommonStartMenu && ILIsParent(pidlCommonStartMenu, this->pidl, FALSE)) { this->wSpecialFID = CSIDL_COMMON_PROGRAMS; } else { this->wSpecialFID = CSIDL_NORMAL; } LEAVECRITICAL; } return this->wSpecialFID; } // // HACK: We have no official mechanism to pass hwndOwner to IDropTarget // handlers. This is a hack for Win95 so that CShellLink can get it. // We don't need to make it thread-safe. // HWND HKGetSetUIOwner(HWND hwndOwner, BOOL fSet) { static HWND s_hwndOwner = NULL; if (fSet) { s_hwndOwner = hwndOwner; } return s_hwndOwner; } STDMETHODIMP CFSFolder_GetUIObjectOf(LPSHELLFOLDER psf, HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); LPITEMIDLIST pidlFirst; HRESULT hres = E_INVALIDARG; *ppvOut = NULL; if (IsEqualIID(riid, &IID_IExtractIcon) #ifdef UNICODE || IsEqualIID(riid, &IID_IExtractIconA) #endif ) { if (cidl == 1) { hres = CFSFolder_CreateDefExtIcon(this->pidl, CFSFolder_GetSpecialFID(this), (LPCIDFOLDER)apidl[0], (IExtractIcon**)ppvOut); #ifdef UNICODE // When UNICODE is defined the CFSFolder_CreateDefExtIcon can return // either an IExtractIconW or an IExtractIconA pointer. We should QI // for one that we wanted. if (SUCCEEDED(hres)) { LPEXTRACTICON pxicon = *ppvOut; hres = pxicon->lpVtbl->QueryInterface(pxicon,riid,ppvOut); pxicon->lpVtbl->Release(pxicon); } #endif } } else if (IsEqualIID(riid, &IID_IContextMenu)) { DWORD dwDefClassUsed = SHGCK_DEFCLASS_NOTUSED; // // There should be a selection. // if (cidl && (NULL != (pidlFirst = FS_Combine(this->pidl, apidl[0])))) { // Get the hkeyProgID and hkeyBaseProgID from the first item. HKEY ahkeys[3] = { NULL, NULL, NULL}; DWORD dwFlags = SHGetClassFlags((LPIDFOLDER)apidl[0], FALSE); if (dwFlags & SHCF_UNKNOWN) { TCHAR szPath[MAX_PATH]; if (SHGetPathFromIDList(pidlFirst, szPath)) { CLSID clsid; WCHAR wszPath[MAX_PATH]; StrToOleStr(wszPath, szPath); if (SUCCEEDED(SHXGetClassFile(wszPath, &clsid))) { ahkeys[0] = ProgIDKeyFromCLSID(&clsid); } } } if (ahkeys[0] == NULL) { // if the file class has no verbs // or the control key is down and only 1 item selected... // add unknown so the user can do something if (!FS_IsFolder((LPIDFOLDER)apidl[0]) && ((GetKeyState(VK_SHIFT) < 0 && (cidl == 1) && !(dwFlags & SHCF_IS_LINK)) || !(dwFlags & (SHCF_HAS_VERBS|SHCF_UNKNOWN)))) SHRegOpenKey(HKEY_CLASSES_ROOT, c_szUnknownClass, &ahkeys[0]); SHGetClassKey((LPIDFOLDER)pidlFirst, &ahkeys[1], &dwDefClassUsed, FALSE); if (SHGCK_DEFCLASS_UNKNOWN == dwDefClassUsed && NULL != ahkeys[0]) { // // Both ahkeys[0] and ahkeys[1] contain "Unknown" class key. // Close the duplicate key and clear the array element. // SHCloseClassKey(ahkeys[1]); ahkeys[1] = NULL; } } // // If the class is not a link AND SHGetClassKey didn't use the Base class // as a default, get the base class key. // if (!(dwFlags & SHCF_IS_LINK) && dwDefClassUsed != SHGCK_DEFCLASS_BASE) SHGetBaseClassKey((LPIDFOLDER)pidlFirst, &ahkeys[2]); hres = CDefFolderMenu_Create2(this->pidl, hwndOwner, cidl, apidl, psf, CFSFolder_DFMCallBack, 3, ahkeys, (LPCONTEXTMENU *)ppvOut); if (ahkeys[0]) SHCloseClassKey(ahkeys[0]); if (ahkeys[1]) SHCloseClassKey(ahkeys[1]); if (ahkeys[2]) SHCloseClassKey(ahkeys[2]); ILFree(pidlFirst); } } else if (cidl > 0 && IsEqualIID(riid, &IID_IDataObject)) { // // We load class specific data object handler only when the OLE // is already loaded. Providing object specific data won't provide // any feature to the sytem unless there is at lease one OLE target. // LPDATAOBJECT pdtInner = NULL; #ifdef OLE_DELAYED_LOADING if ((cidl == 1) && g_hmodOLE) { #else if ((cidl ==1)) { #endif DWORD dwFlags = SHGetClassFlags((LPIDFOLDER)apidl[0], FALSE); if (dwFlags & SHCF_HAS_DATAHANDLER) { if (NULL != (pidlFirst = FS_Combine(this->pidl, apidl[0]))) { hres = FSLoadHandler(pidlFirst, c_szDataHandler, &IID_IDataObject, (IUnknown**)&pdtInner); ILFree(pidlFirst); } } } #ifdef CAIRO_DS if (this->fIsDSFolder) { hres = FSDS_CreateFSIDArray(this->pidl, cidl, apidl, pdtInner, (LPDATAOBJECT*)ppvOut); } else { #endif // CAIRO_DS hres = _FS_CreateFSIDArrayFromFSFolder(psf, this->pidl, cidl, apidl, pdtInner, (LPDATAOBJECT*)ppvOut); #ifdef CAIRO_DS } #endif if (pdtInner) { pdtInner->lpVtbl->Release(pdtInner); } } else if (IsEqualIID(riid, &IID_IDropTarget)) { hres = E_FAIL; // IDropTarget must be a single object. if (cidl == 1) { if (FS_IsFolder((LPIDFOLDER)apidl[0]) || FS_IsJunction((LPIDFOLDER)apidl[0])) { LPSHELLFOLDER psfT; hres = CFSFolder_BindToObject(psf, apidl[0], NULL, &IID_IShellFolder, &psfT); if (SUCCEEDED(hres)) { hres = psfT->lpVtbl->CreateViewObject(psfT, hwndOwner, &IID_IDropTarget, ppvOut); psfT->lpVtbl->Release(psfT); } } else { LPCITEMIDLIST pidlChild; BOOL bFreePidl = FALSE; // // apidl could contain absolute pidls rather than relative. // if (ILIsEmpty(apidl[0]) || (ILFindLastID(apidl[0]) == apidl[0])) { pidlChild = apidl[0]; pidlFirst = FS_Combine(this->pidl, pidlChild); bFreePidl = TRUE; } else { pidlChild = ILFindLastID(apidl[0]); pidlFirst = (LPITEMIDLIST)apidl[0]; } Assert(FS_IsFile((LPIDFOLDER)pidlChild)); if (NULL != pidlFirst) { DWORD dwFlags = SHGetClassFlags((LPIDFOLDER)pidlChild, FALSE); if (dwFlags & SHCF_HAS_DROPHANDLER) { HKGetSetUIOwner(hwndOwner, TRUE); hres = FSLoadHandler(pidlFirst, c_szDropHandler, &IID_IDropTarget, (IUnknown**)ppvOut); } // BUGBUG: we need a handler registered for exe types. else { TCHAR szName[MAX_PATH]; FS_CopyName((LPCIDFOLDER)pidlChild, szName,ARRAYSIZE(szName)); if (PathIsExe(szName)) { hres = CIDLDropTarget_Create(hwndOwner, &c_CExeDropTargetVtbl, pidlFirst, (LPDROPTARGET *)ppvOut); } } if (bFreePidl) { ILFree(pidlFirst); } } } } } return hres; } STDMETHODIMP CFSFolder_GetDisplayNameOf(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET pStrRet) { // Note that psf could be NULL! HRESULT hres=E_INVALIDARG; LPCITEMIDLIST pidlLast = ILFindLastID(pidl); if (FS_IsValidID(pidlLast)) { LPIDFOLDER pidf = (LPIDFOLDER)pidlLast; LPCITEMIDLIST pidlNext = _ILNext(pidlLast); if (uFlags & SHGDN_FORPARSING) { // // We need to return 'path' // Assert(psf); // This is fatal. // // Check if we need to return a relative path or absolute path. // if (uFlags & SHGDN_INFOLDER) { // // Relative path. // if (ILIsEmpty(pidlNext)) { if ((FS_GetType(pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE) { #ifdef UNICODE WCHAR szTmp[MAX_PATH]; FS_CopyName(pidf, szTmp, ARRAYSIZE(szTmp)); pStrRet->pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTmp)+1)*SIZEOF(TCHAR)); if ( pStrRet->pOleStr != NULL ) { pStrRet->uType = STRRET_OLESTR; lstrcpy(pStrRet->pOleStr, szTmp); hres = S_OK; } else { hres = E_OUTOFMEMORY; } #else pStrRet->uType = STRRET_CSTR; FS_CopyName(pidf, pStrRet->cStr, ARRAYSIZE(pStrRet->cStr)); hres = S_OK; #endif } else { pStrRet->uType = STRRET_OFFSET; pStrRet->uOffset = (LPBYTE)FS_GetName(pidf) - (LPBYTE)pidl; hres = S_OK; } } else { TCHAR szTmp[MAX_PATH]; FS_CopyName(pidf, szTmp, ARRAYSIZE(szTmp)); hres = ILGetRelDisplayName(psf, pStrRet, pidlLast, szTmp, MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_BACKSLASH)); } } else { // // Absolute path. // LPFSFOLDER this = IToClass(CFSFolder, sf, psf); LPCITEMIDLIST pidlJctn = FSFindJunction(pidlNext); if (pidlJctn) { LPCITEMIDLIST pidlJNext = _ILNext(pidlJctn); if (!ILIsEmpty(pidlJNext)) { LPITEMIDLIST pidlBind = ILClone(pidlJctn); if (pidlBind) { LPSHELLFOLDER psfJctn; _ILNext(pidlBind)->mkid.cb = 0; hres = SUCCEEDED(FSBindToObject(&CLSID_NULL, this->pidl, pidlJctn, NULL, &IID_IShellFolder, &psfJctn)); ILFree(pidlBind); if (SUCCEEDED(hres)) { hres = psfJctn->lpVtbl->GetDisplayNameOf(psfJctn, pidlJNext, uFlags, pStrRet); psfJctn->lpVtbl->Release(psfJctn); } } else hres = E_OUTOFMEMORY; } else pidlJctn = NULL; } if (!pidlJctn) hres = SHGetPathHelper(this->pidl, pidlLast, pStrRet); } } else { BOOL fShowExt; BOOL fBeautifyourSelf; LPTSTR pszName; #ifdef UNICODE TCHAR szName[MAX_PATH]; FS_CopyName(pidf, szName, ARRAYSIZE(szName)); pszName = szName; #else pszName = FS_GetName(pidf); #endif // // This is not fatal, but the shell is not supposed to // pass multi-level pidl without SHGDN_FORPARSING. // Assert(ILIsEmpty(pidlNext)); // We try to calculate if we should try to make the name look pretty. For // most cases this is done as part of the pidl, but if someone call // SHGetFileInfo with user attributes it is not... fBeautifyourSelf = (pidf->fs.dateModified == 0) && ((pidf->fs.wAttrs & FSTREEX_ATTRIBUTE_NOLFN) == 0) && (lstrlen(pszName) <= 12); if (fBeautifyourSelf) { #ifdef UNICODE TCHAR szAltName[MAX_PATH]; FS_CopyAltName(pidf,szAltName,ARRAYSIZE(szAltName)); if (lstrlen(szAltName) != 0) { fBeautifyourSelf = FALSE; } #else fBeautifyourSelf = (lstrlen(pszName + lstrlen(pszName) +1) == 0); #endif } // // No, it contains only one ID // // Check if we need to hide extension or not. // Also if it is a simple pidl we may also want to make the name look pretty.. // { #ifdef CAIRO_DS LPFSFOLDER this = IToClass(CFSFolder, sf, psf); TCHAR szPath[MAX_PATH]; if ((this) && (this->fIsDSFolder)) { LPCITEMIDLIST pidlAbs = ILCombine(this->pidl, pidlLast); fShowExt = FS_ShowExtension((LPIDFOLDER)pidlAbs, TRUE); } else { #endif // CAIRO_DS fShowExt = FS_ShowExtension(pidf, FALSE); #ifdef CAIRO_DS } #endif //CAIRO_DS } hres = S_OK; if ((FS_GetType(pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE) { #ifdef UNICODE if (!fShowExt || fBeautifyourSelf) { // Yes, we need hide the extension. Copy the primaty name // and remove the extension. if (!fShowExt) PathRemoveExtension(szName); if (fBeautifyourSelf) PathMakePretty(szName); } pStrRet->pOleStr = (LPOLESTR)SHAlloc((lstrlen(szName)+1)*SIZEOF(TCHAR)); if ( pStrRet->pOleStr != NULL ) { pStrRet->uType = STRRET_OLESTR; lstrcpy(pStrRet->pOleStr, szName); } else { hres = E_OUTOFMEMORY; } #else pStrRet->uType = STRRET_CSTR; FS_CopyName(pidf, pStrRet->cStr, ARRAYSIZE(pStrRet->cStr)); if ( !fShowExt || fBeautifyourSelf) { // Yes, we need hide the extension. Copy the primaty name // and remove the extension. if (!fShowExt) PathRemoveExtension(pStrRet->cStr); if (fBeautifyourSelf) PathMakePretty(pStrRet->cStr); } #endif } else { if ( !fShowExt || fBeautifyourSelf) { // Yes, we need hide the extension. Copy the primary name // and remove the extension. #ifdef UNICODE TCHAR szName[MAX_PATH]; pStrRet->uType = STRRET_CSTR; MultiByteToWideChar(CP_ACP, 0, FS_GetName((LPIDFOLDERA)pidf), -1, szName, ARRAYSIZE(szName)); if (!fShowExt) PathRemoveExtension(szName); if (fBeautifyourSelf) PathMakePretty(szName); WideCharToMultiByte(CP_ACP, 0, szName, -1, pStrRet->cStr, ARRAYSIZE(pStrRet->cStr), NULL, NULL); #else pStrRet->uType = STRRET_CSTR; lstrcpy(pStrRet->cStr,FS_GetName((LPIDFOLDERA)pidf)); if (!fShowExt) PathRemoveExtension(pStrRet->cStr); if (fBeautifyourSelf) PathMakePretty(pStrRet->cStr); #endif } else { pStrRet->uType = STRRET_OFFSET; pStrRet->uOffset = (LPBYTE)FS_GetName(pidf) - (LPBYTE)pidl; } } } } return hres; } void FS_GetDisplayNameOf(HIDA hida, UINT i, LPTSTR pszBuffer) { STRRET str; BOOL fAllocated; LPCITEMIDLIST pidlRel = IDA_GetRelativeIDListPtr((LPIDA)GlobalLock(hida), i, &fAllocated); if (pidlRel) { HRESULT hres = CFSFolder_GetDisplayNameOf(NULL, pidlRel, SHGDN_NORMAL, &str); if (SUCCEEDED(hres)) { StrRetToStrN(pszBuffer, MAX_PATH, &str, pidlRel); } if (fAllocated) { ILFree ((LPITEMIDLIST)pidlRel); } } } HRESULT CFSFolder_SetNameOf(LPSHELLFOLDER psf, HWND hwndOwner, LPCITEMIDLIST pidl, LPCOLESTR lpsName, DWORD uFlags, LPITEMIDLIST * ppidlOut) { LPFSFOLDER this = IToClass(CFSFolder, sf, psf); HRESULT hres; TCHAR szNewName[MAX_PATH], szDir[MAX_PATH]; TCHAR szOldName[MAX_PATH]; LPCIDFOLDER pidf = (LPCIDFOLDER)pidl; #ifdef CAIRO_DS LPITEMIDLIST pidlAbs; #endif UINT iDirLen; FS_CopyName(pidf, szOldName, ARRAYSIZE(szDir)); OleStrToStr(szNewName, lpsName); // // If we have hidden the extension, append it to the new name. // if (!FS_ShowExtension(pidf, FALSE)) { LPCTSTR pszExt = PathFindExtension(szOldName); if (*pszExt) { // Note that we can't call PathAddExtension, which removes // existing extension. lstrcat(szNewName, pszExt); } } SHGetPathFromIDList(this->pidl, szDir); iDirLen = lstrlen(szDir); // There are cases where the old name exceeded the maximum path, which // would give a bogus error message. To avoid this we should check for // this case and see if using the short name for the file might get // around this... // if (iDirLen + lstrlen(szOldName) + 2 > MAX_PATH) { TCHAR szOldAltName[MAX_PATH]; FS_CopyAltName(pidf,szOldAltName,ARRAYSIZE(szOldAltName)); if (iDirLen + lstrlen(szOldAltName) + 2 <= MAX_PATH) lstrcpy(szOldName,szOldAltName); } #ifdef CAIRO_DS // BUGBUG- -errors not handled if (this->fIsDSFolder) { DWORD dwFlags; pidlAbs = FS_Combine (this->pidl, pidl); dwFlags = SHGetClassFlags ((LPCIDFOLDER)pidlAbs, TRUE); if (dwFlags & SHCF_SUPPORTS_IOBJLIFE) { hres = DoDSRename (szDir, szOldName, szNewName); } if (SUCCEEDED(hres)) { if (ppidlOut) { PathAppend(szDir, szNewName); DebugMsg(DM_TRACE, TEXT("sh TR - CFSFolder_SetNameOf returning pidl for %s"), szDir); hres = CFSFolder_CreateIDForItem(szDir, ppidlOut, TRUE); } } } else { #endif switch (SHRenameFile(hwndOwner, szDir, szOldName, szNewName, FALSE)) { case ERROR_PATH_NOT_FOUND: case ERROR_ACCESS_DENIED: case DE_RENAMREPLACE: hres = E_FAIL; if (ppidlOut) { *ppidlOut = NULL; } break; case 0: default: hres = S_OK; // // Return the new pidl if ppidlOut is specified. // if (ppidlOut) { PathAppend(szDir, szNewName); DebugMsg(DM_TRACE, TEXT("sh TR - CFSFolder_SetNameOf returning pidl for %s"), szDir); hres = CFSFolder_CreateIDForItem(szDir, ppidlOut, TRUE); } break; } #ifdef CAIRO_DS } #endif return hres; } STDMETHODIMP FS_GetDetailsOf(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS lpDetails) { LPIDFOLDER pidf = (LPIDFOLDER)pidl; #ifdef UNICODE TCHAR szTemp[MAX_PATH]; #endif if (iColumn >= FS_ICOL_MAX) { return(E_NOTIMPL); } lpDetails->str.uType = STRRET_CSTR; lpDetails->str.cStr[0] = '\0'; if (!pidf) { // // If getting the title for the Attributes column, and the // attribute character list hasn't been initialized, do so. // These are the characters used to represent file attributes // in the explorer details list view. // if (FS_ICOL_ATTRIB == s_fs_cols[iColumn].iCol && TEXT('\0') == g_szAttributeChars[0]) { LoadString(HINST_THISDLL, IDS_ATTRIB_CHARS, g_szAttributeChars, ARRAYSIZE(g_szAttributeChars)); } #ifdef UNICODE LoadString(HINST_THISDLL, s_fs_cols[iColumn].ids, szTemp, ARRAYSIZE(szTemp)); lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR)); if ( lpDetails->str.pOleStr != NULL ) { lpDetails->str.uType = STRRET_OLESTR; lstrcpy(lpDetails->str.pOleStr, szTemp); } else { return E_OUTOFMEMORY; } #else LoadString(HINST_THISDLL, s_fs_cols[iColumn].ids, lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr)); #endif lpDetails->fmt = s_fs_cols[iColumn].iFmt; lpDetails->cxChar = s_fs_cols[iColumn].cchCol; return S_OK; } switch (iColumn) { case FS_ICOL_NAME: if ((FS_GetType(pidf) & SHID_FS_UNICODE) == SHID_FS_UNICODE) { #ifdef UNICODE FS_CopyName(pidf, szTemp, ARRAYSIZE(szTemp)); lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR)); if ( lpDetails->str.pOleStr != NULL ) { lpDetails->str.uType = STRRET_OLESTR; lstrcpy(lpDetails->str.pOleStr, szTemp); } else { return E_OUTOFMEMORY; } #else lpDetails->str.uType = STRRET_CSTR; FS_CopyName(pidf, lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr)); #endif } else { lpDetails->str.uType = STRRET_OFFSET; lpDetails->str.uOffset = (LPBYTE)FS_GetName(pidf) - (LPBYTE)pidl; } break; case FS_ICOL_SIZE: if (!FS_IsFolder(pidf)) { ULONGLONG cbSize; TCHAR szNum[MAX_COMMA_AS_K_SIZE]; FS_GetSize(pidlParent, pidf, &cbSize); SizeFormatAsK64(cbSize, szNum); #ifdef UNICODE lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szNum)+1)*SIZEOF(TCHAR)); if ( lpDetails->str.pOleStr != NULL ) { lpDetails->str.uType = STRRET_OLESTR; lstrcpy(lpDetails->str.pOleStr, szNum); } else { return E_OUTOFMEMORY; } #else lstrcpy(lpDetails->str.cStr, szNum); #endif } break; case FS_ICOL_TYPE: #ifdef UNICODE FS_GetTypeName((LPIDFOLDER)pidf, szTemp, ARRAYSIZE(szTemp)); lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR)); if ( lpDetails->str.pOleStr != NULL ) { lpDetails->str.uType = STRRET_OLESTR; lstrcpy(lpDetails->str.pOleStr, szTemp); } else { return E_OUTOFMEMORY; } #else FS_GetTypeName((LPIDFOLDER)pidf, lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr)); #endif break; case FS_ICOL_MODIFIED: #ifdef UNICODE BldDateTimeString(pidf->fs.dateModified, pidf->fs.timeModified, szTemp); lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR)); if ( lpDetails->str.pOleStr != NULL ) { lpDetails->str.uType = STRRET_OLESTR; lstrcpy(lpDetails->str.pOleStr, szTemp); } else { return E_OUTOFMEMORY; } #else BldDateTimeString(pidf->fs.dateModified, pidf->fs.timeModified, lpDetails->str.cStr); #endif break; case FS_ICOL_ATTRIB: { LPTSTR pszAttributes = NULL; #ifdef UNICODE lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((NUM_ATTRIB_CHARS + 1)*SIZEOF(TCHAR)); if (NULL != lpDetails->str.pOleStr) { lpDetails->str.uType = STRRET_OLESTR; pszAttributes = lpDetails->str.pOleStr; } else return E_OUTOFMEMORY; #else pszAttributes = lpDetails->str.cStr; #endif BuildAttributeString(pidf->fs.wAttrs, pszAttributes, NUM_ATTRIB_CHARS + 1); } break; } return(S_OK); } STDMETHODIMP FS_ColumnClick(HWND hwndMain, UINT iColumn) { Assert(iColumn < FS_ICOL_MAX); ShellFolderView_ReArrange(hwndMain, iColumn); return S_OK; } HRESULT FSTree_SimpleIDListFromPath(LPCTSTR pszPath, LPITEMIDLIST *ppidl) { IDFOLDER idf; // This is large, so do not recurse CLSID clsid; LPIDFOLDER pidf, pidfNew; LPCTSTR pszNext; UINT cbSize; BOOL fCLSID; #ifdef UNICODE CHAR szPathAnsi[MAX_PATH]; WCHAR szPathWide[MAX_PATH]; BOOL fUnicode; #endif idf.bFlags = SHID_FS; // I don't know what it is idf.fs.dwSize = 0; idf.fs.dateModified = 0; idf.fs.timeModified = 0; idf.fs.wAttrs = 0; #ifdef UNICODE WideCharToMultiByte(CP_ACP, 0, pszPath, -1, szPathAnsi, ARRAYSIZE(szPathAnsi), NULL, NULL); MultiByteToWideChar(CP_ACP, 0, szPathAnsi, -1, szPathWide, ARRAYSIZE(szPathWide)); if (lstrcmp(pszPath,szPathWide) != 0) { idf.bFlags = SHID_FS_UNICODE; fUnicode = TRUE; cbSize = ((3 * lstrlen(pszPath) / 2) + 2) * SIZEOF(TCHAR); } else { fUnicode = FALSE; cbSize = (3 * lstrlen(pszPath) / 2) + 2; // Compute for ANSI idl } #else // The path length should be a good approximation of the ID length // Add 2 in case a 0 length string is passed in cbSize = (3 * lstrlen(pszPath) / 2) + 2; #endif pidf = (LPIDFOLDER)_ILCreate(cbSize); if (!pidf) { *ppidl = NULL; return E_OUTOFMEMORY; } pidf->cb = 0; for (pszNext = pszPath; *pszNext; pszPath = pszNext + 1) { UINT uLen; pszNext = StrChr(pszPath, TEXT('\\')); if (!pszNext) { pszNext = pszPath + lstrlen(pszPath); } // uLen will be count of CHARACTERS uLen = pszNext - pszPath; // Do some simple checks if (uLen == 0 || uLen >= ARRAYSIZE(idf.fs.cFileName)) { ILFree((LPITEMIDLIST)pidf); return E_INVALIDARG; } // Always fill in the long name, and NULL the short name #ifdef UNICODE if (fUnicode) { lstrcpyn(idf.fs.cFileName, pszPath, uLen + 1); cbSize = (uLen + 1) * SIZEOF(TCHAR); idf.fs.cFileName[uLen + 1] = TEXT('\0'); fCLSID = _GetFileCLSID(idf.fs.cFileName, &clsid); } else { WideCharToMultiByte(CP_ACP, 0, pszPath, uLen+1, ((LPIDFOLDERA)&idf)->fs.cFileName, ARRAYSIZE(((LPIDFOLDERA)&idf)->fs.cFileName), NULL, NULL ); cbSize = (uLen + 1); // Null terminate name ((LPIDFOLDERA)&idf)->fs.cFileName[uLen] = '\0'; // Null short name ((LPIDFOLDERA)&idf)->fs.cFileName[uLen + 1] = '\0'; lstrcpyn(szPathWide,pszPath,uLen+1); szPathWide[uLen+1] = TEXT('\0'); fCLSID = _GetFileCLSID(szPathWide,&clsid); } #else lstrcpyn(idf.fs.cFileName, pszPath, uLen + 1); cbSize = (uLen + 1); idf.fs.cFileName[uLen + 1] = TEXT('\0'); fCLSID = _GetFileCLSID(idf.fs.cFileName, &clsid); #endif cbSize++; // The 2 is for the NULL's idf.cb = FIELDOFFSET(IDFOLDER, fs.cFileName) + cbSize; // Try to handle the cases of the filename is in the form of // foo.{GUID} to set the junction point and work from there. if (fCLSID) { UNALIGNED CLSID *pclsid; idf.bFlags |= SHID_JUNCTION; idf.cb += SIZEOF(CLSID); pclsid = (UNALIGNED CLSID *)FS_GetCLSID(&idf); // We may need to allocate more room for this, but not overly likely that // we will need it and at worst it should overlap copy from the guid // structure below it. *pclsid = clsid; DebugMsg(DM_TRACE, TEXT("FSTree_SimpleIDListFromPath - handle guid file name %s"), idf.fs.cFileName); } pidfNew = (LPIDFOLDER)ILAppendID((LPITEMIDLIST)pidf, (LPSHITEMID)&idf, TRUE); if (!pidfNew) { ILFree((LPITEMIDLIST)pidf); return E_OUTOFMEMORY; } pidf = pidfNew; } *ppidl = (LPITEMIDLIST)pidf; return S_OK; } //=========================================================================== // File system related binding code //=========================================================================== // // Internal function prototype. // STDAPI SILBindToFSFolder(LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut); STDAPI SILRelBindToFSFolder(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlRel, REFIID riid, LPVOID * ppvOut); LPCITEMIDLIST FSFindJunction(LPCITEMIDLIST pidl) { while (pidl->mkid.cb) { UINT uType = pidl->mkid.abID[0]; if (uType & SHID_JUNCTION) return pidl; pidl = _ILNext(pidl); } return NULL; } LPCITEMIDLIST FSFindJunctionNext(LPCITEMIDLIST pidl) { for ( ; pidl->mkid.cb ; pidl=_ILNext(pidl)) { UINT uType = pidl->mkid.abID[0]; if (uType & SHID_JUNCTION) { pidl = _ILNext(pidl); break; } } return pidl; } // // Requires: // pidl points a file system object (either file or directory) // // Parameters: // rclsid -- Known clsid of the folder we want to bind to. // Use &CLSID_NULL if not known (typically the case). // pidl -- Absolute IDList // riid -- Required interface // ppvOut -- Points to the variable in which the interface // pointer should be returned. // HRESULT FSBindToFSFolder(REFCLSID rclsid, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut) { HRESULT hres; LPCIDFOLDER pidf = (LPCIDFOLDER)ILFindLastID(pidl); if (FS_IsJunction(pidf)) { CLSID clsid, *pclsid = NULL; const UNALIGNED CLSID * uapclsid = FS_GetCLSID(pidf); if (uapclsid) { clsid = *uapclsid; pclsid = &clsid; } #ifdef SYNC_BRIEFCASE // Is this folder a briefcase? if (IsEqualCLSID(pclsid, &CLSID_Briefcase)) { // Yes; treat the briefcase folder almost like a standard // CFSFolder, but with a different v-table. Don't // bother going thru registry to find the instance // constructor. hres = CFSBrfFolder_CreateFromIDList(pidl, riid, ppvOut); goto Leave; } else #endif // SYNC_BRIEFCASE { IPersistFolder * ppf; // // There is a special CLSID for this folder. Attempt // to get the IPersistFolder interface to it. // hres = SHCoCreateInstance(NULL, pclsid, NULL, &IID_IPersistFolder, &ppf); if (SUCCEEDED(hres)) { hres = ppf->lpVtbl->Initialize(ppf, pidl); if (SUCCEEDED(hres)) { hres = ppf->lpVtbl->QueryInterface(ppf, riid, ppvOut); } ppf->lpVtbl->Release(ppf); } #ifdef CLOUDS // HACK: if it failed then check to see if it's the credits folder if (!SUCCEEDED(hres) && IsEqualCLSID(pclsid, &CLSID_Clouds)) hres = Clouds_CreateFromIDList(pidl, riid, ppvOut); #endif goto Leave; } } #ifdef SYNC_BRIEFCASE // Is this folder a subfolder inside a briefcase? else if (IsEqualCLSID(rclsid, &CLSID_Briefcase)) { // Yes hres = CFSBrfFolder_CreateFromIDList(pidl, riid, ppvOut); goto Leave; } #endif // SYNC_BRIEFCASE #ifdef USE_OLEDB // // We're binding to a COFSFolder // hres = COFSFolder_CreateFromIDList(pidl, riid, ppvOut); #else // // We're binding to a standard CFSFolder // hres = CFSFolder_CreateFromIDList(pidl, riid, ppvOut); #endif Leave: return hres; } HRESULT FSRelBindToFSFolder(REFCLSID rclsid, LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlRel, REFIID riid, LPVOID * ppvOut) { HRESULT hres=E_OUTOFMEMORY; LPITEMIDLIST pidl = ILCombine(pidlParent, pidlRel); if (pidl) { hres = FSBindToFSFolder(rclsid, pidl, riid, ppvOut); ILFree(pidl); } return hres; } // // Description: // This function creates an instance of file/directory, which is // specified by pidlParent and pidlRel. If it finds any junction // point in the middle of pidRel, it binds to the IShellFolder // interface of the junction point, and let it handle the rest. // // Parameters: // pclsidKnown -- Typically &CLSID_NULL. But on some occasions we know // the clsid of a folder we want to bind to, // and this clsid is based upon the parent folder. // Subfolders in a briefcase are a prime example. // pidlParent -- First part of the IDList, we have already evaluated this part. // pidlRel -- Second part of the IDList, we don't know what's in it. // // Comments: // It is important to note that this function does not parse pidlParent. // HRESULT FSBindToObject(REFCLSID rclsidKnown, LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlRel, LPBC pbc, REFIID riid, LPVOID *ppvOut) { HRESULT hres; LPCITEMIDLIST pidlRight = FSFindJunctionNext(pidlRel); if (ILIsEmpty(pidlRight)) { hres = FSRelBindToFSFolder(rclsidKnown, pidlParent, pidlRel, riid, ppvOut); } else { LPITEMIDLIST pidlLeft = ILClone(pidlRel); hres = E_OUTOFMEMORY; if (pidlLeft) { LPSHELLFOLDER pshf; _ILSkip(pidlLeft, (ULONG)pidlRight-(ULONG)pidlRel)->mkid.cb = 0; hres = FSRelBindToFSFolder(rclsidKnown, pidlParent, pidlLeft, &IID_IShellFolder, &pshf); if (SUCCEEDED(hres)) { hres = pshf->lpVtbl->BindToObject(pshf, pidlRight, pbc, riid, ppvOut); pshf->lpVtbl->Release(pshf); } ILFree(pidlLeft); } } return hres; } //=========================================================================== void FS_CommonPrefix(LPCITEMIDLIST *ppidl1, LPCITEMIDLIST *ppidl2) { LPCIDFOLDER pidf1 = (LPCIDFOLDER)(*ppidl1); LPCIDFOLDER pidf2 = (LPCIDFOLDER)(*ppidl2); BOOL bJunction; HRESULT hres; while (!FS_IsEmpty(pidf1) && !FS_IsEmpty(pidf2)) { // Check whether either one is a junction, but don't break yet bJunction = (pidf1->bFlags|pidf2->bFlags) & SHID_JUNCTION; hres = FS_CompareItemIDs((LPCSHITEMID)pidf1, (LPCSHITEMID)pidf2); if (hres != ResultFromShort(0)) { break; } pidf1 = FS_Next(pidf1); pidf2 = FS_Next(pidf2); if (bJunction) { // We'll just stop at junction points break; } } // Return the new pointers *ppidl1 = (LPCITEMIDLIST)pidf1; *ppidl2 = (LPCITEMIDLIST)pidf2; } //=========================================================================== // // SHGetFileIcon // // This function returns the icon handle to be used to represent the specified // file. The caller should destroy the icon eventually. // //=========================================================================== HICON WINAPI SHGetFileIcon(HINSTANCE hinst, LPCTSTR pszPath, DWORD dwFileAttributes, UINT uFlags) { SHFILEINFO sfi; SHGetFileInfo(pszPath, dwFileAttributes, &sfi, SIZEOF(sfi), uFlags | SHGFI_ICON); return sfi.hIcon; } //=========================================================================== // // SHGetFileInfo // // This function returns shell info about a given pathname. // a app can get the following: // // Icon (large or small) // Display Name // Name of File Type // // this function replaces SHGetFileIcon // //=========================================================================== DWORD WINAPI SHGetFileInfo(LPCTSTR pszPath, DWORD dwFileAttributes, SHFILEINFO *psfi, UINT cbFileInfo, UINT uFlags) { LPITEMIDLIST pidlFull; LPITEMIDLIST pidlLast; LPSHELLFOLDER psf; LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE); DWORD res=1; HRESULT hres; TCHAR achPath[MAX_PATH]; // BUGBUG // stupid hack to flush the icon cache // take this out. if (pszPath == NULL) { _IconCacheSave(); return 0; } // // another silly hack to get EXE type // if (uFlags == SHGFI_EXETYPE) { return GetExeType(pszPath); } if (psfi == NULL) return 0; psfi->hIcon = 0; psfi->szDisplayName[0] = 0; psfi->szTypeName[0] = 0; // // do some simmple check on the input path. // if (!(uFlags & SHGFI_PIDL)) { if (uFlags & SHGFI_USEFILEATTRIBUTES) { if ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_READONLY))) { DebugMsg(DM_TRACE, TEXT("sh TR - SHGetFileInfo cant use SHGFI_USEFILEATTRIBUTES for a sys/ro directory (possible junction)")); uFlags &= ~SHGFI_USEFILEATTRIBUTES; } else if (PathIsRoot(pszPath)) { DebugMsg(DM_TRACE, TEXT("sh TR - SHGetFileInfo cant use SHGFI_USEFILEATTRIBUTES for a roots")); uFlags &= ~SHGFI_USEFILEATTRIBUTES; } } if (PathIsRelative(pszPath)) { GetCurrentDirectory(ARRAYSIZE(achPath), achPath); PathCombine(achPath, achPath, pszPath); pszPath = achPath; } } if (uFlags & SHGFI_PIDL) pidlFull = (LPITEMIDLIST)pszPath; else if (uFlags & SHGFI_USEFILEATTRIBUTES) pidlFull = SHSimpleIDListFromPath(pszPath); else pidlFull = ILCreateFromPath(pszPath); if (pidlFull == NULL) return 0; pidlLast = (LPITEMIDLIST)ILFindLastID(pidlFull); // // use dwFileAttributes if it is specified. // if ((uFlags & SHGFI_USEFILEATTRIBUTES) && IS_PATHIDL(pidlLast)) { BYTE bJunction = ((LPIDFOLDER)pidlLast)->bFlags & SHID_JUNCTION; BYTE bUnicode = ((LPIDFOLDER)pidlLast)->bFlags & SHID_FS_UNICODE; Assert((((LPIDFOLDER)pidlLast)->bFlags & SHID_TYPEMASK) == SHID_FS || (((LPIDFOLDER)pidlLast)->bFlags & SHID_TYPEMASK) == SHID_FS_UNICODE); if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { ((LPIDFOLDER)pidlLast)->bFlags |= SHID_FS_DIRECTORY | bJunction | bUnicode; } else { ((LPIDFOLDER)pidlLast)->bFlags |= SHID_FS_FILE | bJunction | bUnicode; } } // // get type name for the path // if (uFlags & SHGFI_TYPENAME) { FS_GetTypeName((LPIDFOLDER)pidlLast, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName)); } if (uFlags & ( SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_ICONLOCATION | SHGFI_ICON)) { if (pidlLast == pidlFull) { DebugMsg(DM_TRACE, TEXT("sh TR - SHGetFileInfo get info for desktop")); psf = psfDesktop; psf->lpVtbl->AddRef(psf); hres = S_OK; } else { USHORT cb = pidlLast->mkid.cb; pidlLast->mkid.cb = 0; // truncate hres = psfDesktop->lpVtbl->BindToObject(psfDesktop, pidlFull, NULL, &IID_IShellFolder, &psf); pidlLast->mkid.cb = cb; // restore } if (FAILED(hres)) { res = 0; goto exit; } // // get attributes for file // if (uFlags & SHGFI_ATTRIBUTES) { psfi->dwAttributes = 0xFFFFFFFF; // get all of them psf->lpVtbl->GetAttributesOf(psf, 1, &pidlLast, &psfi->dwAttributes); } // // get icon location, place the icon path into szDisplayName // if (uFlags & SHGFI_ICONLOCATION) { IExtractIcon *pxi; UINT wFlags; if (SUCCEEDED(psf->lpVtbl->GetUIObjectOf(psf, NULL, 1, &pidlLast, &IID_IExtractIcon, NULL, &pxi))) { pxi->lpVtbl->GetIconLocation(pxi, 0, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName), &psfi->iIcon, &wFlags); pxi->lpVtbl->Release(pxi); // // the returned location is not a filename we cant return it. // just give then nothing. // if (wFlags & GIL_NOTFILENAME) { // special case one of our shell32.dll icons...... if (psfi->szDisplayName[0] != TEXT('*')) psfi->iIcon=0; psfi->szDisplayName[0] = 0; } } } // // get the icon for the file. // if ((uFlags & SHGFI_SYSICONINDEX) || (uFlags & SHGFI_ICON)) { if (himlIcons == NULL) FileIconInit( FALSE ); if (uFlags & SHGFI_SYSICONINDEX) res = (DWORD)((uFlags & SHGFI_SMALLICON) ? himlIconsSmall : himlIcons); if (uFlags & SHGFI_OPENICON) SHMapPIDLToSystemImageListIndex(psf, pidlLast, &psfi->iIcon); else psfi->iIcon = SHMapPIDLToSystemImageListIndex(psf, pidlLast, NULL); } if (uFlags & SHGFI_ICON) { HIMAGELIST himl; UINT flags=0; int cx, cy; if (uFlags & SHGFI_SMALLICON) { himl = himlIconsSmall; cx = GetSystemMetrics(SM_CXSMICON); cy = GetSystemMetrics(SM_CYSMICON); } else { himl = himlIcons; cx = GetSystemMetrics(SM_CXICON); cy = GetSystemMetrics(SM_CYICON); } if (!(uFlags & SHGFI_ATTRIBUTES)) { psfi->dwAttributes = SFGAO_LINK; // get link only psf->lpVtbl->GetAttributesOf(psf, 1, &pidlLast, &psfi->dwAttributes); } // // check for a overlay image thing (link overlay) // if ((psfi->dwAttributes & SFGAO_LINK) || (uFlags & SHGFI_LINKOVERLAY)) { flags |= INDEXTOOVERLAYMASK(II_LINK - II_OVERLAYFIRST + 1); } // // check for selected state // if (uFlags & SHGFI_SELECTED) { flags |= ILD_BLEND50; } psfi->hIcon = ImageList_GetIcon(himl, psfi->iIcon, flags); // // if the caller does not want a "shell size" icon // convert the icon to the "system" icon size. // if (psfi->hIcon && !(uFlags & SHGFI_SHELLICONSIZE)) { psfi->hIcon = CopyImage(psfi->hIcon, IMAGE_ICON, cx, cy, LR_COPYRETURNORG|LR_COPYDELETEORG); } } // // get display name for the path // if (uFlags & SHGFI_DISPLAYNAME) { STRRET str; psf->lpVtbl->GetDisplayNameOf(psf, pidlLast, SHGDN_NORMAL, &str); StrRetToStrN(psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName), &str, pidlLast); } if (psf) psf->lpVtbl->Release(psf); } exit: if (!(uFlags & SHGFI_PIDL)) ILFree(pidlFull); return res; } #define SHVTBL_REGITEM 1 // c_RegItemsSFVtbl #define SHVTBL_DRIVE 2 // c_DrivesSFVtbl #define SHVTBL_NET 3 // c_NetRootVtbl or c_NetResVtbl #define SHVTBL_FS 4 // c_FSFolderVtbl or c_FSBrfFolderVtbl #define SHVTBL_DESKTOP 5 // c_RootOfEvilSFVtbl extern IShellFolderVtbl c_RegItemsSFVtbl; extern IShellFolderVtbl c_DrivesSFVtbl; extern IShellFolderVtbl c_NetRootVtbl; extern IShellFolderVtbl c_NetResVtbl; extern IShellFolderVtbl c_RootOfEvilSFVtbl; HRESULT _GetPidlDescription(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, LPSHDESCRIPTIONID lpdid, UINT cbSize) { BYTE type; BYTE group; UINT iIndex; DWORD dwDescription; const UNALIGNED CLSID *pclsid; UINT iVtbl = 0; if (cbSize != SIZEOF(SHDESCRIPTIONID)) return E_INVALIDARG; lpdid->dwDescriptionId = 0; lpdid->clsid = CLSID_NULL; // // Now validate that the pidl and the psf match (so that later we can really // change this into a method on something like IShellFolder2). // type = SIL_GetType(pidl) & SHID_TYPEMASK; group = type & SHID_GROUPMASK; if (group == SHID_ROOT) { iVtbl = SHVTBL_REGITEM; dwDescription = SHDID_ROOT_REGITEM; } if (group == SHID_COMPUTER) { iVtbl = SHVTBL_DRIVE; switch( type ) { case SHID_COMPUTER_FIXED: dwDescription = SHDID_COMPUTER_FIXED; break; case SHID_COMPUTER_RAMDISK: dwDescription = SHDID_COMPUTER_RAMDISK; break; case SHID_COMPUTER_CDROM: dwDescription = SHDID_COMPUTER_CDROM; break; case SHID_COMPUTER_NETDRIVE: dwDescription = SHDID_COMPUTER_NETDRIVE; break; case SHID_COMPUTER_DRIVE525: dwDescription = SHDID_COMPUTER_DRIVE525; break; case SHID_COMPUTER_DRIVE35: dwDescription = SHDID_COMPUTER_DRIVE35; break; case SHID_COMPUTER_REMOVABLE: dwDescription = SHDID_COMPUTER_REMOVABLE; break; default: dwDescription = SHDID_COMPUTER_OTHER; break; } } if (group == SHID_FS) { iVtbl = SHVTBL_FS; // Turn off unicode and common bits type = (type & ~(SHID_FS_UNICODE | SHID_FS_COMMONITEM)) | SHID_FS; switch( type ) { case SHID_FS_FILE: dwDescription = SHDID_FS_FILE; break; case SHID_FS_DIRECTORY: dwDescription = SHDID_FS_DIRECTORY; break; default: dwDescription = SHDID_FS_OTHER; break; } } if (group == SHID_NET) { iVtbl = SHVTBL_NET; switch( type ) { case SHID_NET_DOMAIN: dwDescription = SHDID_NET_DOMAIN; break; case SHID_NET_SERVER: dwDescription = SHDID_NET_SERVER; break; case SHID_NET_SHARE: dwDescription = SHDID_NET_SHARE; break; case SHID_NET_RESTOFNET: dwDescription = SHDID_NET_RESTOFNET;break; default: dwDescription = SHDID_NET_OTHER; break; } } switch(iVtbl) { case SHVTBL_REGITEM: if (psf->lpVtbl == &c_RegItemsSFVtbl) break; return E_INVALIDARG; case SHVTBL_DRIVE: if (psf->lpVtbl == &c_DrivesSFVtbl) break; return E_INVALIDARG; case SHVTBL_NET: if (psf->lpVtbl == &c_NetRootVtbl || psf->lpVtbl == &c_NetResVtbl) break; return E_INVALIDARG; case SHVTBL_FS: if (psf->lpVtbl == &c_FSFolderVtbl || psf->lpVtbl == &c_FSBrfFolderVtbl) break; return E_INVALIDARG; default: return E_INVALIDARG; } lpdid->dwDescriptionId = dwDescription; pclsid = FS_GetCLSID((LPCIDFOLDER)pidl); if (pclsid) lpdid->clsid = *pclsid; return S_OK; } #ifdef UNICODE //=========================================================================== // // SHGetFileInfoA Stub // // This function calls SHGetFileInfoW and then converts the returned // information back to ANSI. // //=========================================================================== DWORD WINAPI SHGetFileInfoA(LPCSTR pszPath, DWORD dwFileAttributes, SHFILEINFOA *psfi, UINT cbFileInfo, UINT uFlags) { WCHAR szPathW[ MAX_PATH ]; LPWSTR pszPathW; DWORD dwRet; if (uFlags & SHGFI_PIDL) { pszPathW = (LPWSTR)pszPath; // Its a pidl, fake it as a WSTR } else { // Convert string only if its not a pidl MultiByteToWideChar( CP_ACP, 0, pszPath, -1, szPathW, MAX_PATH ); pszPathW = szPathW; } if (psfi) { SHFILEINFOW sfiw; dwRet = SHGetFileInfoW( pszPathW, dwFileAttributes, &sfiw, cbFileInfo, uFlags ); psfi->hIcon = sfiw.hIcon; psfi->iIcon = sfiw.iIcon; psfi->dwAttributes = sfiw.dwAttributes; WideCharToMultiByte( CP_ACP, 0, sfiw.szDisplayName, -1, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName), NULL, NULL ); WideCharToMultiByte( CP_ACP, 0, sfiw.szTypeName, -1, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName), NULL, NULL ); } else { dwRet = SHGetFileInfoW( pszPathW, dwFileAttributes, (SHFILEINFOW *)psfi, cbFileInfo, uFlags ); } return dwRet; } #else DWORD WINAPI SHGetFileInfoW(LPCWSTR pszPath, DWORD dwFileAttributes, SHFILEINFOW *psfi, UINT cbFileInfo, UINT uFlags) { return 0; // BUGBUG - BobDay - We should move this into SHUNIMP.C } #endif //=========================================================================== // // SHGetCachedInfoFromPidl // // This function will extract information that is cached in the pidl such // in the information that was returned from a FindFirst file. This function // is sortof a hack as t allow outside callers to be able to get at the infomation // without knowing how we store it in the pidl. // a app can get the following: //=========================================================================== #ifdef UNICODE HRESULT WINAPI SHGetDataFromIDListA(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, int nFormat, PVOID pv, int cb) { HRESULT hres = E_INVALIDARG; switch(nFormat) { case SHGDFIL_FINDDATA: { WIN32_FIND_DATAW wfd; WIN32_FIND_DATAA *pwfda = pv; hres = SHGetDataFromIDListW(psf, pidl, nFormat, &wfd, SIZEOF(wfd)); if (SUCCEEDED(hres) && cb >= SIZEOF(WIN32_FIND_DATAA) ) { CopyMemory(pwfda, &wfd, FIELD_OFFSET(WIN32_FIND_DATAA,cFileName)); WideCharToMultiByte(CP_ACP, 0, wfd.cFileName, -1, pwfda->cFileName, ARRAYSIZE(pwfda->cFileName), 0, 0); WideCharToMultiByte(CP_ACP, 0, wfd.cAlternateFileName, -1, pwfda->cAlternateFileName, ARRAYSIZE(pwfda->cAlternateFileName), 0, 0); } else hres = E_INVALIDARG; } break; case SHGDFIL_NETRESOURCE: { LPNETRESOURCEW pnrw; LPNETRESOURCEA pnra = pv; LPWSTR lpszSource[4]; LPSTR lpszDest[4] = {NULL, NULL, NULL, NULL}; LPSTR psza; UINT cchRemaining; UINT cchItem; UINT i; pnrw = (LPNETRESOURCEW)LocalAlloc(LPTR, cb*SIZEOF(TCHAR)); // Give us more than enough room if (!pnrw) hres = E_OUTOFMEMORY; else { hres = SHGetDataFromIDListW(psf, pidl, nFormat, pnrw, cb*SIZEOF(TCHAR)); if (SUCCEEDED(hres) && cb >= SIZEOF(NETRESOURCE)) { CopyMemory(pnra,pnrw,FIELD_OFFSET(NETRESOURCE,lpLocalName)); psza = (LPSTR)(pnra + 1); // Point just past the structure if (cb > SIZEOF(NETRESOURCE)) { cchRemaining = cb - SIZEOF(NETRESOURCE); lpszSource[0] = pnrw->lpLocalName; lpszSource[1] = pnrw->lpRemoteName; lpszSource[2] = pnrw->lpComment; lpszSource[3] = pnrw->lpProvider; for (i = 0; i < 4; i++) { if (lpszSource[i]) { lpszDest[i] = psza; cchItem = WideCharToMultiByte(CP_ACP, 0, lpszSource[i], -1, lpszDest[i], cchRemaining, 0, 0); cchRemaining -= cchItem; psza += cchItem; } } } pnra->lpLocalName = lpszDest[0]; pnra->lpRemoteName = lpszDest[1]; pnra->lpComment = lpszDest[2]; pnra->lpProvider = lpszDest[3]; } else hres = E_INVALIDARG; LocalFree(pnrw); } } break; case SHGDFIL_DESCRIPTIONID: // No string information, just pass on through hres = SHGetDataFromIDListW(psf, pidl, nFormat, pv, cb); break; } return hres; } #else HRESULT WINAPI SHGetDataFromIDListW(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, int nFormat, PVOID pv, int cb) { return E_NOTIMPL; } #endif HRESULT WINAPI SHGetDataFromIDList(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, int nFormat, PVOID pv, int cb) { if (!pv || !psf || (psf->lpVtbl == NULL) || !pidl) return E_INVALIDARG; switch (nFormat) { case SHGDFIL_FINDDATA: { LPCIDFOLDER pidf = (LPCIDFOLDER)pidl; WIN32_FIND_DATA *pfd = pv; // First lets Validate a couple of things before we continue here. // We could also test the VTABLE pointer if that appears necessary if (cb < SIZEOF(WIN32_FIND_DATA) || (psf->lpVtbl->EnumObjects != &CFSFolder_EnumObjects) || !FS_IsValidID(pidl) || !FS_IsRealID(pidf)) return E_INVALIDARG; _fmemset(pfd, 0, SIZEOF(*pfd)); // Note that COFSFolder doesn't provide any times _but_ COFSFolder DosDateTimeToFileTime(pidf->fs.dateModified, pidf->fs.timeModified, &pfd->ftLastWriteTime); pfd->dwFileAttributes = pidf->fs.wAttrs & ~FSTREEX_ATTRIBUTE_MASK; pfd->nFileSizeLow = pidf->fs.dwSize; Assert(pfd->nFileSizeHigh == 0); FS_CopyName(pidf,pfd->cFileName,ARRAYSIZE(pfd->cFileName)); FS_CopyAltName(pidf,pfd->cAlternateFileName,ARRAYSIZE(pfd->cAlternateFileName)); return ERROR_SUCCESS; } case SHGDFIL_NETRESOURCE: // Processing of this in netviewx.c return CNET_GetNetResourceForPidl(psf, pidl, pv, (UINT)cb); case SHGDFIL_DESCRIPTIONID: return _GetPidlDescription(psf, pidl, (LPSHDESCRIPTIONID)pv, (UINT)cb); } return E_INVALIDARG; } BOOL SHFS_IsRealID(LPCIDFOLDER pidf) { return FS_IsRealID(pidf); } //=========================================================================== // CFSFolder::IShellIcon : Members //=========================================================================== // // QueryInterface // HRESULT STDMETHODCALLTYPE CFSFolder_Icon_QueryInterface(IShellIcon *psi, REFIID riid, LPVOID * ppvObj) { IUnknown *this = (IUnknown*)IToClass(CFSFolder, si, psi); return this->lpVtbl->QueryInterface(this, riid, ppvObj); } // // AddRef // ULONG STDMETHODCALLTYPE CFSFolder_Icon_AddRef(IShellIcon *psi) { IUnknown *this = (IUnknown*)IToClass(CFSFolder, si, psi); return this->lpVtbl->AddRef(this); } // // Release // ULONG STDMETHODCALLTYPE CFSFolder_Icon_Release(IShellIcon *psi) { IUnknown *this = (IUnknown*)IToClass(CFSFolder, si, psi); return this->lpVtbl->Release(this); } // // GetIconOf // HRESULT STDMETHODCALLTYPE CFSFolder_Icon_GetIconOf(IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piIndex) { LPFSFOLDER this = IToClass(CFSFolder, si, psi); LPCIDFOLDER pidf = (LPCIDFOLDER)pidl; DWORD dwFlags; int iIcon; #ifdef CAIRO_DS TCHAR szPath[MAX_PATH]; LPCITEMIDLIST pidlAbs; #endif Assert(IS_FSIDL(pidl) || SIL_GetType(pidl) == SHID_ROOT_REGITEM); if (!IS_FSIDL(pidl)) return S_FALSE; // // special case for Folder. // // WARNING: don't replace this if-statement with FS_IsFolder(pidf))!!! // otherwise all junctions (like briefcase) will get the Folder icon. // #ifdef CAIRO_DS if ((FS_IsFileFolder(pidf)) && (!((this) && (this->fIsDSFolder)))) #else if (FS_IsFileFolder(pidf)) #endif //CAIRO_DS { if (flags & GIL_OPENICON) iIcon = II_FOLDEROPEN; #ifdef PROGMAN_ICON else if (CFSFolder_GetSpecialFID(this) == CSIDL_PROGRAMS) iIcon = II_STSPROGS; else if (CFSFolder_GetSpecialFID(this) == CSIDL_COMMON_PROGRAMS) iIcon = II_STCPROGS; #endif // PROGRAM_ICON else iIcon = II_FOLDER; *piIndex = Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0); return S_OK; } #ifdef CAIRO_DS if (this->fIsDSFolder) { pidlAbs = ILCombine(this->pidl, pidl); #if DBG ==1 SHGetPathFromIDList(pidlAbs, szPath ); #endif dwFlags = SHGetClassFlags((LPIDFOLDER)pidlAbs, TRUE); } else { #endif dwFlags = SHGetClassFlags((LPIDFOLDER)pidl, FALSE); #ifdef CAIRO_DS } #endif // // if the icon is per-instance, try to look it up // if (dwFlags & SHCF_ICON_PERINSTANCE) { // // get a unique identifier for this file. // DWORD uid = FS_GetUID(pidf); HRESULT hres; TCHAR szTmp[MAX_PATH]; LPSHELLFOLDER psf; if (uid == 0) return S_FALSE; // // look for entry in the icon cache. // FS_CopyName(pidf,szTmp,ARRAYSIZE(szTmp)); *piIndex = LookupIconIndex(szTmp, uid, flags | GIL_NOTFILENAME); if (*piIndex != -1) return S_OK; // // async extract (GIL_ASYNC) support // // we cant find the icon in the icon cache, we need to do real work // to get the icon. if the caller specified GIL_ASYNC // dont do the work, return E_PENDING forcing the caller to call // back later to get the real icon. // // when returing E_PENDING we must fill in a default icon index // if (flags & GIL_ASYNC) { // // come up with a default icon and return E_PENDING // if (!(dwFlags & SHCF_HAS_ICONHANDLER) && PathIsExe(szTmp)) iIcon = II_APPLICATION; else iIcon = II_DOCNOASSOC; *piIndex = Shell_GetCachedImageIndex(c_szShell32Dll, iIcon, 0); return E_PENDING; } // // look up icon using IExtractIcon, this will load handler iff needed // by calling ::GetUIObjectOf // if (SUCCEEDED(hres = psi->lpVtbl->QueryInterface(psi, &IID_IShellFolder, &psf))) { hres = SHGetIconFromPIDL(psf, NULL, (LPCITEMIDLIST)pidf, flags, piIndex); psf->lpVtbl->Release(psf); } // // remember this perinstance icon in the cache so we dont // need to load the handler again. // // SHGetIconFromPIDL will always return a valid image index // (it may default to a standard one) but it will fail // if the file cant be accessed or some other sort of error. // we dont want to cache in this case. // if (SUCCEEDED(hres) && (dwFlags & SHCF_HAS_ICONHANDLER)) { AddToIconTable(szTmp, uid, flags | GIL_NOTFILENAME, *piIndex); } if (*piIndex != -1) return S_OK; else return S_FALSE; } // // icon is per-class dwFlags has the image index // else { *piIndex = (dwFlags & SHCF_ICON_INDEX); return S_OK; } } //=========================================================================== // Briefcase Source Code Included //=========================================================================== #ifdef SYNC_BRIEFCASE // All IShellFolder, IShellDetails, IDataObject member function code // is in brfcase.c // #include "brfcase.c" #endif // SYNC_BRIEFCASE #ifdef CLOUDS //=========================================================================== // clouds IShellFolder...see clouds.cpp for more info // basically an FSFolder with a special view object //=========================================================================== // prototype for view creation in clouds.cpp STDMETHODIMP CloudFolder_CreateViewObject( LPSHELLFOLDER, HWND, REFIID, LPVOID * ); IShellFolderVtbl c_CloudFolderVtbl = { CDefShellFolder_QueryInterface, CFSFolder_AddRef, CFSFolder_Release, CFSFolder_ParseDisplayName, CFSFolder_EnumObjects, CFSFolder_BindToObject, CDefShellFolder_BindToStorage, CFSFolder_CompareIDs, CloudFolder_CreateViewObject, CFSFolder_GetAttributesOf, CFSFolder_GetUIObjectOf, CFSFolder_GetDisplayNameOf, CFSFolder_SetNameOf, }; STDMETHODIMP Clouds_CreateFromIDList(LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvOut) { HRESULT hres = E_OUTOFMEMORY; LPFSFOLDER pfsf = (void*)LocalAlloc(LPTR, SIZEOF(CFSFolder)); if (pfsf) { pfsf->sf.lpVtbl = &c_CloudFolderVtbl; pfsf->si.lpVtbl = &c_FSFolderIconVtbl; pfsf->cRef = 1; pfsf->pidl = ILClone(pidl); if (pfsf->pidl) hres = pfsf->sf.lpVtbl->QueryInterface(&pfsf->sf, riid, ppvOut); pfsf->sf.lpVtbl->Release(&pfsf->sf); } return hres; } #endif //CLOUDS //=========================================================================== // CFSFolder : IPersistFolder Members //=========================================================================== STDMETHODIMP CFSFolder_PF_QueryInterface(IPersistFolder *ppf, REFIID riid, LPVOID * ppvObj) { LPFSFOLDER this = IToClass(CFSFolder, pf, ppf); return this->sf.lpVtbl->QueryInterface(&this->sf, riid, ppvObj); } ULONG STDMETHODCALLTYPE CFSFolder_PF_AddRef(IPersistFolder *ppf) { LPFSFOLDER this = IToClass(CFSFolder, pf, ppf); return this->sf.lpVtbl->AddRef(&this->sf); } ULONG STDMETHODCALLTYPE CFSFolder_PF_Release(IPersistFolder *ppf) { LPFSFOLDER this = IToClass(CFSFolder, pf, ppf); return this->sf.lpVtbl->Release(&this->sf); } STDMETHODIMP CFSFolder_PF_GetClassID(LPPERSISTFOLDER fld, LPCLSID lpClassID) { *lpClassID = CLSID_ShellFSFolder; return NOERROR; } STDMETHODIMP CFSFolder_PF_Initialize(IPersistFolder *ppf, LPCITEMIDLIST pidl) { LPFSFOLDER this = IToClass(CFSFolder, pf, ppf); if (this->pidl) { ILFree(this->pidl); this->pidl = NULL; } this->pidl = ILClone(pidl); this->wSpecialFID = CSIDL_NOTCACHED; return this->pidl ? S_OK : E_OUTOFMEMORY; } // // If somebody call CoCreateInstance(CLSID_ShellFSFolder), we return this instance. // HRESULT CALLBACK CFSFolder_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, LPVOID * ppvOut) { extern const ITEMIDLIST c_idlDesktop; return CFSFolder_CreateFromIDList((LPITEMIDLIST)&c_idlDesktop,riid,ppvOut); }