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.
2067 lines
57 KiB
2067 lines
57 KiB
//*************************************************************
|
|
// File name: PROQUOTA.C
|
|
//
|
|
// Description: Profile quota management
|
|
//
|
|
// Microsoft Confidential
|
|
// Copyright (c) Microsoft Corporation 1996
|
|
// All rights reserved
|
|
//
|
|
//*************************************************************
|
|
|
|
#include <windows.h>
|
|
#include <wchar.h>
|
|
#include <aclapi.h>
|
|
#include <shellapi.h>
|
|
#include <commctrl.h>
|
|
#include <objbase.h>
|
|
#include <strsafe.h>
|
|
#include "proquota.h"
|
|
#include "debug.h"
|
|
|
|
#define WINLOGON_KEY TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon")
|
|
#define SYSTEM_POLICIES_KEY TEXT("Software\\Policies\\Microsoft\\Windows\\System")
|
|
|
|
#define MAX_MESSAGE_LENGTH 256
|
|
|
|
HINSTANCE hInst;
|
|
HWND hwndMain;
|
|
HWND g_hQuotaDlg = NULL;
|
|
BOOL g_bHideSmallItems;
|
|
BOOL g_bShowReg = FALSE;
|
|
HANDLE g_hThread;
|
|
HANDLE g_hExitEvent;
|
|
HANDLE g_hQuotaDlgEvent;
|
|
DWORD g_dwProfileSize = 0;
|
|
DWORD g_dwProfileSizeTemp = 0;
|
|
DWORD g_dwMaxProfileSize = 10240; //KB
|
|
CRITICAL_SECTION g_cs;
|
|
HICON hIconGood, hIconCaution, hIconStop;
|
|
BOOL g_bQueryEndSession;
|
|
TCHAR g_szExcludeList[2*MAX_PATH + 2]; // "User exclusion list;Policy exclusion list"
|
|
TCHAR* g_lpQuotaMessage = NULL;
|
|
DWORD g_cbQuotaMessage = 0;
|
|
|
|
TCHAR szClassName[] = TEXT("proquota");
|
|
TCHAR szEventName[] = TEXT("proquota instance event");
|
|
TCHAR szSizeFormat[40];
|
|
|
|
BOOL g_bWarnUser = FALSE;
|
|
DWORD g_dwWarnUserTimeout = 15; // minutes
|
|
BOOL g_bWarningTimerRunning = FALSE;
|
|
BOOL g_bWarningDisplayed = FALSE;
|
|
|
|
|
|
//
|
|
// Function prototypes
|
|
//
|
|
|
|
LRESULT CALLBACK ProQuotaWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
LRESULT CALLBACK QuotaDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
|
BOOL SetSecurity (void);
|
|
BOOL ReadRegistry (void);
|
|
BOOL ReadExclusionList();
|
|
VOID QuotaThread(HWND hWnd);
|
|
BOOL RecurseDirectory (LPTSTR lpDir, UINT cchBuffer, LPTSTR lpTop, HWND hLV, LPTSTR lpExcludeList);
|
|
BOOL EnumerateProfile (HWND hLV);
|
|
BOOL CheckSemicolon (LPTSTR lpDir, UINT cchBuffer);
|
|
LPTSTR CheckSlash (LPTSTR lpDir, UINT cchBuffer, UINT* pcchRemaining);
|
|
LPTSTR ConvertExclusionList (LPCTSTR lpSourceDir, LPCTSTR lpExclusionList);
|
|
BOOL GetDisplayName(LPCTSTR lpDir, LPTSTR lpTop, LPTSTR lpDisplayName, DWORD cchDisplayName);
|
|
|
|
//*************************************************************
|
|
//
|
|
// WinMain()
|
|
//
|
|
// Purpose: Entry point
|
|
//
|
|
// Parameters: hInstance - Instance handle
|
|
// hPrevInstance - Previous Instance
|
|
// lpCmdLine - Command line
|
|
// nCmdShow - ShowWindow flag
|
|
//
|
|
// Return: int
|
|
//
|
|
//*************************************************************
|
|
|
|
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|
LPSTR lpCmdLine, INT nCmdShow)
|
|
{
|
|
MSG msg;
|
|
WNDCLASS wc;
|
|
HANDLE hEvent = NULL;
|
|
int iRet = 0;
|
|
|
|
|
|
//
|
|
// Verbose output
|
|
//
|
|
|
|
#if DBG
|
|
InitDebugSupport();
|
|
DebugMsg((DM_VERBOSE, TEXT("WinMain: Entering...")));
|
|
#endif
|
|
|
|
hInst = hInstance;
|
|
|
|
|
|
//
|
|
// Check if this app is already running
|
|
//
|
|
|
|
hEvent = OpenEvent (EVENT_ALL_ACCESS, FALSE, szEventName);
|
|
|
|
if (hEvent) {
|
|
DebugMsg((DM_VERBOSE, TEXT("WinMain: Proquota already running. Exiting...")));
|
|
goto Exit;
|
|
}
|
|
|
|
hEvent = CreateEvent (NULL, TRUE, TRUE, szEventName);
|
|
|
|
|
|
g_hQuotaDlgEvent = CreateEvent (NULL, FALSE, TRUE, NULL);
|
|
|
|
if (!g_hQuotaDlgEvent) {
|
|
DebugMsg((DM_VERBOSE, TEXT("WinMain: Proquota Couldn't get prowquota dlg event, error %d..."), GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Get the quota settings
|
|
//
|
|
|
|
if (!ReadRegistry()) {
|
|
DebugMsg((DM_VERBOSE, TEXT("WinMain: ReadRegistry returned FALSE. Exiting...")));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Munge the access mask on the process token so taskmgr
|
|
// can't kill this app.
|
|
//
|
|
|
|
SetSecurity();
|
|
|
|
//
|
|
// Make sure proquota is the first one that is attempted to be shutdown
|
|
//
|
|
|
|
SetProcessShutdownParameters(0x3ff, 0);
|
|
|
|
//
|
|
// Initialize
|
|
//
|
|
|
|
__try {
|
|
if(!InitializeCriticalSectionAndSpinCount(&g_cs, 0x80000000)) {
|
|
DebugMsg((DM_WARNING, TEXT("WinMain: InitializeCriticalSectionAndSpinCount failed with %d"), GetLastError()));
|
|
goto Exit;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DebugMsg((DM_WARNING, TEXT("WinMain: InitializeCriticalSection failed")));
|
|
goto Exit;
|
|
}
|
|
|
|
InitCommonControls();
|
|
CoInitialize(NULL);
|
|
|
|
LoadString (hInst, IDS_SIZEFMT, szSizeFormat, ARRAYSIZE(szSizeFormat));
|
|
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = ProQuotaWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = szClassName;
|
|
|
|
if (!RegisterClass(&wc)) {
|
|
DebugMsg((DM_WARNING, TEXT("WinMain: RegisterClass failed with %d"), GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Create a hidden top level window so we get
|
|
// broadcasted messages.
|
|
//
|
|
|
|
hwndMain = CreateWindow(szClassName, NULL, WS_OVERLAPPED, 0, 0, 0, 0,
|
|
NULL, NULL, hInstance, NULL);
|
|
|
|
if (!hwndMain) {
|
|
DebugMsg((DM_WARNING, TEXT("WinMain: CreateWindow failed with %d"), GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
while (GetMessage (&msg, NULL, 0, 0)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
iRet = (int)(msg.wParam);
|
|
|
|
Exit:
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("WinMain: Leaving...")));
|
|
|
|
if (hEvent) {
|
|
CloseHandle (hEvent);
|
|
}
|
|
|
|
if (g_hQuotaDlgEvent) {
|
|
CloseHandle(g_hQuotaDlgEvent);
|
|
}
|
|
|
|
if (g_lpQuotaMessage) {
|
|
LocalFree(g_lpQuotaMessage);
|
|
g_lpQuotaMessage = NULL;
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// ProQuotaWndProc()
|
|
//
|
|
// Purpose: Window procedure
|
|
//
|
|
// Parameters: hWnd - Window handle
|
|
// message - Window message
|
|
// wParam - WPARAM
|
|
// lParam - LPARAM
|
|
//
|
|
//
|
|
// Return: LRESULT
|
|
//
|
|
//*************************************************************
|
|
|
|
LRESULT CALLBACK ProQuotaWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DWORD dwThreadId;
|
|
|
|
switch (message) {
|
|
case WM_CREATE:
|
|
|
|
hIconGood = LoadIcon (hInst, MAKEINTRESOURCE(IDI_ICON));
|
|
hIconCaution = LoadIcon (hInst, MAKEINTRESOURCE(IDI_CAUTION));
|
|
hIconStop = LoadIcon (hInst, MAKEINTRESOURCE(IDI_STOP));
|
|
|
|
g_hExitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
|
|
|
|
if (!g_hExitEvent) {
|
|
DebugMsg((DM_WARNING, TEXT("ProQuotaWndProc: Failed to create exit event with error %d"), GetLastError()));
|
|
return -1;
|
|
}
|
|
|
|
g_hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) QuotaThread,
|
|
(LPVOID) hWnd, CREATE_SUSPENDED, &dwThreadId);
|
|
|
|
if (!g_hThread) {
|
|
DebugMsg((DM_WARNING, TEXT("ProQuotaWndProc: Failed to create thread with error %d"), GetLastError()));
|
|
CloseHandle (g_hExitEvent);
|
|
return -1;
|
|
}
|
|
|
|
SetThreadPriority (g_hThread, THREAD_PRIORITY_IDLE);
|
|
ResumeThread (g_hThread);
|
|
break;
|
|
|
|
case WM_USER:
|
|
|
|
if (lParam == WM_LBUTTONDBLCLK) {
|
|
PostMessage (hWnd, WM_QUOTADLG, 0, 0);
|
|
}
|
|
|
|
#if DBG
|
|
if (lParam == WM_RBUTTONUP) {
|
|
DestroyWindow (hWnd);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case WM_QUERYENDSESSION:
|
|
{
|
|
BOOL bLogoff;
|
|
|
|
//EnterCriticalSection (&g_cs);
|
|
bLogoff = (g_dwProfileSize <= g_dwMaxProfileSize);
|
|
//LeaveCriticalSection (&g_cs);
|
|
|
|
//
|
|
// If it is zero assume that it has not yet finished enumerating..
|
|
//
|
|
|
|
if (g_dwProfileSize == 0) {
|
|
bLogoff = FALSE;
|
|
DebugMsg((DM_VERBOSE, TEXT("ProQuotaWndProc: Recd QueryEnd Message before enumerating.")));
|
|
}
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ProQuotaWndProc: Recd QueryEnd Message. Returning %s"), bLogoff?TEXT("TRUE"):TEXT("FALSE")));
|
|
|
|
if (bLogoff) {
|
|
return TRUE;
|
|
}
|
|
|
|
PostMessage (hWnd, WM_QUOTADLG, 1, 0);
|
|
}
|
|
return FALSE;
|
|
|
|
|
|
case WM_QUOTADLG:
|
|
if (!g_hQuotaDlg) {
|
|
|
|
if (wParam) {
|
|
g_bQueryEndSession = TRUE;
|
|
} else {
|
|
g_bQueryEndSession = FALSE;
|
|
}
|
|
|
|
DialogBox (hInst, MAKEINTRESOURCE(IDD_QUOTA), hwndMain, QuotaDlgProc);
|
|
g_hQuotaDlg = NULL;
|
|
}
|
|
break;
|
|
|
|
case WM_WARNUSER:
|
|
if (!g_bWarningDisplayed) {
|
|
TCHAR szTitle[100];
|
|
|
|
g_bWarningDisplayed = TRUE;
|
|
|
|
LoadString (hInst, IDS_MSGTITLE, szTitle, ARRAYSIZE(szTitle));
|
|
MessageBox(hWnd, g_lpQuotaMessage, szTitle, MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL);
|
|
|
|
g_bWarningDisplayed = FALSE;
|
|
}
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
if (g_dwWarnUserTimeout > 0) {
|
|
PostMessage (hWnd, WM_WARNUSER, 0, 0);
|
|
}
|
|
break;
|
|
|
|
case WM_EXITWINDOWS:
|
|
ExitWindowsDialog(NULL);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
NOTIFYICONDATA nid;
|
|
|
|
nid.cbSize = sizeof(nid);
|
|
nid.hWnd = hWnd;
|
|
nid.uID = 1;
|
|
|
|
Shell_NotifyIcon (NIM_DELETE, &nid);
|
|
|
|
SetEvent (g_hExitEvent);
|
|
|
|
WaitForSingleObject (g_hThread, INFINITE);
|
|
|
|
CloseHandle (g_hExitEvent);
|
|
CloseHandle (g_hThread);
|
|
PostQuitMessage(0);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return (DefWindowProc(hWnd, message, wParam, lParam));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// QuotaThread()
|
|
//
|
|
// Purpose: Initializes the tray icon
|
|
//
|
|
// Parameters: hWnd - main window handle
|
|
//
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
VOID QuotaThread (HWND hWnd)
|
|
{
|
|
NOTIFYICONDATA nid;
|
|
TCHAR szProfile[MAX_PATH];
|
|
TCHAR szMessage[MAX_MESSAGE_LENGTH];
|
|
HANDLE hFileChange = INVALID_HANDLE_VALUE;
|
|
HANDLE hRegChange = NULL;
|
|
HANDLE hWaitHandles[4];
|
|
BOOL bFirst = TRUE;
|
|
HICON hOk = NULL, hWarning = NULL, hBad = NULL;
|
|
DWORD dwDelta;
|
|
HKEY hKeySystem = NULL;
|
|
LONG lResult;
|
|
DWORD dwResult;
|
|
HRESULT hr;
|
|
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: Entering...")));
|
|
|
|
//
|
|
// Load the status icons
|
|
//
|
|
|
|
hOk = LoadImage (hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON,
|
|
16, 16, LR_DEFAULTCOLOR);
|
|
if (!hOk) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to load OK icon. Error %d"), GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
hWarning = LoadImage (hInst, MAKEINTRESOURCE(IDI_CAUTION), IMAGE_ICON,
|
|
16, 16, LR_DEFAULTCOLOR);
|
|
if (!hWarning) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to load Warning icon. Error %d"), GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
hBad = LoadImage (hInst, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON,
|
|
16, 16, LR_DEFAULTCOLOR);
|
|
if (!hBad) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to load stop icon. Error %d"), GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the profile directory
|
|
//
|
|
|
|
szProfile[0] = TEXT('\0');
|
|
GetEnvironmentVariable (TEXT("USERPROFILE"), szProfile, ARRAYSIZE(szProfile));
|
|
|
|
if (szProfile[0] == TEXT('\0')) {
|
|
ExitThread (0);
|
|
}
|
|
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: User's profile: <%s>"), szProfile));
|
|
|
|
|
|
//
|
|
// Setup change notify
|
|
//
|
|
|
|
hFileChange = FindFirstChangeNotification (szProfile, TRUE,
|
|
FILE_NOTIFY_CHANGE_FILE_NAME |
|
|
FILE_NOTIFY_CHANGE_DIR_NAME |
|
|
FILE_NOTIFY_CHANGE_SIZE);
|
|
|
|
if (hFileChange == INVALID_HANDLE_VALUE) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to setup file change notification. %d"),
|
|
GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
lResult = RegOpenKeyEx (HKEY_CURRENT_USER,
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
|
|
0, KEY_READ, &hKeySystem);
|
|
|
|
if (lResult != ERROR_SUCCESS) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to open registry key. %d"), lResult));
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
hRegChange = CreateEvent (NULL, FALSE, FALSE, NULL);
|
|
|
|
if (!hRegChange) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to setup reg event for change notification. %d"),
|
|
GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
lResult = RegNotifyChangeKeyValue(hKeySystem, FALSE, REG_NOTIFY_CHANGE_LAST_SET,
|
|
hRegChange, TRUE);
|
|
|
|
if (lResult != ERROR_SUCCESS) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: Failed to setup RegNotifyChangeKeyValue. %d"),
|
|
lResult));
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
hWaitHandles[0] = g_hExitEvent;
|
|
hWaitHandles[1] = hFileChange;
|
|
hWaitHandles[2] = hRegChange;
|
|
hWaitHandles[3] = g_hQuotaDlgEvent;
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Calculate the profile size
|
|
//
|
|
|
|
if (g_hQuotaDlg) {
|
|
DebugMsg((DM_VERBOSE, TEXT("QuotaTHread: Enumerating profile and refreshing dialog")));
|
|
if (!EnumerateProfile (GetDlgItem (g_hQuotaDlg, IDC_QUOTA_FILELIST))) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: EnumerateProfile failed with Dlg Item.")));
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (!EnumerateProfile (NULL)) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: EnumerateProfile failed.")));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Update the status icon
|
|
//
|
|
|
|
nid.cbSize = sizeof(nid);
|
|
nid.hWnd = hWnd;
|
|
nid.uID = 1;
|
|
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
|
nid.uCallbackMessage = WM_USER;
|
|
szMessage[0] = TEXT('\0');
|
|
|
|
if (g_dwProfileSize > g_dwMaxProfileSize) {
|
|
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: User has exceeded their profile quota.")));
|
|
nid.hIcon = hBad;
|
|
LoadString (hInst, IDS_SIZEBAD, szMessage, ARRAYSIZE(szMessage));
|
|
dwDelta = g_dwProfileSize - g_dwMaxProfileSize;
|
|
|
|
if (g_bWarnUser && !g_bWarningTimerRunning) {
|
|
g_bWarningTimerRunning = TRUE;
|
|
SetTimer (hwndMain, 1, g_dwWarnUserTimeout * 60000, NULL);
|
|
PostMessage (hwndMain, WM_WARNUSER, 0, 0);
|
|
}
|
|
|
|
} else if ( (g_dwMaxProfileSize - g_dwProfileSize) < (g_dwProfileSize * .10)) {
|
|
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: User is within 10% of their profile quota.")));
|
|
nid.hIcon = hWarning;
|
|
LoadString (hInst, IDS_SIZEWARN, szMessage, ARRAYSIZE(szMessage));
|
|
dwDelta = g_dwMaxProfileSize - g_dwProfileSize;
|
|
|
|
if (g_bWarnUser && g_bWarningTimerRunning) {
|
|
KillTimer (hwndMain, 1);
|
|
g_bWarningTimerRunning = FALSE;
|
|
}
|
|
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: User has space available in their profile quota.")));
|
|
nid.hIcon = hOk;
|
|
LoadString (hInst, IDS_SIZEOK, szMessage, ARRAYSIZE(szMessage));
|
|
dwDelta = g_dwMaxProfileSize - g_dwProfileSize;
|
|
|
|
if (g_bWarnUser && g_bWarningTimerRunning) {
|
|
KillTimer (hwndMain, 1);
|
|
g_bWarningTimerRunning = FALSE;
|
|
}
|
|
}
|
|
|
|
hr = StringCchPrintf(nid.szTip, ARRAYSIZE(nid.szTip), szMessage, dwDelta);
|
|
if (FAILED(hr)) {
|
|
DebugMsg((DM_WARNING, TEXT("QuotaThread: Insufficient buffer for message. Error 0x%x"), hr));
|
|
continue;
|
|
}
|
|
|
|
if (bFirst) {
|
|
if (Shell_NotifyIcon (NIM_ADD, &nid)) {
|
|
bFirst = FALSE;
|
|
}
|
|
} else {
|
|
Shell_NotifyIcon (NIM_MODIFY, &nid);
|
|
}
|
|
|
|
|
|
//
|
|
// Notify the dialog if it's present
|
|
//
|
|
|
|
if (g_hQuotaDlg) {
|
|
PostMessage (g_hQuotaDlg, WM_REFRESH, 0, 0);
|
|
}
|
|
|
|
|
|
//
|
|
// Clean up and wait for the next change
|
|
//
|
|
|
|
FindNextChangeNotification (hFileChange);
|
|
|
|
|
|
dwResult = WaitForMultipleObjects (4, hWaitHandles, FALSE, INFINITE);
|
|
|
|
|
|
if (dwResult == WAIT_FAILED) {
|
|
break;
|
|
}
|
|
|
|
switch (dwResult - WAIT_OBJECT_0) {
|
|
|
|
case 0:
|
|
goto Exit;
|
|
break;
|
|
|
|
case 2:
|
|
|
|
EnterCriticalSection (&g_cs);
|
|
|
|
if (!ReadRegistry()) {
|
|
PostMessage (hwndMain, WM_DESTROY, 0, 0);
|
|
goto Exit;
|
|
}
|
|
|
|
LeaveCriticalSection (&g_cs);
|
|
|
|
RegNotifyChangeKeyValue(hKeySystem, FALSE,
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
hRegChange, TRUE);
|
|
// fall through
|
|
|
|
case 1:
|
|
Sleep (2000);
|
|
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: Running background enumeration.")));
|
|
break;
|
|
|
|
case 3:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
Exit:
|
|
|
|
if (hOk) {
|
|
DestroyIcon(hOk);
|
|
}
|
|
|
|
if (hWarning) {
|
|
DestroyIcon(hWarning);
|
|
}
|
|
|
|
if (hBad) {
|
|
DestroyIcon(hBad);
|
|
}
|
|
|
|
if (hKeySystem) {
|
|
RegCloseKey (hKeySystem);
|
|
}
|
|
|
|
if (hRegChange) {
|
|
CloseHandle (hRegChange);
|
|
}
|
|
|
|
if (INVALID_HANDLE_VALUE != hFileChange) {
|
|
FindCloseChangeNotification (hFileChange);
|
|
}
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("QuotaThread: Leaving...")));
|
|
ExitThread (0);
|
|
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// SetSecurity()
|
|
//
|
|
// Purpose: Removes TERMINATE_PROCESS access to this process
|
|
// so taskman can't blow us away.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL SetSecurity (void)
|
|
{
|
|
HANDLE hProcess;
|
|
PACL pDACL;
|
|
PSECURITY_DESCRIPTOR pSD;
|
|
WORD wIndex;
|
|
ACE_HEADER * lpAceHeader;
|
|
ACCESS_ALLOWED_ACE * lpAce;
|
|
DWORD dwResult;
|
|
|
|
hProcess = GetCurrentProcess();
|
|
|
|
if (GetSecurityInfo (hProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
|
|
NULL, NULL, &pDACL, NULL, &pSD) != ERROR_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (wIndex = 0; wIndex < pDACL->AceCount; wIndex++) {
|
|
|
|
if (GetAce(pDACL, wIndex, &lpAceHeader)) {
|
|
|
|
if (lpAceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE) {
|
|
lpAce = (ACCESS_ALLOWED_ACE *) lpAceHeader;
|
|
|
|
lpAce->Mask &= ~(PROCESS_TERMINATE | WRITE_DAC | WRITE_OWNER);
|
|
}
|
|
}
|
|
}
|
|
|
|
dwResult = SetSecurityInfo (hProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
|
|
NULL, NULL, pDACL, NULL);
|
|
|
|
LocalFree (pSD);
|
|
|
|
if (dwResult != ERROR_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// ReadExclusionList()
|
|
//
|
|
// Purpose: Checks if the profile quota policy is set,
|
|
// and if so gets the max profile size.
|
|
//
|
|
// Parameters: void
|
|
//
|
|
// Return: TRUE if profile quota is enabled
|
|
// FALSE if not
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL ReadExclusionList()
|
|
{
|
|
TCHAR szExcludeList2[MAX_PATH+1];
|
|
TCHAR szExcludeList1[MAX_PATH+1];
|
|
HKEY hKey;
|
|
DWORD dwSize, dwType;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Check for a list of directories to exclude both user preferences
|
|
// and user policy
|
|
//
|
|
|
|
szExcludeList1[0] = TEXT('\0');
|
|
if (RegOpenKeyEx (HKEY_CURRENT_USER,
|
|
TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
|
|
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
|
|
|
dwSize = sizeof(szExcludeList1);
|
|
if (RegQueryValueEx (hKey,
|
|
TEXT("ExcludeProfileDirs"),
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) szExcludeList1,
|
|
&dwSize) != ERROR_SUCCESS) {
|
|
|
|
// ignore user exclusion list
|
|
szExcludeList1[0] = TEXT('\0');
|
|
}
|
|
|
|
RegCloseKey (hKey);
|
|
}
|
|
|
|
szExcludeList2[0] = TEXT('\0');
|
|
if (RegOpenKeyEx (HKEY_CURRENT_USER,
|
|
SYSTEM_POLICIES_KEY,
|
|
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
|
|
|
dwSize = sizeof(szExcludeList2);
|
|
if (RegQueryValueEx (hKey,
|
|
TEXT("ExcludeProfileDirs"),
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) szExcludeList2,
|
|
&dwSize) != ERROR_SUCCESS) {
|
|
|
|
// ignore policy exclusion list
|
|
szExcludeList2[0] = TEXT('\0');
|
|
}
|
|
|
|
RegCloseKey (hKey);
|
|
}
|
|
|
|
|
|
//
|
|
// Merge the user preferences and policy together
|
|
//
|
|
|
|
g_szExcludeList[0] = TEXT('\0');
|
|
|
|
if (szExcludeList1[0] != TEXT('\0')) {
|
|
hr = StringCchCopy(g_szExcludeList, ARRAYSIZE(g_szExcludeList), szExcludeList1);
|
|
if (FAILED(hr)) {
|
|
DebugMsg((DM_WARNING, TEXT("ReadExclusionList: Fail to copy user exclusion list. Error 0x%x"), hr));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!CheckSemicolon(g_szExcludeList, ARRAYSIZE(g_szExcludeList))) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (szExcludeList2[0] != TEXT('\0')) {
|
|
hr = StringCchCat(g_szExcludeList, ARRAYSIZE(g_szExcludeList), szExcludeList2);
|
|
if (FAILED(hr)) {
|
|
DebugMsg((DM_WARNING, TEXT("ReadExclusionList: Fail to copy policy exclusion list. Error 0x%x"), hr));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// ReadQuotaMsg()
|
|
//
|
|
// Purpose: Reads the msg that needs to be displayed.
|
|
//
|
|
// Parameters: hKey - Handle to the open policy
|
|
//
|
|
// Return: TRUE if mesg could be read
|
|
// FALSE otherwise
|
|
//
|
|
//*************************************************************
|
|
BOOL ReadQuotaMsg(HKEY hKey)
|
|
{
|
|
DWORD dwType, dwSize, dwValue, dwErr;
|
|
BOOL bLoadDefault = FALSE;
|
|
|
|
if (!g_lpQuotaMessage) {
|
|
g_cbQuotaMessage = MAX_PATH * sizeof(TCHAR);
|
|
g_lpQuotaMessage = LocalAlloc (LPTR, g_cbQuotaMessage);
|
|
if (!g_lpQuotaMessage) {
|
|
g_cbQuotaMessage = 0;
|
|
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to allocate memory for msg with %d."), GetLastError()));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Query the size for quota message
|
|
//
|
|
|
|
dwErr = RegQueryValueEx (hKey, TEXT("ProfileQuotaMessage"), NULL,
|
|
&dwType, NULL, &dwSize);
|
|
|
|
if (dwErr != ERROR_SUCCESS) {
|
|
//
|
|
// Load the default message otherwise
|
|
//
|
|
|
|
bLoadDefault = TRUE;
|
|
goto Load_Default;
|
|
}
|
|
|
|
// Ensure proper size
|
|
if (dwSize > g_cbQuotaMessage) {
|
|
if (g_lpQuotaMessage) {
|
|
LocalFree(g_lpQuotaMessage);
|
|
}
|
|
|
|
g_cbQuotaMessage = dwSize;
|
|
g_lpQuotaMessage = LocalAlloc(LPTR, g_cbQuotaMessage);
|
|
if (!g_lpQuotaMessage) {
|
|
g_cbQuotaMessage = 0;
|
|
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to allocate memory for msg with %d."), GetLastError()));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
dwSize = g_cbQuotaMessage;
|
|
dwErr = RegQueryValueEx (hKey, TEXT("ProfileQuotaMessage"), NULL,
|
|
&dwType, (LPBYTE) g_lpQuotaMessage, &dwSize);
|
|
|
|
if (dwErr != ERROR_SUCCESS) {
|
|
//
|
|
// Load the default message otherwise
|
|
//
|
|
|
|
bLoadDefault = TRUE;
|
|
goto Load_Default;
|
|
}
|
|
|
|
//
|
|
// if there is any message expand the environment variables in it.
|
|
//
|
|
|
|
if (*g_lpQuotaMessage) {
|
|
DWORD cchSize;
|
|
LPTSTR lpTemp;
|
|
|
|
//
|
|
// Get the size of expanded string buffer including NULL terminator
|
|
//
|
|
|
|
cchSize = ExpandEnvironmentStrings (g_lpQuotaMessage, NULL, 0);
|
|
if (cchSize == 0) {
|
|
//
|
|
// Load the default message otherwise
|
|
//
|
|
|
|
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to expand env var"), GetLastError()));
|
|
bLoadDefault = TRUE;
|
|
goto Load_Default;
|
|
}
|
|
|
|
lpTemp = LocalAlloc (LPTR, cchSize * sizeof(TCHAR));
|
|
|
|
if (lpTemp) {
|
|
cchSize = ExpandEnvironmentStrings (g_lpQuotaMessage, lpTemp, cchSize);
|
|
|
|
if (cchSize == 0) {
|
|
//
|
|
// Load the default message otherwise
|
|
//
|
|
|
|
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to expand env var"), GetLastError()));
|
|
bLoadDefault = TRUE;
|
|
goto Load_Default;
|
|
}
|
|
|
|
if (cchSize * sizeof(TCHAR) > g_cbQuotaMessage) {
|
|
LocalFree(g_lpQuotaMessage);
|
|
g_lpQuotaMessage = lpTemp;
|
|
g_cbQuotaMessage = cchSize * sizeof(TCHAR);
|
|
}
|
|
else {
|
|
if (FAILED(StringCchCopy(g_lpQuotaMessage, g_cbQuotaMessage/sizeof(TCHAR), lpTemp))) {
|
|
bLoadDefault = TRUE;
|
|
}
|
|
|
|
LocalFree(lpTemp);
|
|
}
|
|
}
|
|
else {
|
|
DebugMsg((DM_WARNING, TEXT("ReadQuotaMsg: Failed to allocate memory for tmp buffer with %d.Not expanding env var"), GetLastError()));
|
|
}
|
|
}
|
|
|
|
Load_Default:
|
|
|
|
if (bLoadDefault) {
|
|
//
|
|
// Load the default message in case of error
|
|
//
|
|
|
|
LoadString (hInst, IDS_DEFAULTMSG, g_lpQuotaMessage, g_cbQuotaMessage/sizeof(TCHAR));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// ReadRegistry()
|
|
//
|
|
// Purpose: Checks if the profile quota policy is set,
|
|
// and if so gets the max profile size.
|
|
//
|
|
// Parameters: void
|
|
//
|
|
// Return: TRUE if profile quota is enabled
|
|
// FALSE if not
|
|
//
|
|
//*************************************************************
|
|
BOOL ReadRegistry (void)
|
|
{
|
|
LONG lResult;
|
|
HKEY hKey;
|
|
DWORD dwType, dwSize, dwValue, dwErr;
|
|
|
|
lResult = RegOpenKeyEx (HKEY_CURRENT_USER,
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
|
|
0, KEY_READ, &hKey);
|
|
|
|
if (lResult == ERROR_SUCCESS) {
|
|
|
|
dwSize = sizeof(dwValue);
|
|
lResult = RegQueryValueEx (hKey, TEXT("EnableProfileQuota"), NULL,
|
|
&dwType, (LPBYTE) &dwValue, &dwSize);
|
|
|
|
if (lResult == ERROR_SUCCESS) {
|
|
|
|
if (dwValue) {
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Profile quotas are enabled.")));
|
|
|
|
dwSize = sizeof(g_dwMaxProfileSize);
|
|
RegQueryValueEx (hKey, TEXT("MaxProfileSize"), NULL,
|
|
&dwType, (LPBYTE) &g_dwMaxProfileSize, &dwSize);
|
|
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Max Profile Size: %d"), g_dwMaxProfileSize));
|
|
|
|
|
|
dwSize = sizeof(g_bShowReg);
|
|
RegQueryValueEx (hKey, TEXT("IncludeRegInProQuota"), NULL,
|
|
&dwType, (LPBYTE) &g_bShowReg, &dwSize);
|
|
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Show registry in file list: %s"),
|
|
g_bShowReg ? TEXT("TRUE") : TEXT("FALSE")));
|
|
|
|
|
|
dwSize = sizeof(g_bWarnUser);
|
|
RegQueryValueEx (hKey, TEXT("WarnUser"), NULL,
|
|
&dwType, (LPBYTE) &g_bWarnUser, &dwSize);
|
|
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Warn user when quota exceeded: %s"),
|
|
g_bWarnUser ? TEXT("TRUE") : TEXT("FALSE")));
|
|
|
|
|
|
if (g_bWarnUser) {
|
|
|
|
dwSize = sizeof(g_dwWarnUserTimeout);
|
|
if (RegQueryValueEx (hKey, TEXT("WarnUserTimeout"), NULL,
|
|
&dwType, (LPBYTE) &g_dwWarnUserTimeout, &dwSize) == ERROR_SUCCESS) {
|
|
|
|
if (g_dwWarnUserTimeout > 1440) {
|
|
g_dwWarnUserTimeout = 1440;
|
|
}
|
|
|
|
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: User warning reminder timeout: %d"), g_dwWarnUserTimeout));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now read the message that needs to be displayed
|
|
//
|
|
|
|
if (!ReadQuotaMsg(hKey)) {
|
|
RegCloseKey (hKey);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (ReadExclusionList()) {
|
|
RegCloseKey (hKey);
|
|
return TRUE;
|
|
}
|
|
else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Failed to read the ExclusionList")));
|
|
}
|
|
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Profile quotas are DISABLED.")));
|
|
}
|
|
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Failed to query EnableProfileQuota with error %d."), lResult));
|
|
}
|
|
|
|
RegCloseKey (hKey);
|
|
|
|
} else {
|
|
DebugMsg((DM_VERBOSE, TEXT("ReadRegistry: Failed to open System policy key with error %d."), lResult));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// CheckSlash()
|
|
//
|
|
// Purpose: Checks for an ending slash and adds one if
|
|
// it is missing.
|
|
//
|
|
// Parameters: lpDir - directory
|
|
// cchBuffer - Buffer size in char
|
|
// pcchRemaining - buffer remaining after adding '\',
|
|
// can be NULL if not required
|
|
//
|
|
// Return: Pointer to the end of the string
|
|
//
|
|
// Comments:
|
|
//
|
|
// History: Date Author Comment
|
|
// 6/19/95 ericflo Created
|
|
//
|
|
//*************************************************************
|
|
LPTSTR CheckSlash (LPTSTR lpDir, UINT cchBuffer, UINT* pcchRemaining)
|
|
{
|
|
UINT cchDir = lstrlen(lpDir);
|
|
LPTSTR lpEnd;
|
|
|
|
lpEnd = lpDir + cchDir;
|
|
if (pcchRemaining) {
|
|
*pcchRemaining = cchBuffer - cchDir - 1;
|
|
}
|
|
|
|
if (*(lpEnd - 1) != TEXT('\\')) {
|
|
if (cchDir + 1 >= cchBuffer) { // No space to put \, should never happen
|
|
return NULL;
|
|
}
|
|
*lpEnd = TEXT('\\');
|
|
lpEnd++;
|
|
*lpEnd = TEXT('\0');
|
|
if (pcchRemaining)
|
|
*pcchRemaining -= 1;
|
|
}
|
|
|
|
return lpEnd;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// CheckSemicolon()
|
|
//
|
|
// Purpose: Checks for an ending slash and adds one if
|
|
// it is missing.
|
|
//
|
|
// Parameters: lpDir - directory
|
|
// cchSize - buffer size in character
|
|
//
|
|
// Return: TRUE - success
|
|
// FALSE - Insufficient buffer space
|
|
//
|
|
// Comments:
|
|
//
|
|
// History: Date Author Comment
|
|
// 6/19/95 ericlfo Created
|
|
//
|
|
//*************************************************************
|
|
BOOL CheckSemicolon (LPTSTR lpDir, UINT cchBuffer)
|
|
{
|
|
UINT cchDir = lstrlen(lpDir);
|
|
LPTSTR lpEnd;
|
|
|
|
lpEnd = lpDir + cchDir;
|
|
|
|
if (*(lpEnd - 1) != TEXT(';')) {
|
|
if (cchDir + 1 >= cchBuffer) {
|
|
return FALSE; // No space to put ;, should never happen
|
|
}
|
|
*lpEnd = TEXT(';');
|
|
lpEnd++;
|
|
*lpEnd = TEXT('\0');
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// RecurseDirectory()
|
|
//
|
|
// Purpose: Recurses through the subdirectories counting the size.
|
|
//
|
|
// Parameters: lpDir - Directory
|
|
// cchBuffer - Buffer size in char
|
|
// lpTop - Top of the display name
|
|
// hLV - Listview window handle (optional)
|
|
// lpExcludeList - Null-termed list of dirs to be skipped (optional)
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
// Comments:
|
|
//
|
|
// History: Date Author Comment
|
|
// 1/30/96 ericflo Created
|
|
// 12/22/98 ushaji Added exclusionlist support
|
|
// Notes:
|
|
// The buffer size expected is MAX_PATH+4 for some internal processing
|
|
// We should fix this to be better post Win 2K.
|
|
//*************************************************************
|
|
|
|
BOOL RecurseDirectory (LPTSTR lpDir, UINT cchBuffer, LPTSTR lpTop, HWND hLV, LPTSTR lpExcludeList)
|
|
{
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
WIN32_FIND_DATA fd;
|
|
LPTSTR lpEnd, lpTemp;
|
|
BOOL bResult = TRUE;
|
|
BOOL bSkip;
|
|
UINT cchRemaining;
|
|
HRESULT hr;
|
|
|
|
|
|
//
|
|
// Setup the ending pointer
|
|
//
|
|
|
|
lpEnd = CheckSlash (lpDir, cchBuffer, &cchRemaining);
|
|
if (!lpEnd) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Append *.* to the source directory
|
|
//
|
|
|
|
hr = StringCchCopy(lpEnd, cchRemaining, TEXT("*.*"));
|
|
if (FAILED(hr)) {
|
|
bResult = FALSE;
|
|
goto RecurseDir_Exit;
|
|
}
|
|
|
|
//
|
|
// Search through the source directory
|
|
//
|
|
|
|
hFile = FindFirstFile(lpDir, &fd);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
|
|
if ( (GetLastError() == ERROR_FILE_NOT_FOUND) ||
|
|
(GetLastError() == ERROR_PATH_NOT_FOUND) ) {
|
|
|
|
//
|
|
// bResult is already initialized to TRUE, so
|
|
// just fall through.
|
|
//
|
|
|
|
} else {
|
|
|
|
DebugMsg((DM_WARNING, TEXT("RecurseDirectory: FindFirstFile for <%s> failed with %d."),
|
|
lpDir, GetLastError()));
|
|
bResult = FALSE;
|
|
}
|
|
|
|
goto RecurseDir_Exit;
|
|
}
|
|
|
|
|
|
do {
|
|
|
|
//
|
|
// Append the file / directory name to the working buffer
|
|
//
|
|
|
|
// skip the file if the path > MAX_PATH
|
|
|
|
if ((UINT)(1+lstrlen(fd.cFileName)+lstrlen(lpDir)+lstrlen(TEXT("\\*.*"))) >= cchBuffer) {
|
|
continue;
|
|
}
|
|
|
|
hr = StringCchCopy(lpEnd, cchRemaining, fd.cFileName);
|
|
if (FAILED(hr)) {
|
|
bResult = FALSE;
|
|
goto RecurseDir_Exit;
|
|
}
|
|
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
|
|
//
|
|
// Check for "." and ".."
|
|
//
|
|
|
|
if (!lstrcmpi(fd.cFileName, TEXT("."))) {
|
|
continue;
|
|
}
|
|
|
|
if (!lstrcmpi(fd.cFileName, TEXT(".."))) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check for reparse point
|
|
//
|
|
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
|
DebugMsg((DM_WARNING, TEXT("RecurseDirectory: Found a reparse point <%s>, skip it!"), lpDir));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if this directory should be excluded
|
|
//
|
|
|
|
if (lpExcludeList) {
|
|
|
|
bSkip = FALSE;
|
|
lpTemp = lpExcludeList;
|
|
|
|
while (*lpTemp) {
|
|
|
|
if (lstrcmpi (lpTemp, lpDir) == 0) {
|
|
bSkip = TRUE;
|
|
break;
|
|
}
|
|
|
|
lpTemp += lstrlen (lpTemp) + 1;
|
|
}
|
|
|
|
if (bSkip) {
|
|
DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> due to exclusion list."),
|
|
lpDir));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Found a directory.
|
|
//
|
|
// 1) Change into that subdirectory on the source drive.
|
|
// 2) Recurse down that tree.
|
|
// 3) Back up one level.
|
|
//
|
|
|
|
//
|
|
// Recurse the subdirectory
|
|
//
|
|
|
|
if (!RecurseDirectory(lpDir, cchBuffer, lpTop, hLV, lpExcludeList))
|
|
{
|
|
// Ignore error and continue
|
|
DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> due to error."), lpDir));
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Found a file, add the filesize and put in the listview
|
|
// if appropriate.
|
|
//
|
|
|
|
g_dwProfileSizeTemp += fd.nFileSizeLow;
|
|
DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Profile Size <%d> after <%s> "), g_dwProfileSizeTemp,
|
|
fd.cFileName));
|
|
|
|
if (hLV) {
|
|
LV_ITEM lvi;
|
|
BOOL bAddItem = TRUE;
|
|
|
|
if ((lstrlen(fd.cFileName) >= 6) &&
|
|
(CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE,
|
|
TEXT("ntuser"), 6,
|
|
fd.cFileName, 6) == 2)) {
|
|
bAddItem = (g_bShowReg ? TRUE : FALSE);
|
|
}
|
|
|
|
if (bAddItem && g_bHideSmallItems && (fd.nFileSizeLow <= 2048)) {
|
|
bAddItem = FALSE;
|
|
}
|
|
|
|
if (bAddItem) {
|
|
TCHAR szSize[40];
|
|
TCHAR szDisplayName[MAX_PATH*3];
|
|
DWORD dwFileSize;
|
|
INT iItem;
|
|
|
|
if (fd.nFileSizeLow <= 1024) {
|
|
dwFileSize = 1;
|
|
} else {
|
|
dwFileSize = fd.nFileSizeLow / 1024;
|
|
}
|
|
|
|
hr = StringCchPrintf(szSize, ARRAYSIZE(szSize), szSizeFormat, dwFileSize);
|
|
if (FAILED(hr)) {
|
|
bResult = FALSE;
|
|
goto RecurseDir_Exit;
|
|
}
|
|
|
|
lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
|
|
lvi.iItem = 0;
|
|
lvi.iSubItem = 0;
|
|
lvi.state = 0;
|
|
lvi.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
|
|
lvi.pszText = GetDisplayName(lpDir, lpTop, szDisplayName, ARRAYSIZE(szDisplayName)) ? szDisplayName : lpTop;
|
|
lvi.lParam = fd.nFileSizeLow;
|
|
|
|
iItem = ListView_InsertItem (hLV, &lvi);
|
|
|
|
lvi.mask = LVIF_TEXT | LVIF_STATE;
|
|
lvi.iItem = iItem;
|
|
lvi.iSubItem = 1;
|
|
lvi.state = 0;
|
|
lvi.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
|
|
lvi.pszText = szSize;
|
|
lvi.lParam = fd.nFileSizeLow;
|
|
|
|
ListView_SetItem (hLV, &lvi);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Find the next entry
|
|
//
|
|
|
|
} while (FindNextFile(hFile, &fd));
|
|
|
|
|
|
RecurseDir_Exit:
|
|
|
|
//
|
|
// Remove the file / directory name appended above
|
|
//
|
|
|
|
*lpEnd = TEXT('\0');
|
|
|
|
|
|
//
|
|
// Close the search handle
|
|
//
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
FindClose(hFile);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// CenterWindow()
|
|
//
|
|
// Purpose: Centers a window on the screen
|
|
//
|
|
// Parameters: hwnd - window handle to center
|
|
//
|
|
// Return: void
|
|
//
|
|
// Comments:
|
|
//
|
|
// History: Date Author Comment
|
|
// 2/21/96 ericflo Ported
|
|
//
|
|
//*************************************************************
|
|
|
|
void CenterWindow (HWND hwnd)
|
|
{
|
|
RECT rect;
|
|
LONG dx, dy;
|
|
LONG dxParent, dyParent;
|
|
LONG Style;
|
|
|
|
//
|
|
// Get window rect
|
|
//
|
|
|
|
GetWindowRect(hwnd, &rect);
|
|
|
|
dx = rect.right - rect.left;
|
|
dy = rect.bottom - rect.top;
|
|
|
|
|
|
//
|
|
// Get parent rect
|
|
//
|
|
|
|
Style = GetWindowLong(hwnd, GWL_STYLE);
|
|
if ((Style & WS_CHILD) == 0) {
|
|
|
|
//
|
|
// Return the desktop windows size (size of main screen)
|
|
//
|
|
|
|
dxParent = GetSystemMetrics(SM_CXSCREEN);
|
|
dyParent = GetSystemMetrics(SM_CYSCREEN);
|
|
} else {
|
|
HWND hwndParent;
|
|
RECT rectParent;
|
|
|
|
hwndParent = GetParent(hwnd);
|
|
if (hwndParent == NULL) {
|
|
hwndParent = GetDesktopWindow();
|
|
}
|
|
|
|
GetWindowRect(hwndParent, &rectParent);
|
|
|
|
dxParent = rectParent.right - rectParent.left;
|
|
dyParent = rectParent.bottom - rectParent.top;
|
|
}
|
|
|
|
//
|
|
// Center the child in the parent
|
|
//
|
|
|
|
rect.left = (dxParent - dx) / 2;
|
|
rect.top = (dyParent - dy) / 3;
|
|
|
|
|
|
//
|
|
// Move the child into position
|
|
//
|
|
|
|
SetWindowPos(hwnd, HWND_TOP, rect.left, rect.top, 0, 0, SWP_NOSIZE);
|
|
}
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// QuotaDlgProc()
|
|
//
|
|
// Purpose: Quota dialog box
|
|
//
|
|
// Parameters: hDlg - Window handle
|
|
// message - Window message
|
|
// wParam - WPARAM
|
|
// lParam - LPARAM
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
LRESULT CALLBACK QuotaDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
TCHAR szBuffer[40];
|
|
TCHAR szSize[40];
|
|
HWND hLV;
|
|
LV_COLUMN col;
|
|
RECT rect;
|
|
INT cx;
|
|
HKEY hKey;
|
|
DWORD dwSize, dwType;
|
|
LPTSTR lpMessage;
|
|
HRESULT hr;
|
|
|
|
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
|
|
hLV = GetDlgItem (hDlg, IDC_QUOTA_FILELIST);
|
|
|
|
|
|
//
|
|
// Add the columns to the listview
|
|
//
|
|
|
|
GetClientRect (hLV, &rect);
|
|
cx = (rect.right * 31) / 40;
|
|
|
|
LoadString (hInst, IDS_COLUMN1, szBuffer, ARRAYSIZE(szBuffer));
|
|
|
|
col.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
|
|
col.fmt = LVCFMT_LEFT;
|
|
col.cx = cx;
|
|
col.pszText = szBuffer;
|
|
col.iSubItem = 0;
|
|
ListView_InsertColumn (hLV, 0, &col);
|
|
|
|
LoadString (hInst, IDS_COLUMN2, szBuffer, ARRAYSIZE(szBuffer));
|
|
|
|
col.cx = rect.right - cx - GetSystemMetrics(SM_CYHSCROLL);
|
|
col.fmt = LVCFMT_RIGHT;
|
|
col.iSubItem = 1;
|
|
ListView_InsertColumn (hLV, 1, &col);
|
|
|
|
|
|
//
|
|
// Hide small items by default
|
|
//
|
|
|
|
g_bHideSmallItems = TRUE;
|
|
CheckDlgButton (hDlg, IDC_QUOTA_HIDESMALL, BST_CHECKED);
|
|
|
|
|
|
CenterWindow (hDlg);
|
|
SetForegroundWindow (hDlg);
|
|
|
|
|
|
// EnumerateProfile (GetDlgItem (hDlg, IDC_QUOTA_FILELIST));
|
|
|
|
|
|
dwSize = 500 * sizeof(TCHAR);
|
|
lpMessage = LocalAlloc (LPTR, dwSize);
|
|
if (!lpMessage)
|
|
break;
|
|
|
|
LoadString (hInst ,IDS_QUOTAENUMMSG, lpMessage, 500);
|
|
|
|
SetDlgItemText (hDlg, IDC_QUOTA_TEXT, lpMessage);
|
|
|
|
|
|
if (g_dwProfileSize > g_dwMaxProfileSize) {
|
|
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconStop, 0);
|
|
|
|
} else if ( (g_dwMaxProfileSize - g_dwProfileSize) < (g_dwProfileSize * .10)) {
|
|
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconCaution, 0);
|
|
|
|
} else {
|
|
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconGood, 0);
|
|
}
|
|
|
|
//
|
|
// Setting the global value at the end QuotaThread is not trying
|
|
// to refresh the dialog etc. at the same time.
|
|
//
|
|
|
|
g_hQuotaDlg = hDlg;
|
|
|
|
SetEvent(g_hQuotaDlgEvent);
|
|
|
|
LocalFree (lpMessage);
|
|
|
|
break;
|
|
|
|
case WM_REFRESH:
|
|
|
|
//
|
|
// Popuplate the listview
|
|
//
|
|
|
|
|
|
//
|
|
// Set the size information
|
|
//
|
|
|
|
hr = StringCchPrintf(szSize, ARRAYSIZE(szSize), szSizeFormat, g_dwProfileSize);
|
|
if (FAILED(hr))
|
|
break;
|
|
SetDlgItemText (hDlg, IDC_QUOTA_SIZE, szSize);
|
|
|
|
hr = StringCchPrintf(szSize, ARRAYSIZE(szSize), szSizeFormat, g_dwMaxProfileSize);
|
|
if (FAILED(hr))
|
|
break;
|
|
SetDlgItemText (hDlg, IDC_QUOTA_MAXSIZE, szSize);
|
|
|
|
|
|
dwSize = 500 * sizeof(TCHAR);
|
|
lpMessage = LocalAlloc (LPTR, dwSize);
|
|
|
|
if (!lpMessage) {
|
|
break;
|
|
}
|
|
|
|
if (g_dwProfileSize > g_dwMaxProfileSize) {
|
|
|
|
//
|
|
// This messge is already read
|
|
//
|
|
|
|
SetDlgItemText (hDlg, IDC_QUOTA_TEXT, g_lpQuotaMessage);
|
|
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconStop, 0);
|
|
|
|
} else if ( (g_dwMaxProfileSize - g_dwProfileSize) < (g_dwProfileSize * .10)) {
|
|
|
|
LoadString (hInst, IDS_CAUTION, lpMessage, 500);
|
|
SetDlgItemText (hDlg, IDC_QUOTA_TEXT, lpMessage);
|
|
|
|
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconCaution, 0);
|
|
|
|
} else {
|
|
|
|
LoadString (hInst, IDS_LOGOFFOK, lpMessage, 500);
|
|
SetDlgItemText (hDlg, IDC_QUOTA_TEXT, lpMessage);
|
|
|
|
SendDlgItemMessage (hDlg, IDC_QUOTA_ICON, STM_SETICON, (WPARAM) hIconGood, 0);
|
|
}
|
|
|
|
LocalFree (lpMessage);
|
|
break;
|
|
|
|
|
|
case WM_COMMAND:
|
|
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
|
g_hQuotaDlg = NULL;
|
|
if ((g_dwProfileSize < g_dwMaxProfileSize) && (g_bQueryEndSession) && (g_dwProfileSize != 0)) {
|
|
PostMessage (hwndMain, WM_EXITWINDOWS, 0, 0);
|
|
}
|
|
EndDialog(hDlg, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
if (LOWORD(wParam) == IDC_QUOTA_HIDESMALL) {
|
|
g_bHideSmallItems = IsDlgButtonChecked (hDlg, IDC_QUOTA_HIDESMALL);
|
|
SetEvent(g_hQuotaDlgEvent);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// ListViewSortCallback()
|
|
//
|
|
// Purpose: List view callback function for sorting
|
|
//
|
|
// Parameters: lParam1 - lParam1
|
|
// lParam2 - lParam2
|
|
// lParamSort - Column id
|
|
//
|
|
// Return: -1, 0, 1
|
|
//
|
|
//*************************************************************
|
|
INT CALLBACK ListViewSortCallback (LPARAM lParam1, LPARAM lParam2,
|
|
LPARAM lParamSort)
|
|
{
|
|
|
|
if (lParam1 < lParam2) {
|
|
return 1;
|
|
|
|
} else if (lParam1 == lParam2) {
|
|
return 0;
|
|
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// ConvertExclusionList()
|
|
//
|
|
// Purpose: Converts the semi-colon profile relative exclusion
|
|
// list to fully qualified null terminated exclusion
|
|
// list
|
|
//
|
|
// Parameters: lpSourceDir - Profile root directory
|
|
// lpExclusionList - List of directories to exclude
|
|
//
|
|
// Return: List if successful
|
|
// NULL if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
LPTSTR ConvertExclusionList (LPCTSTR lpSourceDir, LPCTSTR lpExclusionList)
|
|
{
|
|
LPTSTR lpExcludeList = NULL, lpInsert, lpEnd, lpTempList;
|
|
LPCTSTR lpTemp, lpDir;
|
|
TCHAR szTemp[MAX_PATH];
|
|
DWORD dwSize = 2; // double null terminator
|
|
DWORD dwStrLen;
|
|
UINT cchRemaining;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Setup a temp buffer to work with
|
|
//
|
|
|
|
hr = StringCchCopy(szTemp, ARRAYSIZE(szTemp), lpSourceDir);
|
|
if (FAILED(hr)) {
|
|
return NULL;
|
|
}
|
|
|
|
lpEnd = CheckSlash (szTemp, ARRAYSIZE(szTemp), &cchRemaining);
|
|
if (!lpEnd) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Loop through the list
|
|
//
|
|
|
|
lpTemp = lpDir = lpExclusionList;
|
|
|
|
while (*lpTemp) {
|
|
|
|
//
|
|
// Look for the semicolon separator
|
|
//
|
|
|
|
while (*lpTemp && ((*lpTemp) != TEXT(';'))) {
|
|
lpTemp++;
|
|
}
|
|
|
|
//
|
|
// Remove any leading spaces
|
|
//
|
|
|
|
while (*lpDir && *lpDir == TEXT(' ')) {
|
|
lpDir++;
|
|
}
|
|
|
|
//
|
|
// Check whether the entry is empty
|
|
//
|
|
|
|
if (lpDir == lpTemp) {
|
|
// If we are at the end of the exclusion list, we're done
|
|
if (!*lpTemp) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Prep for the next entry
|
|
//
|
|
|
|
lpTemp++;
|
|
lpDir = lpTemp;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Put the directory name on the temp buffer
|
|
//
|
|
|
|
*lpEnd = TEXT('\0');
|
|
hr = StringCchCatN(lpEnd, cchRemaining, lpDir, (int)(lpTemp - lpDir));
|
|
if (FAILED(hr)) {
|
|
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to copy 0x%x"), hr));
|
|
LocalFree (lpExcludeList);
|
|
lpExcludeList = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Add the string to the exclusion list
|
|
//
|
|
|
|
if (lpExcludeList) {
|
|
|
|
dwStrLen = lstrlen (szTemp) + 1;
|
|
dwSize += dwStrLen;
|
|
|
|
lpTempList = LocalReAlloc (lpExcludeList, dwSize * sizeof(TCHAR),
|
|
LMEM_MOVEABLE | LMEM_ZEROINIT);
|
|
|
|
if (!lpTempList) {
|
|
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to realloc memory with %d"), GetLastError()));
|
|
LocalFree (lpExcludeList);
|
|
lpExcludeList = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
lpExcludeList = lpTempList;
|
|
|
|
lpInsert = lpExcludeList + dwSize - dwStrLen - 1;
|
|
hr = StringCchCopy(lpInsert, dwStrLen, szTemp);
|
|
if (FAILED(hr)) {
|
|
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to copy 0x%x"), hr));
|
|
LocalFree (lpExcludeList);
|
|
lpExcludeList = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
} else {
|
|
|
|
dwSize += lstrlen (szTemp);
|
|
lpExcludeList = LocalAlloc (LPTR, dwSize * sizeof(TCHAR));
|
|
|
|
if (!lpExcludeList) {
|
|
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to alloc memory with %d"), GetLastError()));
|
|
goto Exit;
|
|
}
|
|
|
|
hr = StringCchCopy(lpExcludeList, dwSize, szTemp);
|
|
if (FAILED(hr)) {
|
|
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to copy 0x%x"), hr));
|
|
LocalFree (lpExcludeList);
|
|
lpExcludeList = NULL;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we are at the end of the exclusion list, we're done
|
|
//
|
|
|
|
if (!(*lpTemp)) {
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Prep for the next entry
|
|
//
|
|
|
|
lpTemp++;
|
|
lpDir = lpTemp;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return lpExcludeList;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// EnumerateProfile()
|
|
//
|
|
// Purpose: Enumerates the profile for size and names
|
|
//
|
|
// Parameters: hLV - listview window handle (optional)
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
|
|
BOOL EnumerateProfile (HWND hLV)
|
|
{
|
|
TCHAR szProfile[2*MAX_PATH];
|
|
LPTSTR lpEnd;
|
|
BOOL bRetVal = FALSE;
|
|
LPTSTR lpExcludeList = NULL;
|
|
LVITEM item;
|
|
|
|
|
|
//
|
|
// Get the profile directory
|
|
//
|
|
|
|
szProfile[0] = TEXT('\0');
|
|
GetEnvironmentVariable (TEXT("USERPROFILE"), szProfile, MAX_PATH);
|
|
|
|
if (szProfile[0] == TEXT('\0')) {
|
|
ExitThread (0);
|
|
}
|
|
|
|
lpEnd = CheckSlash (szProfile, ARRAYSIZE(szProfile), NULL);
|
|
if (!lpEnd) {
|
|
ExitThread (0);
|
|
}
|
|
|
|
|
|
//
|
|
// Claim the critical section
|
|
//
|
|
|
|
EnterCriticalSection (&g_cs);
|
|
|
|
|
|
if (hLV) {
|
|
ListView_DeleteAllItems (hLV);
|
|
}
|
|
|
|
//
|
|
// Get current profile size
|
|
//
|
|
|
|
g_dwProfileSizeTemp = 0;
|
|
|
|
|
|
//
|
|
// Convert the exclusionlist read from the registry to a Null terminated list
|
|
// readable by recursedirectory.
|
|
//
|
|
|
|
if (g_szExcludeList[0] != TEXT('\0'))
|
|
lpExcludeList = ConvertExclusionList (szProfile, g_szExcludeList);
|
|
else
|
|
lpExcludeList = NULL;
|
|
|
|
|
|
if (!RecurseDirectory (szProfile, ARRAYSIZE(szProfile), lpEnd, hLV, lpExcludeList)) {
|
|
SendMessage (hLV, WM_SETREDRAW, TRUE, 0);
|
|
goto Exit;
|
|
}
|
|
|
|
g_dwProfileSize = g_dwProfileSizeTemp;
|
|
|
|
//
|
|
// Sort by size
|
|
//
|
|
|
|
ListView_SortItems (hLV, ListViewSortCallback, 1);
|
|
|
|
|
|
//
|
|
// Select the next item
|
|
//
|
|
|
|
item.mask = LVIF_STATE;
|
|
item.iItem = 0;
|
|
item.iSubItem = 0;
|
|
item.state = LVIS_SELECTED | LVIS_FOCUSED;
|
|
item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
|
|
|
|
SendMessage (hLV, LVM_SETITEMSTATE, 0, (LPARAM) &item);
|
|
|
|
|
|
//
|
|
// Convert to K
|
|
//
|
|
|
|
if (g_dwProfileSize < 1024) {
|
|
g_dwProfileSize = 1;
|
|
} else {
|
|
g_dwProfileSize /= 1024;
|
|
}
|
|
|
|
|
|
bRetVal = TRUE;
|
|
|
|
Exit:
|
|
//
|
|
// Release the critical section
|
|
//
|
|
|
|
LeaveCriticalSection (&g_cs);
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// GetDisplayName()
|
|
//
|
|
// Purpose: Get display name from shell for specific directory
|
|
// to display on the dialog box list view
|
|
//
|
|
// Parameters: IN lpDir : full directory name which display name is required
|
|
// IN lpTop : relative directory name from %USERPROFILE%
|
|
// OUT lpDisplayName : buffer to retrieve the display name
|
|
// IN cchDisplayName : size of display name buffer
|
|
//
|
|
// Return: TRUE if success, else FALSE
|
|
//
|
|
// Remark: This function loops through each directory level of
|
|
// the lpDir and get the shell display name of it, append to the
|
|
// display name buffer.
|
|
//
|
|
//*************************************************************
|
|
BOOL GetDisplayName(LPCTSTR lpDir, LPTSTR lpTop, LPTSTR lpDisplayName, DWORD cchDisplayName)
|
|
{
|
|
SHFILEINFO Info;
|
|
BOOL bFirstSlash = TRUE;
|
|
HRESULT hr;
|
|
|
|
hr = StringCchCopy(lpDisplayName, cchDisplayName, TEXT(""));
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
for ( ; lpTop[0]; lpTop++)
|
|
{
|
|
if (lpTop[0] == TEXT('\\'))
|
|
{
|
|
lpTop[0] = TEXT('\0');
|
|
if (!SHGetFileInfo(lpDir, 0, &Info, sizeof(Info), SHGFI_DISPLAYNAME))
|
|
{
|
|
DebugMsg((DM_WARNING, TEXT("SHGetFileInfo failed, err = %d"), GetLastError()));
|
|
lpTop[0] = TEXT('\\');
|
|
return FALSE;
|
|
}
|
|
lpTop[0] = TEXT('\\');
|
|
if (bFirstSlash)
|
|
{
|
|
bFirstSlash = FALSE;
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchCat(lpDisplayName, cchDisplayName, TEXT("\\"));
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
}
|
|
|
|
hr = StringCchCat(lpDisplayName, cchDisplayName, Info.szDisplayName);
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (!SHGetFileInfo(lpDir, 0, &Info, sizeof(Info), SHGFI_DISPLAYNAME))
|
|
{
|
|
DebugMsg((DM_WARNING, TEXT("SHGetFileInfo failed, err = %d"), GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
hr = StringCchCat(lpDisplayName, cchDisplayName, TEXT("\\"));
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
hr = StringCchCat(lpDisplayName, cchDisplayName, Info.szDisplayName);
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|