mirror of
https://github.com/ladislav-zezula/FileTest
synced 2026-06-08 15:24:12 +00:00
922 lines
30 KiB
C++
922 lines
30 KiB
C++
/*****************************************************************************/
|
|
/* DlgFileTest.cpp Copyright (c) Ladislav Zezula 2009 */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Description : */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Date Ver Who Comment */
|
|
/* -------- ---- --- ------- */
|
|
/* 27.05.09 1.00 Lad Created */
|
|
/*****************************************************************************/
|
|
|
|
#include "FileTest.h"
|
|
#include "resource.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local variables
|
|
|
|
#define SC_HELP_ABOUT (SC_CLOSE + 0x800)
|
|
|
|
static TAnchors * pAnchors;
|
|
static SIZE InitialDialogSize = {0, 0};
|
|
static BOOL bDisableCloseDialog = FALSE;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Thread moving the dialog
|
|
|
|
static DWORD WINAPI MoveDialogThread(PVOID pParam)
|
|
{
|
|
TWindowData * pData = (TWindowData *)pParam;
|
|
int nCurrentY = pData->nStartY;
|
|
int nAddY;
|
|
|
|
// Determine the direction
|
|
nAddY = (g_dwWinVer >= OSVER_WINDOWS_NT4) ? 1 : 5;
|
|
nAddY = (pData->nEndY > pData->nStartY) ? +nAddY : -nAddY;
|
|
|
|
for(;;)
|
|
{
|
|
// Move the dialog
|
|
nCurrentY += nAddY;
|
|
|
|
// Check for end of moving
|
|
if(pData->nEndY > pData->nStartY && nCurrentY > pData->nEndY)
|
|
break;
|
|
if(pData->nEndY < pData->nStartY && nCurrentY < pData->nEndY)
|
|
break;
|
|
|
|
// Move the entire dialog
|
|
SetWindowPos(pData->hDlg, NULL, pData->nStartX, nCurrentY, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
}
|
|
|
|
// We are done; close the thread handle and refresh the dialog position
|
|
GetWindowRect(pData->hDlg, &pData->DialogRect);
|
|
CloseHandle(pData->hThread);
|
|
pData->hThread = NULL;
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// "APC" thread - performing wait on all queued APCs and sends message
|
|
// when an APC is awaken
|
|
|
|
#define ALERT_REASON_STOP_WORKER 0 // The worker needs to stop
|
|
#define ALERT_REASON_UPDATE_WAIT 1 // The wait list needs to be updated
|
|
|
|
static DWORD WINAPI ApcThread(LPVOID pvParameter)
|
|
{
|
|
TWindowData * pData = (TWindowData *)pvParameter;
|
|
PLIST_ENTRY pHeadEntry;
|
|
PLIST_ENTRY pListEntry;
|
|
TApcEntry * ApcList[MAXIMUM_WAIT_OBJECTS];
|
|
HANDLE WaitHandles[MAXIMUM_WAIT_OBJECTS];
|
|
TApcEntry * pApc;
|
|
|
|
// The first event is always the alert event
|
|
WaitHandles[0] = pData->hAlertEvent;
|
|
assert(pData->hAlertEvent != NULL);
|
|
|
|
// Perform a loop until the process does not end
|
|
while(pData->hAlertEvent != NULL)
|
|
{
|
|
DWORD dwWaitCount = 1;
|
|
DWORD dwWaitResult;
|
|
|
|
// Prepare the list of handles to wait
|
|
EnterCriticalSection(&pData->ApcLock);
|
|
{
|
|
pHeadEntry = &pData->ApcList;
|
|
for(pListEntry = pHeadEntry->Flink; pListEntry != pHeadEntry; pListEntry = pListEntry->Flink)
|
|
{
|
|
// Retrieve the APC entry
|
|
pApc = CONTAINING_RECORD(pListEntry, TApcEntry, Entry);
|
|
|
|
// Insert the APC entry to the wait list
|
|
WaitHandles[dwWaitCount] = pApc->hEvent;
|
|
ApcList[dwWaitCount++] = pApc;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&pData->ApcLock);
|
|
|
|
// Now when the list if prepared, we can perform wait on all
|
|
assert(dwWaitCount < MAXIMUM_WAIT_OBJECTS);
|
|
dwWaitResult = WaitForMultipleObjects(dwWaitCount, WaitHandles, FALSE, INFINITE);
|
|
|
|
// If the first wait broke, it means that we need to exit
|
|
if(dwWaitResult == WAIT_OBJECT_0 || dwWaitResult == WAIT_ABANDONED_0)
|
|
{
|
|
// If we need just to update wait list, do it
|
|
if(pData->dwAlertReason == ALERT_REASON_UPDATE_WAIT)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
// Check if any of the APCs has triggered
|
|
if(WAIT_OBJECT_0 < dwWaitResult && dwWaitResult < (WAIT_OBJECT_0 + dwWaitCount))
|
|
{
|
|
// Get the pointer to the triggered APC
|
|
pApc = ApcList[dwWaitResult - WAIT_OBJECT_0];
|
|
assert(pApc != NULL);
|
|
|
|
// Remove the APC from the list (locked)
|
|
EnterCriticalSection(&pData->ApcLock);
|
|
{
|
|
RemoveEntryList(&pApc->Entry);
|
|
pApc->Entry.Flink = NULL;
|
|
pApc->Entry.Blink = NULL;
|
|
pData->nApcCount--;
|
|
}
|
|
LeaveCriticalSection(&pData->ApcLock);
|
|
|
|
// Send the APC to the main dialog
|
|
// Note that the main dialog is responsible for freeing the APC
|
|
PostMessage(pApc->hWndPage, WM_APC, 0, (LPARAM)pApc);
|
|
}
|
|
}
|
|
|
|
// Now we need to free all the APCs
|
|
EnterCriticalSection(&pData->ApcLock);
|
|
{
|
|
pHeadEntry = &pData->ApcList;
|
|
for(pListEntry = pHeadEntry->Flink; pListEntry != pHeadEntry; )
|
|
{
|
|
// Retrieve the APC entry
|
|
pApc = CONTAINING_RECORD(pListEntry, TApcEntry, Entry);
|
|
pListEntry = pListEntry->Flink;
|
|
|
|
// Remove the APC from the list and free it
|
|
FreeApcEntry(pApc);
|
|
}
|
|
|
|
// Reset the APC list
|
|
InitializeListHead(&pData->ApcList);
|
|
pData->nApcCount = 0;
|
|
}
|
|
LeaveCriticalSection(&pData->ApcLock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void AlertApcThread(TWindowData * pData, DWORD dwAlertReason)
|
|
{
|
|
// Signal the event handle and close it
|
|
if(pData->hApcThread && pData->hAlertEvent)
|
|
{
|
|
pData->dwAlertReason = dwAlertReason;
|
|
SetEvent(pData->hAlertEvent);
|
|
}
|
|
}
|
|
|
|
static void GetFileTestAppTitle(LPTSTR szTitle, int nMaxChars)
|
|
{
|
|
TCHAR szUserName[256] = _T("");
|
|
DWORD dwSize = _countof(szUserName);
|
|
UINT nIDTitle = IDS_APP_TITLE;
|
|
BOOL bElevated = FALSE;
|
|
|
|
// Get the elevation flags. Note that this returns FALSE on pre-Vista
|
|
if(GetTokenElevation(&bElevated))
|
|
{
|
|
nIDTitle = bElevated ? IDS_APP_TITLE_VISTA1 : IDS_APP_TITLE_VISTA2;
|
|
}
|
|
|
|
GetUserName(szUserName, &dwSize);
|
|
rsprintf(szTitle, nMaxChars, nIDTitle, szUserName);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local functions
|
|
|
|
static NTSTATUS SafeInitializeCriticalSection(CRITICAL_SECTION & Section)
|
|
{
|
|
#if defined(_MSC_VER)
|
|
__try
|
|
{
|
|
#endif
|
|
InitializeCriticalSection(&Section);
|
|
return STATUS_SUCCESS;
|
|
#if defined(_MSC_VER)
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void InitializeTabControl(HWND hDlg, LPARAM lParam)
|
|
{
|
|
TFileTestData * pData = (TFileTestData *)(lParam);
|
|
PROPSHEETHEADER psh = {sizeof(PROPSHEETHEADER)};
|
|
PROPSHEETPAGE psp[13] = {0};
|
|
TCHAR szAppTitle[256];
|
|
HWND hTabCtrl = GetDlgItem(hDlg, IDC_TAB);
|
|
int nPageNtOpenFile = 0;
|
|
int nPageOpenFile = 0;
|
|
int nPages = 0;
|
|
|
|
// Get the title of FileTest application
|
|
GetFileTestAppTitle(szAppTitle, _countof(szAppTitle));
|
|
|
|
// Fill the property sheet header
|
|
psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_MODELESS;
|
|
psh.hwndParent = hDlg;
|
|
psh.hInstance = g_hInst;
|
|
psh.pszIcon = MAKEINTRESOURCE(IDI_FILE_TEST);
|
|
psh.pszCaption = szAppTitle;
|
|
psh.ppsp = psp;
|
|
|
|
// Fill the "Transaction" page (Vista only)
|
|
if(pfnCreateTransaction != NULL)
|
|
{
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE00_TRANSACTION);
|
|
psp[nPages].pfnDlgProc = PageProc00;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
}
|
|
|
|
// Fill the "CreateFile" page
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE01_CREATE);
|
|
psp[nPages].pfnDlgProc = PageProc01;
|
|
psp[nPages].lParam = lParam;
|
|
psh.nStartPage = nPages;
|
|
nPageOpenFile = nPages++;
|
|
|
|
// Fill the "NtCreateFile"
|
|
// Note: If the file name looks like an NT name, go to NtCreateFile page
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE02_NTCREATE);
|
|
psp[nPages].pfnDlgProc = PageProc02;
|
|
psp[nPages].lParam = lParam;
|
|
nPageNtOpenFile = nPages;
|
|
nPages++;
|
|
|
|
// Fill the "ReadWrite"
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE03_READWRITE);
|
|
psp[nPages].pfnDlgProc = PageProc03;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Fill the "Mapping"
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE04_MAPPING);
|
|
psp[nPages].pfnDlgProc = PageProc04;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Fill the "File Ops".
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE05_FILEOPS);
|
|
psp[nPages].pfnDlgProc = PageProc05;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Fill the "NtFileInfo" page
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE06_NTFILEINFO);
|
|
psp[nPages].pfnDlgProc = PageProc06;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Fill the "NtFsInfo" page.
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE07_NTVOLINFO);
|
|
psp[nPages].pfnDlgProc = PageProc06;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Fill the "EA" page.
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE08_EA);
|
|
psp[nPages].pfnDlgProc = PageProc08; // The same like NtFileInfo
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Fill the "Security".
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE09_SECURITY);
|
|
psp[nPages].pfnDlgProc = PageProc09;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Fill the "Links".
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE10_LINKS);
|
|
psp[nPages].pfnDlgProc = PageProc10;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Fill the "Streams" page.
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE11_STREAMS);
|
|
psp[nPages].pfnDlgProc = PageProc11;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Fill the "Ioctl" page.
|
|
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
|
|
psp[nPages].dwFlags = PSP_DEFAULT;
|
|
psp[nPages].hInstance = g_hInst;
|
|
psp[nPages].pszTemplate = MAKEINTRESOURCE(IDD_PAGE12_IOCTL);
|
|
psp[nPages].pfnDlgProc = PageProc12;
|
|
psp[nPages].lParam = lParam;
|
|
nPages++;
|
|
|
|
// Set the start page and number of pages
|
|
if((psh.nStartPage = pData->InitialPage) == INVALID_PAGE_INDEX)
|
|
psh.nStartPage = IsNativeName(pData->szFileName1) ? nPageNtOpenFile : nPageOpenFile;
|
|
psh.nPages = nPages;
|
|
|
|
// Create Tab Control
|
|
TabCtrl_Create(hTabCtrl, &psh);
|
|
|
|
// Get the currently selected page HWND
|
|
pData->hWndPage = TabCtrl_GetSelectedPage(hTabCtrl);
|
|
}
|
|
|
|
static void RefreshScreenSize(HWND hDlg)
|
|
{
|
|
TWindowData * pData = GetDialogData(hDlg);
|
|
int nScreenHeight;
|
|
int nDialogHeight;
|
|
|
|
// Save the screen size
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &pData->ScreenRect, 0);
|
|
GetWindowRect(hDlg, &pData->DialogRect);
|
|
|
|
// Is the dialog higher than the screen?
|
|
nScreenHeight = pData->ScreenRect.bottom - pData->ScreenRect.top;
|
|
nDialogHeight = pData->DialogRect.bottom - pData->DialogRect.top;
|
|
pData->bDialogBiggerThanScreen = (nDialogHeight > nScreenHeight);
|
|
|
|
// If the dialog is bigger than the screen, set the timer
|
|
if(pData->bDialogBiggerThanScreen)
|
|
{
|
|
if(pData->CheckMouseTimer == 0)
|
|
{
|
|
pData->CheckMouseTimer = SetTimer(hDlg, WM_TIMER_CHECK_MOUSE, 500, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(pData->CheckMouseTimer != 0)
|
|
{
|
|
KillTimer(hDlg, pData->CheckMouseTimer);
|
|
pData->CheckMouseTimer = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Original dialog size
|
|
|
|
#pragma pack(1)
|
|
typedef struct _DLGTEMPLATEEX_BEGIN
|
|
{
|
|
WORD dlgVer;
|
|
WORD signature;
|
|
DWORD helpID;
|
|
DWORD exStyle;
|
|
DWORD style;
|
|
WORD cDlgItems;
|
|
short x;
|
|
short y;
|
|
short cx;
|
|
short cy;
|
|
} DLGTEMPLATEEX_BEGIN, *PDLGTEMPLATEEX_BEGIN;
|
|
#pragma pack()
|
|
|
|
int GetDialogRectFromTemplate(HWND hDlg, UINT DlgResID, RECT & DlgRect)
|
|
{
|
|
PDLGTEMPLATEEX_BEGIN pDlgTemplate;
|
|
HGLOBAL hDlgRes;
|
|
HRSRC hResource;
|
|
|
|
hResource = FindResource(g_hInst, MAKEINTRESOURCE(DlgResID), RT_DIALOG);
|
|
if(hResource != NULL)
|
|
{
|
|
hDlgRes = LoadResource(g_hInst, hResource);
|
|
if(hDlgRes != NULL)
|
|
{
|
|
pDlgTemplate = (DLGTEMPLATEEX_BEGIN *)LockResource(hDlgRes);
|
|
if(pDlgTemplate != NULL)
|
|
{
|
|
// Check the dialog template
|
|
assert(pDlgTemplate->signature == (WORD)-1);
|
|
assert(pDlgTemplate->dlgVer == 1);
|
|
|
|
// Calculate the dialog size in pixels
|
|
DlgRect.top = 0;
|
|
DlgRect.left = 0;
|
|
DlgRect.right = pDlgTemplate->cx;
|
|
DlgRect.bottom = pDlgTemplate->cy;
|
|
MapDialogRect(hDlg, &DlgRect);
|
|
|
|
// Append the borders and the caption
|
|
DlgRect.right += (GetSystemMetrics(SM_CXSIZEFRAME) * 2);
|
|
DlgRect.bottom += GetSystemMetrics(SM_CYCAPTION) + (GetSystemMetrics(SM_CYSIZEFRAME) * 2);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ERROR_RESOURCE_NOT_FOUND;
|
|
}
|
|
|
|
static void FixDialogToOriginalSize(HWND hDlg, UINT DlgResID)
|
|
{
|
|
RECT OriginalRect;
|
|
RECT CurrentRect;
|
|
RECT ScreenRect;
|
|
int x;
|
|
|
|
// Get the work area of the screen
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &ScreenRect, 0);
|
|
ScreenRect.bottom = (ScreenRect.bottom - ScreenRect.top);
|
|
ScreenRect.right = (ScreenRect.right - ScreenRect.left);
|
|
|
|
// Get the current dialog size
|
|
GetWindowRect(hDlg, &CurrentRect);
|
|
CurrentRect.bottom = (CurrentRect.bottom - CurrentRect.top);
|
|
CurrentRect.right = (CurrentRect.right - CurrentRect.left);
|
|
CurrentRect.left = CurrentRect.top = 0;
|
|
|
|
// If the current height is greater than height
|
|
// of the worker area, we need to fix it
|
|
if(CurrentRect.bottom > ScreenRect.bottom)
|
|
{
|
|
// Get the dialog expected size
|
|
GetDialogRectFromTemplate(hDlg, DlgResID, OriginalRect);
|
|
|
|
// If the dialog has been shrunk, fix its size
|
|
if(OriginalRect.bottom > CurrentRect.bottom)
|
|
{
|
|
x = (ScreenRect.right - OriginalRect.right) / 2;
|
|
SetWindowPos(hDlg, NULL, x, 0, OriginalRect.right, OriginalRect.bottom, SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AddAboutToSystemMenu(HWND hDlg)
|
|
{
|
|
MENUITEMINFO mii;
|
|
HMENU hSysMenu;
|
|
TCHAR szItemText[256];
|
|
int nSeparatorIndex = -1;
|
|
int nMenuCount;
|
|
|
|
// Retrieve system menu
|
|
hSysMenu = GetSystemMenu(hDlg, FALSE);
|
|
if(hSysMenu != NULL)
|
|
{
|
|
// Find the separator
|
|
nMenuCount = GetMenuItemCount(hSysMenu);
|
|
for(int i = 0; i < nMenuCount; i++)
|
|
{
|
|
// Retrieve the item type
|
|
ZeroMemory(&mii, sizeof(MENUITEMINFO));
|
|
mii.cbSize = sizeof(MENUITEMINFO);
|
|
mii.fMask = MIIM_FTYPE;
|
|
GetMenuItemInfo(hSysMenu, i, TRUE, &mii);
|
|
|
|
// Separator?
|
|
if(mii.fType == MFT_SEPARATOR)
|
|
{
|
|
nSeparatorIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we found a separator, we need to add two more items
|
|
if(nSeparatorIndex != -1)
|
|
{
|
|
LoadString(g_hInst, IDS_HELP_ABOUT, szItemText, _countof(szItemText));
|
|
InsertMenu(hSysMenu, nSeparatorIndex, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
|
InsertMenu(hSysMenu, nSeparatorIndex+1, MF_BYPOSITION | MF_STRING, SC_HELP_ABOUT, szItemText);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Dialog handlers
|
|
|
|
static int OnInitDialog(HWND hDlg, LPARAM lParam)
|
|
{
|
|
TWindowData * pData = (TWindowData *)lParam;
|
|
DWORD dwThreadId;
|
|
RECT rectDialog;
|
|
|
|
// Initialize dialog data
|
|
SafeInitializeCriticalSection(pData->ApcLock);
|
|
InitializeListHead(&pData->ApcList);
|
|
pData->hDlg = hDlg;
|
|
SetDialogData(hDlg, pData);
|
|
|
|
// Add "About" in the system menu
|
|
AddAboutToSystemMenu(hDlg);
|
|
|
|
//
|
|
// Note: If the screen size is too low at this point (like 800x600),
|
|
// the dialog gets shrinked. We need to resize the dialog to the original size
|
|
//
|
|
|
|
FixDialogToOriginalSize(hDlg, IDD_FILE_TEST);
|
|
GetWindowRect(hDlg, &rectDialog);
|
|
InitialDialogSize.cx = (rectDialog.right - rectDialog.left);
|
|
InitialDialogSize.cy = (rectDialog.bottom - rectDialog.top);
|
|
|
|
// Create anchors for the tab control and Exit button
|
|
if((pAnchors = new TAnchors(hDlg)) != NULL)
|
|
{
|
|
pAnchors->AddAnchor(hDlg, IDC_TAB, akAll);
|
|
pAnchors->AddAnchor(hDlg, IDC_EXIT, akRightBottom);
|
|
}
|
|
|
|
// Create the tooltip window
|
|
g_Tooltip.Initialize(g_hInst, hDlg);
|
|
|
|
// Initialize Tab Control
|
|
InitializeTabControl(hDlg, lParam);
|
|
|
|
// Refresh information about screen rect and dialog rect
|
|
RefreshScreenSize(hDlg);
|
|
|
|
// Create the so called "APC" thread that will monitor our list of APC entries
|
|
pData->hAlertEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
pData->hApcThread = CreateThread(NULL, 0, ApcThread, pData, 0, &dwThreadId);
|
|
return TRUE;
|
|
}
|
|
|
|
static void OnTimerCheckMouse(HWND hDlg)
|
|
{
|
|
TWindowData * pData = GetDialogData(hDlg);
|
|
POINT pt;
|
|
DWORD ThreadID;
|
|
int nTresholdX = 40;
|
|
int nTresholdY = pData->ScreenRect.bottom - 40;
|
|
|
|
// Get the current mouse position
|
|
GetCursorPos(&pt);
|
|
|
|
// If the dialog is currently bigger than the screen, check for limit values
|
|
if(pData->bDialogBiggerThanScreen)
|
|
{
|
|
// Only do something if the mouse cursor is within our rectangle
|
|
if(GetActiveWindow() == hDlg && PtInRect(&pData->DialogRect, pt))
|
|
{
|
|
if(pData->DialogRect.top < pData->ScreenRect.top && pt.y <= nTresholdX)
|
|
{
|
|
// Create thread that will move the dialog down
|
|
if(pData->hThread == NULL)
|
|
{
|
|
pData->nStartX = pData->DialogRect.left;
|
|
pData->nStartY = pData->DialogRect.top;
|
|
pData->nEndY = 0;
|
|
pData->hThread = CreateThread(NULL, 0, MoveDialogThread, pData, 0, &ThreadID);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(pData->DialogRect.bottom > pData->ScreenRect.bottom && pt.y >= nTresholdY)
|
|
{
|
|
// Create thread that will move the dialog up
|
|
if(pData->hThread == NULL)
|
|
{
|
|
pData->nStartX = pData->DialogRect.left;
|
|
pData->nStartY = pData->DialogRect.top;
|
|
pData->nEndY = (pData->ScreenRect.bottom - pData->ScreenRect.top) - (pData->DialogRect.bottom - pData->DialogRect.top);
|
|
pData->hThread = CreateThread(NULL, 0, MoveDialogThread, pData, 0, &ThreadID);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void OnHelpAbout(HWND hDlg)
|
|
{
|
|
HelpAboutDialog(hDlg);
|
|
}
|
|
|
|
static BOOL OnCommand(HWND hDlg, UINT /* nNotify */, UINT nIDCtrl)
|
|
{
|
|
switch(nIDCtrl)
|
|
{
|
|
case IDCANCEL:
|
|
case IDC_EXIT:
|
|
DestroyWindow(hDlg);
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void OnNotify(HWND hDlg, NMHDR * pNMHDR)
|
|
{
|
|
TWindowData * pData;
|
|
|
|
if(pNMHDR->code == TCN_SELCHANGE)
|
|
{
|
|
pData = GetDialogData(hDlg);
|
|
pData->hWndPage = TabCtrl_GetSelectedPage(pNMHDR->hwndFrom);
|
|
}
|
|
}
|
|
|
|
static void OnDestroy(HWND hDlg)
|
|
{
|
|
TWindowData * pData = GetDialogData(hDlg);
|
|
|
|
if(pData != NULL)
|
|
{
|
|
if(pData->hApcThread != NULL)
|
|
{
|
|
// Stop the watcher thread, if any
|
|
AlertApcThread(pData, ALERT_REASON_STOP_WORKER);
|
|
|
|
// Wait for the thread to exit. Not more than 5 second
|
|
WaitForSingleObject(pData->hApcThread, 5000);
|
|
CloseHandle(pData->hApcThread);
|
|
}
|
|
|
|
// Close the alert event handle
|
|
if(pData->hAlertEvent != NULL)
|
|
CloseHandle(pData->hAlertEvent);
|
|
pData->hAlertEvent = NULL;
|
|
|
|
// Delete the APC critical section
|
|
DeleteCriticalSection(&pData->ApcLock);
|
|
}
|
|
}
|
|
|
|
static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// Let Tab Control handle messages belonging to it
|
|
if(TabCtrl_HandleMessages(GetDlgItem(hDlg, IDC_TAB), uMsg, wParam, lParam))
|
|
return TRUE;
|
|
|
|
// Handle messages that have been passed to us
|
|
switch(uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
OnInitDialog(hDlg, lParam);
|
|
return TRUE;
|
|
|
|
case WM_SIZE:
|
|
case WM_GETMINMAXINFO:
|
|
pAnchors->OnMessage(uMsg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGED:
|
|
case WM_DISPLAYCHANGE:
|
|
RefreshScreenSize(hDlg);
|
|
break;
|
|
|
|
case WM_SETTINGCHANGE:
|
|
if(wParam == SPI_SETWORKAREA)
|
|
RefreshScreenSize(hDlg);
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
if(wParam == WM_TIMER_CHECK_MOUSE)
|
|
OnTimerCheckMouse(hDlg);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
return OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam));
|
|
|
|
case WM_SYSCOMMAND:
|
|
if(wParam == SC_HELP_ABOUT)
|
|
OnHelpAbout(hDlg);
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
OnNotify(hDlg, (NMHDR *)lParam);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
OnDestroy(hDlg);
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL IsMyDialogMessage(HWND hDlg, HWND hTabCtrl, LPMSG pMsg)
|
|
{
|
|
// Support for navigation keys
|
|
if(pMsg->message == WM_KEYDOWN)
|
|
{
|
|
switch(pMsg->wParam)
|
|
{
|
|
case VK_F2: // Allow tree item editing using F2 accelerator
|
|
return FALSE;
|
|
|
|
// If the dialog messages are disabled, we pass all key messages as-is.
|
|
// Example: When editing a tree view item, Enter and Esc key would
|
|
// be eaten by the dialog and they would never arrive to the edit box.
|
|
case VK_RETURN:
|
|
case VK_ESCAPE:
|
|
if(bDisableCloseDialog)
|
|
return FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TabCtrl_IsDialogMessage(hDlg, hTabCtrl, pMsg);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Public functions - APC support
|
|
|
|
TApcEntry * CreateApcEntry(TWindowData * pData, UINT ApcType, size_t cbExtraSize)
|
|
{
|
|
TApcEntry * pApc = NULL;
|
|
|
|
// If there is too many APCs queued, do nothing
|
|
if(pData->nApcCount < MAXIMUM_WAIT_OBJECTS - 1)
|
|
{
|
|
// Allocate space for the APC structure
|
|
pApc = (TApcEntry *)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, sizeof(TApcEntry) + cbExtraSize);
|
|
if(pApc != NULL)
|
|
{
|
|
// Create new event object for the APC entry
|
|
pApc->Overlapped.hEvent =
|
|
pApc->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
pApc->ApcType = ApcType;
|
|
pApc->hWndPage = pData->hWndPage;
|
|
if(pApc->hEvent != NULL)
|
|
return pApc;
|
|
|
|
// Free the (now useless) APC entry
|
|
HeapFree(g_hHeap, 0, pApc);
|
|
}
|
|
}
|
|
|
|
// Failed
|
|
return NULL;
|
|
}
|
|
|
|
bool InsertApcEntry(TWindowData * pData, TApcEntry * pApc)
|
|
{
|
|
// Only if the APC is valid
|
|
if(pApc != NULL)
|
|
{
|
|
// Sanity check
|
|
assert(pApc->hEvent != NULL);
|
|
|
|
// If there is too many APCs queued, do nothing
|
|
if(pData->nApcCount < MAXIMUM_WAIT_OBJECTS - 1)
|
|
{
|
|
// Mark the APC as complete asynchronously
|
|
pApc->bAsynchronousCompletion = TRUE;
|
|
|
|
// Insert the APC to the APC list.
|
|
// The APC thread does not know about it yet
|
|
EnterCriticalSection(&pData->ApcLock);
|
|
InsertTailList(&pData->ApcList, &pApc->Entry);
|
|
pData->nApcCount++;
|
|
LeaveCriticalSection(&pData->ApcLock);
|
|
|
|
// Alert the APC thread so it knows that it needs to update the APC list
|
|
AlertApcThread(pData, ALERT_REASON_UPDATE_WAIT);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FreeApcEntry(TApcEntry * pApc)
|
|
{
|
|
if(pApc != NULL)
|
|
{
|
|
if(pApc->hEvent != NULL)
|
|
CloseHandle(pApc->hEvent);
|
|
HeapFree(g_hHeap, 0, pApc);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Public functions
|
|
|
|
int NtUseFileId(HWND hDlg, LPCTSTR szFileId)
|
|
{
|
|
TFileTestData * pData = GetDialogData(hDlg);
|
|
LPCTSTR szPlainName = szFileId + 3;
|
|
HWND hWndParent = GetParent(hDlg);
|
|
HWND hTabCtrl = GetDlgItem(hWndParent, IDC_TAB);
|
|
|
|
// The file ID is expected in the format of "X:\################"
|
|
if(szFileId[1] != _T(':') || szFileId[2] != _T('\\'))
|
|
return ERROR_BAD_FORMAT;
|
|
|
|
// The file ID must not contain special characters
|
|
if(_tcschr(szPlainName, _T('\\')) || _tcschr(szPlainName, _T('/')) || _tcschr(szPlainName, _T(':')))
|
|
return ERROR_BAD_FORMAT;
|
|
|
|
// Copy the directory name
|
|
pData->szDirName[0] = szFileId[0];
|
|
pData->szDirName[1] = szFileId[1];
|
|
pData->szDirName[2] = szFileId[2];
|
|
pData->szDirName[3] = 0;
|
|
pData->UseRelativeFile = FALSE;
|
|
|
|
// Copy the file id
|
|
StringCchCopy(pData->szFileName1, MAX_NT_PATH, szPlainName);
|
|
|
|
// Set the appropriate flags
|
|
pData->OpenFile.dwCreateOptions = FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_BY_FILE_ID;
|
|
|
|
// Switch to the "NtCreateFile" tab
|
|
TabCtrl_SelectPageByID(hTabCtrl, MAKEINTRESOURCE(IDD_PAGE02_NTCREATE));
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
void DisableCloseDialog(HWND hDlg, BOOL bDisable)
|
|
{
|
|
HWND hExitButton = GetDlgItem(GetParent(hDlg), IDC_EXIT);
|
|
|
|
// Initialize the buttons
|
|
// Hide "OK" button and change "Cancel" to "Exit"
|
|
if(hExitButton != NULL)
|
|
EnableWindow(hExitButton, !bDisable);
|
|
bDisableCloseDialog = bDisable;
|
|
}
|
|
|
|
INT_PTR FileTestDialog(HWND hParent, TFileTestData * pData)
|
|
{
|
|
HACCEL hAccelTable = LoadAccelerators(g_hInst, MAKEINTRESOURCE(IDR_ACCELERATORS));
|
|
HWND hTabCtrl;
|
|
HWND hDlg;
|
|
MSG msg;
|
|
|
|
// Create the property sheet
|
|
pData->bEnableResizing = TRUE;
|
|
hDlg = CreateDialogParam(g_hInst,
|
|
MAKEINTRESOURCE(IDD_FILE_TEST),
|
|
hParent,
|
|
DialogProc,
|
|
(LPARAM)pData);
|
|
|
|
// Perform the modal loop
|
|
if(hDlg != NULL)
|
|
{
|
|
// Show the dialog
|
|
hTabCtrl = GetDlgItem(hDlg, IDC_TAB);
|
|
ShowWindow(hDlg, SW_SHOW);
|
|
|
|
// Get the message. Stop processing if WM_QUIT has arrived
|
|
while(IsWindow(hDlg) && GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
// We need an alertable sleep to make APCs to work.
|
|
// Uncomment this if you want to use the asynchronous "ApcRoutine"
|
|
// parameter(s) in some native API
|
|
// MsgWaitForMultipleObjectsEx(0,
|
|
// NULL,
|
|
// INFINITE,
|
|
// QS_ALLEVENTS | QS_ALLINPUT | QS_ALLPOSTMESSAGE,
|
|
// MWMO_WAITALL | MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
|
|
|
|
if(!IsMyDialogMessage(hDlg, hTabCtrl, &msg))
|
|
{
|
|
// Process the accelerator table
|
|
if(!TranslateAccelerator(hDlg, hAccelTable, &msg))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(hAccelTable != NULL)
|
|
DestroyAcceleratorTable(hAccelTable);
|
|
return IDOK;
|
|
}
|