mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
9972 lines
299 KiB
9972 lines
299 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// 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 <brfcasep.h>
|
|
#endif
|
|
|
|
#ifdef CAIRO_DS
|
|
#include <iofs.h>
|
|
#include <dsys.h>
|
|
#include "dsdata.h"
|
|
#endif
|
|
|
|
#include <regstr.h>
|
|
|
|
#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 '<ext> 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; i<s_ExcludeFileExts.cExts; i++) {
|
|
if (s_ExcludeFileExts.ExtKeys[i] == *pSearchTarget) {
|
|
return(i);
|
|
}
|
|
}
|
|
return(-1);
|
|
}
|
|
#else
|
|
_ExtToEXTKEY(pszExt+1);
|
|
// Note that EAX now contains the DWORD to search for
|
|
// HACKHACK: EAX may be 0 for an invalid extension, but we just won't
|
|
// find it in our list, so no problem.
|
|
_asm mov ecx,s_ExcludeFileExts.cExts
|
|
_asm mov edi,offset s_ExcludeFileExts.ExtKeys
|
|
_asm repne scasd
|
|
// BUGBUG: JCXZ does not seem to jump to the right spot, so do the test in C
|
|
_asm sete fFound
|
|
_asm mov dwEDI,edi
|
|
|
|
// return the index of the extension if found, -1 otherwise
|
|
return(fFound ? ((DWORD *)dwEDI-s_ExcludeFileExts.ExtKeys)-1 : -1);
|
|
#endif
|
|
}
|
|
|
|
HRESULT FS_EnumObjects(LPFSFOLDER this, HWND hwndOwner, LPCITEMIDLIST pidlEnum, DWORD grfFlags, LPENUMIDLIST * ppenumUnknown)
|
|
{
|
|
HRESULT hres;
|
|
|
|
EnumFiles * pefile = (void*)LocalAlloc(LPTR, SIZEOF(EnumFiles));
|
|
if (pefile)
|
|
{
|
|
UINT err;
|
|
BOOL fIsNet;
|
|
int idErrorRes;
|
|
UINT dwFlags;
|
|
|
|
if (!SHGetPathFromIDList(pidlEnum, pefile->szFolder))
|
|
{
|
|
//
|
|
// 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; iidl<cidl && fFSOnly; iidl++)
|
|
{
|
|
UINT wType=SIL_GetType(apidl[iidl]);
|
|
switch (wType & SHID_GROUPMASK)
|
|
{
|
|
case SHID_FS:
|
|
case SHID_FS_UNICODE:
|
|
break;
|
|
|
|
case SHID_COMPUTER:
|
|
if (wType==SHID_COMPUTER_REGITEM) {
|
|
fFSOnly=FALSE;
|
|
}
|
|
break;
|
|
|
|
case SHID_ROOT:
|
|
fFSOnly=FALSE;
|
|
break;
|
|
|
|
default:
|
|
Assert(FALSE); // must not come here.
|
|
fFSOnly=FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fFSOnly;
|
|
}
|
|
|
|
//
|
|
// This function is used when there is a REAL FSFOLDER to back the pidls.
|
|
//
|
|
HRESULT _FS_CreateFSIDArrayFromFSFolder(IShellFolder *psfFS,
|
|
LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl,
|
|
LPDATAOBJECT pdtInner, LPDATAOBJECT *pdtobjOut)
|
|
{
|
|
return CIDLData_CreateFromIDArray4(&c_CFSIDLDataVtbl,
|
|
pidlFolder, cidl, apidl, psfFS,
|
|
pdtInner, pdtobjOut);
|
|
}
|
|
|
|
//
|
|
// This function is called from drivesx.c when it's GetUIObjectOf() member is
|
|
// called with riid==IID_IDataObject.
|
|
//
|
|
HRESULT FS_CreateFSIDArray(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST * apidl,
|
|
LPDATAOBJECT pdtInner, LPDATAOBJECT * pdtobjOut)
|
|
{
|
|
return _FS_CreateFSIDArrayFromFSFolder(NULL, pidlFolder, cidl, apidl,
|
|
pdtInner, pdtobjOut);
|
|
}
|
|
|
|
//
|
|
// BUGBUG: called from the file system property sheet page code
|
|
// to avoid a disk hit.
|
|
//
|
|
BOOL CFSFolder_FillFindData(HIDA hida, UINT iItem, LPTSTR pszPath, WIN32_FIND_DATA *pfd)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
LPITEMIDLIST pidl;
|
|
|
|
*pszPath = 0; // assume error
|
|
|
|
pidl = HIDA_ILClone(hida, iItem);
|
|
if (pidl)
|
|
{
|
|
if (SHGetPathFromIDList(pidl, pszPath))
|
|
{
|
|
if (pfd)
|
|
{
|
|
// Fill date/time, attr, size, long name and short name
|
|
LPCIDFOLDER pidfLast = FS_FindLastID(pidl);
|
|
_fmemset(pfd, 0, SIZEOF(*pfd)); // assuem error
|
|
|
|
// Note that COFSFolder doesn't provide any times _but_ LastWrite
|
|
|
|
DosDateTimeToFileTime(pidfLast->fs.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);
|
|
}
|