#include "shellprv.h" #pragma hdrstop #include #include static DWORD s_dwThreadIDSuspendNotify = 0; STDAPI_(BOOL) SuspendSHNotify() { DWORD dwID = GetCurrentThreadId(); DWORD dwOldID = InterlockedCompareExchange(&s_dwThreadIDSuspendNotify, dwID, 0); return (dwOldID == 0); } STDAPI_(BOOL) ResumeSHNotify() { DWORD dwID = GetCurrentThreadId(); DWORD dwOldID = InterlockedCompareExchange(&s_dwThreadIDSuspendNotify, 0, dwID); return (dwOldID == dwID); } STDAPI_(BOOL) SHMoveFile(LPCTSTR pszExisting, LPCTSTR pszNew, LONG lEvent) { BOOL res; // CreateDirectory fails if the directory name being created does // not have room for an 8.3 name to be tagged onto the end of it, // i.e., lstrlen(new_directory_name)+12 must be less or equal to MAX_PATH. // However, NT does not impose this restriction on MoveFile -- which the // shell sometimes uses to manipulate directory names. So, in order to // maintain consistency, we now check the length of the name before we // move the directory... if (IsDirPathTooLongForCreateDir(pszNew) && (GetFileAttributes(pszExisting) & FILE_ATTRIBUTE_DIRECTORY)) { SetLastError(ERROR_FILENAME_EXCED_RANGE); res = FALSE; } else { res = MoveFile(pszExisting, pszNew); if (FALSE == res) { // If we couldn't move the file, see if it had the readonly or system attributes. // If so, clear them, move the file, and set them back on the destination DWORD dwAttributes = GetFileAttributes(pszExisting); if (-1 != dwAttributes && (dwAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) { if (SetFileAttributes(pszExisting, dwAttributes & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) { res = MoveFile(pszExisting, pszNew); if (res) { SetFileAttributes(pszNew, dwAttributes); } else { SetFileAttributes(pszExisting, dwAttributes); // if move failed, return attributes. } } } } } if (res && s_dwThreadIDSuspendNotify != GetCurrentThreadId()) { SHChangeNotify(lEvent, SHCNF_PATH, pszExisting, pszNew); } return res; } STDAPI_(BOOL) Win32MoveFile(LPCTSTR pszExisting, LPCTSTR pszNew, BOOL fDir) { return SHMoveFile(pszExisting, pszNew, fDir ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM); } STDAPI_(BOOL) Win32DeleteFilePidl(LPCTSTR pszFileName, LPCITEMIDLIST pidlFile) { BOOL res = DeleteFile(pszFileName); if (FALSE == res) { // If we couldn't delete the file, see if it has the readonly or // system bits set. If so, clear them and try again DWORD dwAttributes = GetFileAttributes(pszFileName); if (-1 != dwAttributes && (dwAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) { if (SetFileAttributes(pszFileName, dwAttributes & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) { res = DeleteFile(pszFileName); } } } if (res && s_dwThreadIDSuspendNotify != GetCurrentThreadId()) { if (pidlFile) { SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlFile, NULL); } else { SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, pszFileName, NULL); } } return res; } STDAPI_(BOOL) Win32DeleteFile(LPCTSTR pszFileName) { return Win32DeleteFilePidl(pszFileName, NULL); } STDAPI_(BOOL) Win32CreateDirectory(LPCTSTR pszPath, LPSECURITY_ATTRIBUTES lpsa) { BOOL res = CreateDirectory(pszPath, lpsa); if (res && s_dwThreadIDSuspendNotify != GetCurrentThreadId()) { SHChangeNotify(SHCNE_MKDIR, SHCNF_PATH, pszPath, NULL); } return res; } // // Some filesystems (like NTFS, perchance) actually pay attention to // the readonly bit on folders. So, in order to pretend we're sort of // FAT and dumb, we clear the attribute before trying to delete the // directory. // STDAPI_(BOOL) Win32RemoveDirectory(LPCTSTR pszDir) { BOOL res = RemoveDirectory(pszDir); if (FALSE == res) { DWORD dwAttr = GetFileAttributes(pszDir); if ((-1 != dwAttr) && (dwAttr & FILE_ATTRIBUTE_READONLY)) { dwAttr &= ~FILE_ATTRIBUTE_READONLY; SetFileAttributes(pszDir, dwAttr); res = RemoveDirectory(pszDir); } } if (res && s_dwThreadIDSuspendNotify != GetCurrentThreadId()) { SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, pszDir, NULL); } return res; } STDAPI_(HANDLE) Win32CreateFile(LPCTSTR pszFileName, DWORD dwAttrib) { HANDLE hFile = CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, dwAttrib & FILE_ATTRIBUTE_VALID_FLAGS, NULL); if ((INVALID_HANDLE_VALUE != hFile) && (s_dwThreadIDSuspendNotify != GetCurrentThreadId())) { SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, pszFileName, NULL); } return hFile; } STDAPI_(BOOL) CreateWriteCloseFile(HWND hwnd, LPCTSTR pszFile, void *pData, DWORD cbData) { BOOL bRet; HANDLE hfile = Win32CreateFile(pszFile, 0); if (hfile != INVALID_HANDLE_VALUE) { if (cbData) { DWORD dwBytesWritten; WriteFile(hfile, pData, cbData, &dwBytesWritten, 0); } CloseHandle(hfile); bRet = TRUE; } else { TCHAR szPath[MAX_PATH]; // ok to truncate (displayed in error message only) StringCchCopy(szPath, ARRAYSIZE(szPath), pszFile); PathRemoveExtension(szPath); SHSysErrorMessageBox(hwnd, NULL, IDS_CANNOTCREATEFILE, GetLastError(), PathFindFileName(szPath), MB_OK | MB_ICONEXCLAMATION); bRet = FALSE; } return bRet; } #undef SHGetProcessDword STDAPI_(DWORD) SHGetProcessDword(DWORD idProcess, LONG iIndex) { return 0; } STDAPI_(BOOL) SHSetShellWindowEx(HWND hwnd, HWND hwndChild) { return SetShellWindowEx(hwnd, hwndChild); } #define ISEXETSAWARE_MAX_IMAGESIZE (4 * 1024) // allocate at most a 4k block to hold the image header (eg 1 page on x86) // // this is a function that takes a full path to an executable and returns whether or not // the exe has the TS_AWARE bit set in the image header // STDAPI_(BOOL) IsExeTSAware(LPCTSTR pszExe) { BOOL bRet = FALSE; HANDLE hFile = CreateFile(pszExe, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD cbImageSize = GetFileSize(hFile, NULL); LPBYTE pBuffer; if (cbImageSize > ISEXETSAWARE_MAX_IMAGESIZE) { // 4k should be enough to get the image header for everything... cbImageSize = ISEXETSAWARE_MAX_IMAGESIZE; } pBuffer = LocalAlloc(LPTR, cbImageSize); if (pBuffer) { HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, cbImageSize, NULL); if (hMap) { // map the first 4k of the file in LPBYTE pFileMapping = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbImageSize); if (pFileMapping) { _try { memcpy(pBuffer, pFileMapping, cbImageSize); } _except(UnhandledExceptionFilter(GetExceptionInformation())) { // We hit an exception while copying! doh! LocalFree(pBuffer); pBuffer = NULL; } UnmapViewOfFile(pFileMapping); } else { LocalFree(pBuffer); pBuffer = NULL; } CloseHandle(hMap); } else { LocalFree(pBuffer); pBuffer = NULL; } if (pBuffer) { PIMAGE_NT_HEADERS pImageNTHeader; // NOTE: this should work ok for 64-bit images too, since both the IMAGE_NT_HEADERS and the IMAGE_NT_HEADERS64 // structs have a ->Signature and ->OptionalHeader that is identical up to the DllCharacteristics offset. pImageNTHeader = RtlImageNtHeader(pBuffer); if (pImageNTHeader) { if (pImageNTHeader->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE) { // yes, this is a TSAWARE executable! bRet = TRUE; } } LocalFree(pBuffer); } } CloseHandle(hFile); } return bRet; }