320 lines
9.9 KiB
C++
320 lines
9.9 KiB
C++
|
#include "precomp.hxx"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include <shguidp.h> // CLSID_MyDocuments, CLSID_ShellFSFolder
|
||
|
#include <shlobjp.h> // SHFlushSFCache()
|
||
|
#include "util.h"
|
||
|
#include "dll.h"
|
||
|
#include "resource.h"
|
||
|
#include "sddl.h"
|
||
|
|
||
|
HRESULT GetFolderDisplayName(UINT csidl, LPTSTR pszPath, UINT cch)
|
||
|
{
|
||
|
*pszPath = 0;
|
||
|
|
||
|
LPITEMIDLIST pidl;
|
||
|
if (SUCCEEDED(SHGetFolderLocation(NULL, csidl | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &pidl)))
|
||
|
{
|
||
|
SHGetNameAndFlags(pidl, SHGDN_NORMAL, pszPath, cch, NULL);
|
||
|
ILFree(pidl);
|
||
|
}
|
||
|
return *pszPath ? S_OK : E_FAIL;
|
||
|
}
|
||
|
|
||
|
#define MYDOCS_CLSID TEXT("{450d8fba-ad25-11d0-98a8-0800361b1103}") // CLSID_MyDocuments
|
||
|
|
||
|
// Create/Updates file in SendTo directory to have current display name
|
||
|
|
||
|
void UpdateSendToFile()
|
||
|
{
|
||
|
TCHAR szSendToDir[MAX_PATH];
|
||
|
|
||
|
if (S_OK == SHGetFolderPath(NULL, CSIDL_SENDTO, NULL, SHGFP_TYPE_CURRENT, szSendToDir))
|
||
|
{
|
||
|
// Create c:\winnt\profile\chrisg\sendto\<display name>.mydocs
|
||
|
BOOL bDeleteOnly = FALSE;
|
||
|
TCHAR szNewFile[MAX_PATH];
|
||
|
TCHAR szName[MAX_PATH];
|
||
|
if (SUCCEEDED(GetFolderDisplayName(CSIDL_PERSONAL, szName, ARRAYSIZE(szName))))
|
||
|
{
|
||
|
PathCleanupSpec(NULL, szName); // map any illegal chars to file sys chars
|
||
|
PathRemoveBlanks(szName);
|
||
|
|
||
|
PathCombine(szNewFile, szSendToDir, szName);
|
||
|
lstrcat(szNewFile, TEXT(".mydocs"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we can't create a new file, because we don't have a name
|
||
|
bDeleteOnly = TRUE;
|
||
|
}
|
||
|
|
||
|
TCHAR szFile[MAX_PATH];
|
||
|
WIN32_FIND_DATA fd;
|
||
|
|
||
|
// delete c:\winnt\profile\chrisg\sendto\*.mydocs
|
||
|
|
||
|
PathCombine(szFile, szSendToDir, TEXT("*.mydocs"));
|
||
|
|
||
|
HANDLE hFind = FindFirstFile(szFile, &fd);
|
||
|
if (hFind != INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
PathCombine(szFile, szSendToDir, fd.cFileName);
|
||
|
if (0 == lstrcmp(szFile, szNewFile))
|
||
|
{
|
||
|
// The file that we needed to create already exists,
|
||
|
// just leave it in place instead of deleting it and
|
||
|
// then creating it again below (this fixes
|
||
|
// app compat problems - see NT bug 246932)
|
||
|
bDeleteOnly = TRUE;
|
||
|
// file now has the exact display name, MUI adjusts the return from GetFolderDisplayName and
|
||
|
// since we run this every time we dont have to worry about localizing the sendto target.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DeleteFile(szFile);
|
||
|
}
|
||
|
} while (FindNextFile(hFind, &fd));
|
||
|
FindClose(hFind);
|
||
|
}
|
||
|
|
||
|
if (!bDeleteOnly)
|
||
|
{
|
||
|
hFind = CreateFile(szNewFile, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
|
if (hFind != INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
CloseHandle(hFind);
|
||
|
// file now has the exact display name, MUI adjusts the return from GetFolderDisplayName and
|
||
|
// since we run this every time we dont have to worry about localizing the sendto target.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// might be illegal chars in the file name, fall back to the default MyDocs name here
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// test pszChild against pszParent to see if
|
||
|
// pszChild is equal (PATH_IS_EQUAL) or
|
||
|
// a DIRECT child (PATH_IS_CHILD)
|
||
|
|
||
|
DWORD ComparePaths(LPCTSTR pszChild, LPCTSTR pszParent)
|
||
|
{
|
||
|
DWORD dwRet = PATH_IS_DIFFERENT;
|
||
|
|
||
|
TCHAR szParent[MAX_PATH];
|
||
|
StrCpyN(szParent, pszParent, ARRAYSIZE(szParent));
|
||
|
|
||
|
if (PathIsRoot(szParent) && (-1 != PathGetDriveNumber(szParent)))
|
||
|
{
|
||
|
szParent[2] = 0; // trip D:\ -> D: to make code below work
|
||
|
}
|
||
|
|
||
|
INT cchParent = lstrlen(szParent);
|
||
|
INT cchChild = lstrlen(pszChild);
|
||
|
|
||
|
if (cchParent <= cchChild)
|
||
|
{
|
||
|
TCHAR szChild[MAX_PATH];
|
||
|
lstrcpyn(szChild, pszChild, ARRAYSIZE(szChild));
|
||
|
|
||
|
LPTSTR pszChildSlice = szChild + cchParent;
|
||
|
if (TEXT('\\') == *pszChildSlice)
|
||
|
{
|
||
|
*pszChildSlice = 0;
|
||
|
}
|
||
|
|
||
|
if (lstrcmpi(szChild, szParent) == 0)
|
||
|
{
|
||
|
if (cchParent < cchChild)
|
||
|
{
|
||
|
LPTSTR pTmp = pszChildSlice + 1;
|
||
|
|
||
|
while (*pTmp && *pTmp != TEXT('\\'))
|
||
|
{
|
||
|
pTmp++; // find second level path segments
|
||
|
}
|
||
|
|
||
|
if (!(*pTmp))
|
||
|
{
|
||
|
dwRet = PATH_IS_CHILD; // direct child
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dwRet = PATH_IS_EQUAL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return dwRet;
|
||
|
}
|
||
|
|
||
|
// Checks the path to see if it is marked as system or read only and
|
||
|
// then check desktop.ini for CLSID or CLSID2 entry...
|
||
|
|
||
|
BOOL IsPathAlreadyShellFolder(LPCTSTR pszPath, DWORD dwAttrib)
|
||
|
{
|
||
|
BOOL bIsShellFolder = FALSE;
|
||
|
|
||
|
if (PathIsSystemFolder(pszPath, dwAttrib))
|
||
|
{
|
||
|
TCHAR szDesktopIni[MAX_PATH];
|
||
|
PathCombine(szDesktopIni, pszPath, TEXT("desktop.ini"));
|
||
|
|
||
|
// Check for CLSID entry...
|
||
|
TCHAR szBuffer[MAX_PATH];
|
||
|
GetPrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID"), TEXT("foo"), szBuffer, ARRAYSIZE(szBuffer), szDesktopIni);
|
||
|
|
||
|
if ((lstrcmpi(szBuffer, TEXT("foo")) !=0) &&
|
||
|
(lstrcmpi(szBuffer, MYDOCS_CLSID) !=0))
|
||
|
{
|
||
|
bIsShellFolder = TRUE;
|
||
|
}
|
||
|
|
||
|
// Check for CLSID2 entry...
|
||
|
GetPrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID2"), TEXT("foo"), szBuffer, ARRAYSIZE(szBuffer), szDesktopIni);
|
||
|
|
||
|
if ((lstrcmpi(szBuffer, TEXT("foo")) != 0) &&
|
||
|
(lstrcmpi(szBuffer, MYDOCS_CLSID) != 0))
|
||
|
{
|
||
|
bIsShellFolder = TRUE;
|
||
|
}
|
||
|
}
|
||
|
return bIsShellFolder;
|
||
|
}
|
||
|
|
||
|
const struct
|
||
|
{
|
||
|
DWORD dwDir;
|
||
|
DWORD dwFlags;
|
||
|
DWORD dwRet;
|
||
|
}
|
||
|
_adirs[] =
|
||
|
{
|
||
|
{ CSIDL_DESKTOP, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_DESKTOP },
|
||
|
{ CSIDL_PERSONAL, PATH_IS_EQUAL , PATH_IS_MYDOCS },
|
||
|
{ CSIDL_SENDTO, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_SENDTO },
|
||
|
{ CSIDL_RECENT, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_RECENT },
|
||
|
{ CSIDL_HISTORY, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_HISTORY },
|
||
|
{ CSIDL_COOKIES, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_COOKIES },
|
||
|
{ CSIDL_PRINTHOOD, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_PRINTHOOD },
|
||
|
{ CSIDL_NETHOOD, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_NETHOOD },
|
||
|
{ CSIDL_STARTMENU, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_STARTMENU },
|
||
|
{ CSIDL_TEMPLATES, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_TEMPLATES },
|
||
|
{ CSIDL_FAVORITES, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_FAVORITES },
|
||
|
{ CSIDL_FONTS, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_FONTS },
|
||
|
{ CSIDL_APPDATA, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_APPDATA },
|
||
|
{ CSIDL_INTERNET_CACHE, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_TEMP_INET },
|
||
|
{ CSIDL_COMMON_STARTMENU, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_STARTMENU },
|
||
|
{ CSIDL_COMMON_DESKTOPDIRECTORY, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_DESKTOP },
|
||
|
{ CSIDL_WINDOWS, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_WINDOWS },
|
||
|
{ CSIDL_SYSTEM, PATH_IS_EQUAL | PATH_IS_CHILD, PATH_IS_SYSTEM },
|
||
|
{ CSIDL_PROFILE, PATH_IS_EQUAL , PATH_IS_PROFILE },
|
||
|
};
|
||
|
|
||
|
BOOL PathEndsInDot(LPCTSTR pszPath)
|
||
|
{
|
||
|
// CreateDirectory("c:\foo.") or CreateDirectory("c:\foo.....")
|
||
|
// will succeed but create a directory named "c:\foo", which isn't
|
||
|
// what the user asked for. So we use this function to guard
|
||
|
// against those cases.
|
||
|
//
|
||
|
// Note that this simple test also picks off "c:\foo\." -- ok for
|
||
|
// our purposes.
|
||
|
|
||
|
UINT cLen = lstrlen(pszPath);
|
||
|
return (cLen >= 1) && (pszPath[cLen - 1] == TEXT('.'));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Checks the path to see if it is okay as a MyDocs path
|
||
|
//
|
||
|
DWORD IsPathGoodMyDocsPath(HWND hwnd, LPCTSTR pszPath)
|
||
|
{
|
||
|
if (NULL == pszPath)
|
||
|
{
|
||
|
return PATH_IS_ERROR;
|
||
|
}
|
||
|
|
||
|
TCHAR szRootPath[MAX_PATH];
|
||
|
lstrcpyn(szRootPath, pszPath, ARRAYSIZE(szRootPath));
|
||
|
if (!PathStripToRoot(szRootPath))
|
||
|
{
|
||
|
return PATH_IS_ERROR;
|
||
|
}
|
||
|
|
||
|
if (PathEndsInDot(pszPath))
|
||
|
{
|
||
|
return PATH_IS_ERROR;
|
||
|
}
|
||
|
|
||
|
DWORD dwRes, dwAttr = GetFileAttributes(pszPath);
|
||
|
if (dwAttr == 0xFFFFFFFF)
|
||
|
{
|
||
|
if (0xFFFFFFFF == GetFileAttributes(szRootPath))
|
||
|
{
|
||
|
// If the root path doesn't exist, then we're not going
|
||
|
// to be able to create a path:
|
||
|
return PATH_IS_ERROR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return PATH_IS_NONEXISTENT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
|
||
|
{
|
||
|
return PATH_IS_NONDIR;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < ARRAYSIZE(_adirs); i++)
|
||
|
{
|
||
|
TCHAR szPathToCheck[MAX_PATH];
|
||
|
//
|
||
|
// Check for various special shell folders
|
||
|
//
|
||
|
if (S_OK == SHGetFolderPath(hwnd, _adirs[i].dwDir | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPathToCheck))
|
||
|
{
|
||
|
dwRes = ComparePaths(pszPath, szPathToCheck);
|
||
|
|
||
|
if (dwRes & _adirs[i].dwFlags)
|
||
|
{
|
||
|
//
|
||
|
// The inevitable exceptions
|
||
|
//
|
||
|
switch (_adirs[i].dwDir)
|
||
|
{
|
||
|
case CSIDL_DESKTOP:
|
||
|
if (PATH_IS_CHILD == dwRes)
|
||
|
{
|
||
|
continue; // allowing subfolder of CSIDL_DESKTOP
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
} // switch
|
||
|
|
||
|
return _adirs[i].dwRet;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure path isn't set as a system or some other kind of
|
||
|
// folder that already has a CLSID or CLSID2 entry...
|
||
|
//
|
||
|
if (IsPathAlreadyShellFolder(pszPath, dwAttr))
|
||
|
{
|
||
|
return PATH_IS_SHELLFOLDER;
|
||
|
}
|
||
|
|
||
|
return PATH_IS_GOOD;
|
||
|
}
|
||
|
|