mirror of
https://github.com/ladislav-zezula/FileTest
synced 2026-06-08 15:24:12 +00:00
fbf207fb2b
+ Deleting directory tree improved: Now uses Win10's FILE_DISPOSITION_INFORMATION_EX if necessary.
1692 lines
52 KiB
C++
1692 lines
52 KiB
C++
/*****************************************************************************/
|
|
/* Utils.cpp Copyright (c) Ladislav Zezula 2004 */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Description: */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Date Ver Who Comment */
|
|
/* -------- ---- --- ------- */
|
|
/* 05.01.04 1.00 Lad The first version of Utils.cpp */
|
|
/*****************************************************************************/
|
|
|
|
#include "FileTest.h"
|
|
#include "resource.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// _tcstol replacement
|
|
|
|
static BYTE CharToValue[0x80] =
|
|
{
|
|
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
};
|
|
|
|
static TCHAR ValueToChar[] = _T("0123456789ABCDEF");
|
|
|
|
static inline LPCTSTR SkipHexaPrefix(LPCTSTR szString)
|
|
{
|
|
return (szString[0] == _T('0') && (szString[1] == _T('x') || szString[1] == _T('X'))) ? szString + 2 : szString;
|
|
}
|
|
|
|
static void Hex2TextXX(ULONGLONG Value, LPTSTR szBuffer, int nSize)
|
|
{
|
|
// Get the number of hexa digits
|
|
nSize = nSize + nSize - 1;
|
|
|
|
// Convert the number to string
|
|
for(int i = 0; i <= nSize; i++)
|
|
{
|
|
szBuffer[nSize - i] = ValueToChar[Value & 0x0F];
|
|
Value >>= 4;
|
|
}
|
|
|
|
// Terminate the string
|
|
szBuffer[nSize + 1] = 0;
|
|
}
|
|
|
|
static bool IsPossibleFileId(LPCTSTR szFileName)
|
|
{
|
|
size_t nLength = _tcslen(szFileName);
|
|
|
|
// File ID has 15 characters, object ID has 32
|
|
if(nLength != 16 && nLength != 32)
|
|
return false;
|
|
|
|
// File ID can only contain hexadecimal letters
|
|
for(size_t i = 0; i < nLength; i++)
|
|
{
|
|
if(!isxdigit(szFileName[i]))
|
|
return false;
|
|
}
|
|
|
|
// All checks passed. It could be a file ID
|
|
return true;
|
|
}
|
|
|
|
static void ShowConversionError(HWND hWndParent, HWND hWndChild)
|
|
{
|
|
// Show the error to the user
|
|
MessageBoxRc(hWndParent, IDS_ERROR, IDS_E_CONVERT_VALUE);
|
|
|
|
// Select and focus the control
|
|
SendMessage(hWndChild, EM_SETSEL, 0, (LPARAM)-1);
|
|
SetFocus(hWndChild);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Converts the string to long. This function was written because
|
|
// the builtin strtol does not work correctly (try to convert 0xAABBCCDD !!!)
|
|
|
|
DWORD StrToInt(LPCTSTR szString, LPTSTR * szEnd, int nRadix)
|
|
{
|
|
DWORD nSaveValue = 0;
|
|
DWORD nValue = 0;
|
|
int nDigit;
|
|
|
|
while((nDigit = szString[0]) != 0)
|
|
{
|
|
// If the character is not an hexa number, break
|
|
if(nDigit > 0x80 || CharToValue[nDigit] == 0xFF)
|
|
break;
|
|
|
|
// Convert to digit
|
|
nDigit = CharToValue[nDigit];
|
|
if(nDigit > (nRadix - 1))
|
|
break;
|
|
|
|
// Move the value to the next rank and add the digit
|
|
nSaveValue = nValue;
|
|
nValue *= nRadix;
|
|
nValue += nDigit;
|
|
szString++;
|
|
|
|
// Overflow check
|
|
if(nValue < nSaveValue)
|
|
break;
|
|
}
|
|
|
|
if(szEnd != NULL)
|
|
*szEnd = (LPTSTR)szString;
|
|
return nValue;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// bool values support
|
|
|
|
int Text2Bool(LPCTSTR szText, bool * pValue)
|
|
{
|
|
bool bNewValue = false;
|
|
|
|
if(_tcsicmp(szText, _T("true")))
|
|
bNewValue = true;
|
|
else if(_tcsicmp(szText, _T("on")))
|
|
bNewValue = true;
|
|
else if(_tcsicmp(szText, _T("1")))
|
|
bNewValue = true;
|
|
|
|
pValue[0] = bNewValue;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 32-bit values support
|
|
|
|
int Text2Hex32(LPCTSTR szText, PDWORD pValue)
|
|
{
|
|
DWORD Value = 0;
|
|
int nDigit;
|
|
|
|
// Skip the C-style hexa prefix, if any
|
|
szText = SkipHexaPrefix(szText);
|
|
|
|
// Convert the value
|
|
while((nDigit = *szText++) != 0)
|
|
{
|
|
// If the character is not a hexa number, break
|
|
if(nDigit > 0x80 || CharToValue[nDigit] == 0xFF)
|
|
return ERROR_BAD_FORMAT;
|
|
|
|
// Convert to digit
|
|
nDigit = CharToValue[nDigit];
|
|
|
|
// Overflow check
|
|
if(Value & 0xF0000000)
|
|
return ERROR_ARITHMETIC_OVERFLOW;
|
|
Value = (Value << 0x04) + nDigit;
|
|
}
|
|
|
|
// Give the value to the caller
|
|
*pValue = Value;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
int DlgText2Hex32(HWND hDlg, UINT nIDCtrl, PDWORD pValue)
|
|
{
|
|
TCHAR szText[128];
|
|
HWND hWndChild = GetDlgItem(hDlg, nIDCtrl);
|
|
int nError = ERROR_INVALID_PARAMETER;
|
|
|
|
// Perform the conversion of the child text to a 32-bit hexa value
|
|
if(hWndChild != NULL)
|
|
{
|
|
// Retrieve the window text
|
|
GetWindowText(hWndChild, szText, _maxchars(szText));
|
|
|
|
// Attempt to convert the value
|
|
nError = Text2Hex32(szText, pValue);
|
|
if(nError != ERROR_SUCCESS)
|
|
ShowConversionError(hDlg, hWndChild);
|
|
}
|
|
|
|
return nError;
|
|
}
|
|
|
|
void Hex2Text32(LPTSTR szBuffer, DWORD Value)
|
|
{
|
|
Hex2TextXX(Value, szBuffer, sizeof(DWORD));
|
|
}
|
|
|
|
void Hex2DlgText32(HWND hDlg, UINT nIDCtrl, DWORD Value)
|
|
{
|
|
TCHAR szText[128];
|
|
|
|
Hex2TextXX(Value, szText, sizeof(DWORD));
|
|
SetDlgItemText(hDlg, nIDCtrl, szText);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Pointer support
|
|
|
|
int Text2HexPtr(LPCTSTR szText, PDWORD_PTR pValue)
|
|
{
|
|
DWORD_PTR ValueMask = ((DWORD_PTR)0x0F << ((sizeof(DWORD_PTR) * 8) - 4));
|
|
DWORD_PTR Value = 0;
|
|
int nDigit;
|
|
|
|
// Skip the C-style hexa prefix, if any
|
|
szText = SkipHexaPrefix(szText);
|
|
|
|
// Convert the value
|
|
while((nDigit = *szText++) != 0)
|
|
{
|
|
// If the character is not a hexa number, break
|
|
if(nDigit > 0x80 || CharToValue[nDigit] == 0xFF)
|
|
return ERROR_BAD_FORMAT;
|
|
|
|
// Convert to digit
|
|
nDigit = CharToValue[nDigit];
|
|
|
|
// Overflow check
|
|
if(Value & ValueMask)
|
|
return ERROR_ARITHMETIC_OVERFLOW;
|
|
Value = (Value << 0x04) + nDigit;
|
|
|
|
}
|
|
|
|
// Give the value to the caller
|
|
*pValue = Value;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
int DlgText2HexPtr(HWND hDlg, UINT nIDCtrl, PDWORD_PTR pValue)
|
|
{
|
|
TCHAR szText[128];
|
|
HWND hWndChild = GetDlgItem(hDlg, nIDCtrl);
|
|
int nError = ERROR_INVALID_PARAMETER;
|
|
|
|
// Perform the conversion of the child text to a 32-bit hexa value
|
|
if(hWndChild != NULL)
|
|
{
|
|
// Retrieve the window text
|
|
GetWindowText(hWndChild, szText, _maxchars(szText));
|
|
|
|
// Attempt to convert the value
|
|
nError = Text2HexPtr(szText, pValue);
|
|
if(nError != ERROR_SUCCESS)
|
|
ShowConversionError(hDlg, hWndChild);
|
|
}
|
|
|
|
return nError;
|
|
}
|
|
|
|
void Hex2TextPtr(LPTSTR szBuffer, DWORD_PTR Value)
|
|
{
|
|
Hex2TextXX(Value, szBuffer, sizeof(DWORD_PTR));
|
|
}
|
|
|
|
void Hex2DlgTextPtr(HWND hDlg, UINT nIDCtrl, DWORD_PTR Value)
|
|
{
|
|
TCHAR szText[128];
|
|
|
|
Hex2TextXX(Value, szText, sizeof(DWORD_PTR));
|
|
SetDlgItemText(hDlg, nIDCtrl, szText);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 64-bit values support
|
|
|
|
int Text2Hex64(LPCTSTR szText, PLONGLONG pValue)
|
|
{
|
|
ULONGLONG SaveValue = 0;
|
|
ULONGLONG Value = 0;
|
|
int nDigit;
|
|
|
|
// Skip the C-style hexa prefix, if any
|
|
szText = SkipHexaPrefix(szText);
|
|
|
|
// Convert the value
|
|
while((nDigit = *szText++) != 0)
|
|
{
|
|
// If the character is not a hexa number, break
|
|
if(nDigit > 0x80 || CharToValue[nDigit] == 0xFF)
|
|
return ERROR_BAD_FORMAT;
|
|
|
|
// Convert to digit
|
|
nDigit = CharToValue[nDigit];
|
|
|
|
// Move the value to the next rank and add the digit
|
|
SaveValue = Value;
|
|
Value = (Value << 0x04) + nDigit;
|
|
|
|
// Overflow check
|
|
if(Value < SaveValue)
|
|
return ERROR_ARITHMETIC_OVERFLOW;
|
|
}
|
|
|
|
// Give the value to the caller
|
|
*pValue = Value;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
int DlgText2Hex64(HWND hDlg, UINT nIDCtrl, PLONGLONG pValue)
|
|
{
|
|
TCHAR szText[128];
|
|
HWND hWndChild = GetDlgItem(hDlg, nIDCtrl);
|
|
int nError = ERROR_INVALID_PARAMETER;
|
|
|
|
// Perform the conversion of the child text to a 32-bit hexa value
|
|
if(hWndChild != NULL)
|
|
{
|
|
// Retrieve the window text
|
|
GetWindowText(hWndChild, szText, _maxchars(szText));
|
|
|
|
// Attempt to convert the value
|
|
nError = Text2Hex64(szText, pValue);
|
|
if(nError != ERROR_SUCCESS)
|
|
ShowConversionError(hDlg, hWndChild);
|
|
}
|
|
|
|
return nError;
|
|
}
|
|
|
|
void Hex2Text64(LPTSTR szBuffer, LONGLONG Value)
|
|
{
|
|
Hex2TextXX(Value, szBuffer, sizeof(LONGLONG));
|
|
}
|
|
|
|
void Hex2DlgText64(HWND hDlg, UINT nIDCtrl, LONGLONG Value)
|
|
{
|
|
TCHAR szText[128];
|
|
|
|
Hex2TextXX(Value, szText, sizeof(LONGLONG));
|
|
SetDlgItemText(hDlg, nIDCtrl, szText);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Path manipulation
|
|
|
|
LPTSTR FindDirectoryPathPart(LPTSTR szFullPath)
|
|
{
|
|
LPTSTR szPathPart = szFullPath;
|
|
|
|
// Skip the initial complicated parts
|
|
// \\.\GlobalRoot\Device\Mup\Dir1\Dir2
|
|
// \\.\Device\Mup\Dir1\Dir2
|
|
while(szPathPart[0] != 0 && !isalpha(szPathPart[0]))
|
|
szPathPart++;
|
|
|
|
// If found, search fuhrter for well-known path parts
|
|
if(szPathPart[0] != 0)
|
|
{
|
|
// Is it "GlobalRoot" ?
|
|
if(!_tcsnicmp(szPathPart, _T("GlobalRoot\\"), 11))
|
|
szPathPart += 11;
|
|
if(!_tcsnicmp(szPathPart, _T("Device\\"), 7))
|
|
szPathPart += 11;
|
|
|
|
// Skip the Drive letter, if present
|
|
if(isalpha(szPathPart[0]) && szPathPart[1] == _T(':'))
|
|
szPathPart += 2;
|
|
|
|
// Skip slashes and backslashes
|
|
while(szPathPart[0] == _T('\\') || szPathPart[0] == _T('/'))
|
|
szPathPart++;
|
|
|
|
// Return what we got
|
|
return szPathPart;
|
|
}
|
|
|
|
// Not recognized, return NULL
|
|
return NULL;
|
|
}
|
|
|
|
LPTSTR FindNextPathSeparator(LPTSTR szPathPart)
|
|
{
|
|
// Skip slashes and backslashes
|
|
while(szPathPart[0] == _T('\\') || szPathPart[0] == _T('/'))
|
|
szPathPart++;
|
|
|
|
// Find next slash, backslash or end of string
|
|
while(szPathPart[0] != 0 && szPathPart[0] != _T('\\') && szPathPart[0] != _T('/'))
|
|
szPathPart++;
|
|
|
|
return szPathPart;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Other
|
|
|
|
// Calculates the necessary length of PFILE_FULL_EA_INFORMATION structure
|
|
ULONG GetEaEntrySize(PFILE_FULL_EA_INFORMATION EaInfo)
|
|
{
|
|
ULONG EntrySize = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]);
|
|
|
|
//
|
|
// The length of the item must be calculated very exactly.
|
|
// No additional bytes (except for default alignment of
|
|
// FILE_FULL_EA_INFORMATION structure) is allowed.
|
|
//
|
|
|
|
EntrySize += EaInfo->EaNameLength + 1; // Add the name length plus the ending zero character
|
|
EntrySize += EaInfo->EaValueLength; // Add the data length
|
|
EntrySize = ALIGN_INT32(EntrySize); // Align to 4-byte boundary
|
|
|
|
return EntrySize;
|
|
}
|
|
|
|
DWORD TreeView_GetChildCount(HWND hTreeView, HTREEITEM hItem)
|
|
{
|
|
DWORD dwChildCount = 0;
|
|
|
|
// If both TreeView and item handle are valid
|
|
if(hTreeView != NULL && hItem != NULL)
|
|
{
|
|
// Count all children
|
|
hItem = TreeView_GetChild(hTreeView, hItem);
|
|
while(hItem != NULL)
|
|
{
|
|
dwChildCount++;
|
|
hItem = TreeView_GetNextSibling(hTreeView, hItem);
|
|
}
|
|
}
|
|
|
|
return dwChildCount;
|
|
}
|
|
|
|
LPARAM TreeView_GetItemParam(HWND hTreeView, HTREEITEM hItem)
|
|
{
|
|
TVITEM tvi;
|
|
|
|
// Retrieve the item param
|
|
tvi.mask = TVIF_PARAM;
|
|
tvi.hItem = hItem;
|
|
tvi.lParam = 0;
|
|
TreeView_GetItem(hTreeView, &tvi);
|
|
|
|
// Return the parameter
|
|
return tvi.lParam;
|
|
}
|
|
|
|
HTREEITEM TreeView_SetTreeItem(HWND hTreeView, HTREEITEM hItem, LPCTSTR szText, LPARAM lParam)
|
|
{
|
|
TVITEM tvi;
|
|
|
|
// Retrieve the item param
|
|
tvi.mask = TVIF_TEXT | TVIF_PARAM;
|
|
tvi.hItem = hItem;
|
|
tvi.lParam = lParam;
|
|
tvi.pszText = (LPTSTR)szText;
|
|
if(!TreeView_SetItem(hTreeView, &tvi))
|
|
hItem = NULL;
|
|
|
|
// Return the parameter
|
|
return hItem;
|
|
}
|
|
|
|
HTREEITEM InsertTreeItem(HWND hTreeView, HTREEITEM hParent, HTREEITEM hInsertAfter, LPCTSTR szText, PVOID pParam)
|
|
{
|
|
TVINSERTSTRUCT tvis;
|
|
|
|
// If NULL specified as InsertAfter, we insert it after the last item
|
|
if(hInsertAfter == NULL)
|
|
hInsertAfter = TVI_LAST;
|
|
|
|
// Insert the item to the tree
|
|
tvis.hParent = hParent;
|
|
tvis.hInsertAfter = hInsertAfter;
|
|
tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
|
|
tvis.item.pszText = (LPTSTR)szText;
|
|
tvis.item.lParam = (LPARAM)pParam;
|
|
return TreeView_InsertItem(hTreeView, &tvis);
|
|
}
|
|
|
|
HTREEITEM InsertTreeItem(HWND hTreeView, HTREEITEM hParent, LPCTSTR szText, PVOID pParam)
|
|
{
|
|
return InsertTreeItem(hTreeView, hParent, NULL, szText, pParam);
|
|
}
|
|
|
|
HTREEITEM InsertTreeItem(HWND hTreeView, HTREEITEM hParent, LPCTSTR szText, LPARAM lParam)
|
|
{
|
|
return InsertTreeItem(hTreeView, hParent, TVI_LAST, szText, (PVOID)lParam);
|
|
}
|
|
|
|
void TreeView_DeleteChildren(HWND hTreeView, HTREEITEM hParent)
|
|
{
|
|
HTREEITEM hItem;
|
|
|
|
// Remove all children, if any
|
|
while((hItem = TreeView_GetChild(hTreeView, hParent)) != NULL)
|
|
TreeView_DeleteItem(hTreeView, hItem);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Virtualization for the current process
|
|
|
|
BOOL GetTokenElevation(PBOOL pbElevated)
|
|
{
|
|
TOKEN_ELEVATION Elevation;
|
|
HANDLE hToken;
|
|
DWORD dwLength = 0;
|
|
DWORD dwValue = 0;
|
|
BOOL bResult = FALSE;
|
|
|
|
if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
|
{
|
|
// Step1: Query the elevation type
|
|
bResult = GetTokenInformation(hToken, TokenElevationType, &dwValue, sizeof(dwValue), &dwLength);
|
|
if(bResult)
|
|
{
|
|
switch((TOKEN_ELEVATION_TYPE)dwValue)
|
|
{
|
|
case TokenElevationTypeDefault:
|
|
bResult = GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &dwLength);
|
|
if(bResult)
|
|
pbElevated[0] = Elevation.TokenIsElevated ? TRUE : FALSE;
|
|
break;
|
|
|
|
case TokenElevationTypeFull:
|
|
pbElevated[0] = TRUE;
|
|
break;
|
|
|
|
case TokenElevationTypeLimited:
|
|
pbElevated[0] = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
CloseHandle(hToken);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL GetTokenVirtualizationEnabled(PBOOL pbEnabled)
|
|
{
|
|
HANDLE hToken;
|
|
DWORD dwEnabled = 0;
|
|
DWORD dwLength = 0;
|
|
BOOL bResult = FALSE;
|
|
|
|
if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
|
{
|
|
bResult = GetTokenInformation(hToken, TokenVirtualizationEnabled, &dwEnabled, sizeof(dwEnabled), &dwLength);
|
|
if(bResult && pbEnabled)
|
|
pbEnabled[0] = dwEnabled ? TRUE : FALSE;
|
|
|
|
CloseHandle(hToken);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL SetTokenVirtualizationEnabled(BOOL bEnabled)
|
|
{
|
|
HANDLE hToken;
|
|
DWORD dwEnabled = bEnabled;
|
|
BOOL bResult = FALSE;
|
|
|
|
if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT, &hToken))
|
|
{
|
|
bResult = SetTokenInformation(hToken,
|
|
TokenVirtualizationEnabled,
|
|
&dwEnabled,
|
|
sizeof(dwEnabled));
|
|
CloseHandle(hToken);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Set result status
|
|
|
|
static void DeleteBlinkTimer(HWND hDlg)
|
|
{
|
|
TFileTestData * pData = GetDialogData(hDlg);
|
|
|
|
// If there is already a blinker timer, destroy it
|
|
if(pData->BlinkTimer != 0)
|
|
KillTimer(hDlg, pData->BlinkTimer);
|
|
pData->BlinkTimer = 0;
|
|
|
|
// If there is a blink window already, destroy it
|
|
if(pData->hWndBlink != NULL)
|
|
DestroyWindow(pData->hWndBlink);
|
|
pData->hWndBlink = NULL;
|
|
}
|
|
|
|
static VOID CALLBACK BlinkTimerProc(HWND hDlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
|
|
{
|
|
UNREFERENCED_PARAMETER(dwTime);
|
|
UNREFERENCED_PARAMETER(uMsg);
|
|
|
|
if(idEvent == WM_TIMER_BLINK)
|
|
{
|
|
DeleteBlinkTimer(hDlg);
|
|
}
|
|
}
|
|
|
|
HWND AttachIconToEdit(HWND hDlg, HWND hWndChild, UINT nIDIcon)
|
|
{
|
|
HICON hIcon;
|
|
POINT pt;
|
|
HWND hWndBlink = NULL;
|
|
RECT rect;
|
|
|
|
hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(nIDIcon), IMAGE_ICON, 16, 16, LR_SHARED);
|
|
if(hIcon != NULL)
|
|
{
|
|
// Get the position of the edit box window
|
|
GetWindowRect(hWndChild, &rect);
|
|
pt.x = rect.left;
|
|
pt.y = rect.top;
|
|
ScreenToClient(hDlg, &pt);
|
|
pt.x = pt.x - 18;
|
|
pt.y = pt.y + (rect.bottom - rect.top - 16) / 2;
|
|
|
|
// Create the window for icon
|
|
hWndBlink = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
|
|
WC_STATIC,
|
|
NULL,
|
|
WS_CHILD | WS_VISIBLE | SS_ICON,
|
|
pt.x, pt.y, 18, 18,
|
|
hDlg,
|
|
NULL,
|
|
g_hInst,
|
|
NULL);
|
|
if(hWndBlink != NULL)
|
|
{
|
|
// Apply the icon to the icon window
|
|
SendMessage(hWndBlink, STM_SETICON, (WPARAM)hIcon, 0);
|
|
}
|
|
}
|
|
|
|
return hWndBlink;
|
|
}
|
|
|
|
static void CreateBlinkingIcon(HWND hDlg, HWND hWndChild, int nSeverity)
|
|
{
|
|
TFileTestData * pData = GetDialogData(hDlg);
|
|
HWND hWndBlink;
|
|
UINT IconSet[] = {IDI_ICON_INFORMATION, IDI_ICON_ERROR, IDI_ICON_WAIT};
|
|
|
|
// If there is already a blinker timer, destroy it
|
|
DeleteBlinkTimer(hDlg);
|
|
|
|
// Create the icon, left-attached to the edit field
|
|
hWndBlink = AttachIconToEdit(hDlg, hWndChild, IconSet[nSeverity]);
|
|
|
|
// Create timer for hiding the window
|
|
pData->BlinkTimer = SetTimer(hDlg, WM_TIMER_BLINK, 800, BlinkTimerProc);
|
|
pData->hWndBlink = hWndBlink;
|
|
}
|
|
|
|
void SetWindowEnumText(HWND hWndChild, LPCTSTR TextArray[], size_t nArraySize, size_t nIndex)
|
|
{
|
|
LPCTSTR szTextToSet = _T("");
|
|
|
|
// Sanity check
|
|
assert(hWndChild != NULL);
|
|
|
|
// Get the proper text message
|
|
if(nIndex < nArraySize)
|
|
szTextToSet = TextArray[nIndex];
|
|
|
|
// Set the text message
|
|
SetWindowText(hWndChild, szTextToSet);
|
|
}
|
|
|
|
void SetResultInfo(HWND hDlg, NTSTATUS Status, HANDLE hHandle, UINT_PTR ResultLength, PLARGE_INTEGER pResultLength)
|
|
{
|
|
LPCTSTR szStatus;
|
|
LPTSTR szError;
|
|
TCHAR szText[256] = _T("");
|
|
HWND hWndChild;
|
|
int nSeverity = SEVERITY_SUCCESS;
|
|
BOOL bEnable = TRUE;
|
|
|
|
LPCTSTR szNtCreateResult[] =
|
|
{
|
|
_T("FILE_SUPERSEDED"),
|
|
_T("FILE_OPENED"),
|
|
_T("FILE_CREATED"),
|
|
_T("FILE_OVERWRITTEN"),
|
|
_T("FILE_EXISTS"),
|
|
_T("FILE_DOES_NOT_EXIST"),
|
|
_T("0x00000006"),
|
|
_T("FILE_OPLOCK_BROKEN_TO_LEVEL_2"),
|
|
_T("FILE_OPLOCK_BROKEN_TO_NONE"),
|
|
_T("FILE_OPBATCH_BREAK_UNDERWAY")
|
|
};
|
|
|
|
// Set the text for NTSTATUS
|
|
hWndChild = GetDlgItem(hDlg, IDC_RESULT_STATUS);
|
|
if(hWndChild != NULL)
|
|
{
|
|
switch(Status)
|
|
{
|
|
case STATUS_PENDING:
|
|
szStatus = NtStatus2Text(Status);
|
|
nSeverity = SEVERITY_PENDING;
|
|
break;
|
|
|
|
case STATUS_INVALID_DATA_FORMAT:
|
|
szStatus = _T("The entered value has bad format.");
|
|
nSeverity = SEVERITY_ERROR;
|
|
break;
|
|
|
|
case STATUS_CANNOT_EDIT_THIS:
|
|
szStatus = _T("This item is not editable.");
|
|
nSeverity = SEVERITY_ERROR;
|
|
break;
|
|
|
|
case STATUS_FILE_ID_CONVERSION:
|
|
szStatus = _T("Skipped conversion of File ID to an NT name.");
|
|
nSeverity = SEVERITY_SUCCESS;
|
|
break;
|
|
|
|
case STATUS_COPIED_TO_CLIPBOARD:
|
|
szStatus = _T("Item value copied to clipboard.");
|
|
nSeverity = SEVERITY_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
szStatus = NtStatus2Text(Status);
|
|
nSeverity = NT_SUCCESS(Status) ? SEVERITY_SUCCESS : SEVERITY_ERROR;
|
|
break;
|
|
}
|
|
|
|
SetWindowText(hWndChild, szStatus);
|
|
}
|
|
|
|
// Set the text for GetLastError() result
|
|
hWndChild = GetDlgItem(hDlg, IDC_LAST_ERROR);
|
|
if(hWndChild != NULL)
|
|
{
|
|
switch(Status)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
nSeverity = SEVERITY_SUCCESS;
|
|
break;
|
|
|
|
case ERROR_IO_PENDING:
|
|
nSeverity = SEVERITY_PENDING;
|
|
break;
|
|
|
|
default:
|
|
nSeverity = SEVERITY_ERROR;
|
|
break;
|
|
}
|
|
|
|
// Format the result string
|
|
szError = GetErrorText(Status);
|
|
if(szError != NULL)
|
|
{
|
|
StringCchPrintf(szText, _countof(szText), _T("(0x%08lX) %s"), Status, szError);
|
|
SetWindowText(hWndChild, szText);
|
|
delete [] szError;
|
|
}
|
|
}
|
|
|
|
// Set the handle, if present
|
|
hWndChild = GetDlgItem(hDlg, IDC_HANDLE);
|
|
if(hWndChild != NULL)
|
|
{
|
|
if(hHandle == INVALID_HANDLE_VALUE)
|
|
SetWindowText(hWndChild, _T("INVALID_HANDLE_VALUE"));
|
|
else if(hHandle == NULL)
|
|
SetWindowText(hWndChild, _T("NULL"));
|
|
else
|
|
Hex2DlgTextPtr(hDlg, IDC_HANDLE, (DWORD_PTR)hHandle);
|
|
}
|
|
|
|
// Enable/disable the "CloseHandle" button, if present
|
|
hWndChild = GetDlgItem(hDlg, IDC_CLOSE_HANDLE);
|
|
if(hWndChild != NULL)
|
|
{
|
|
if(IsHandleInvalid(hHandle))
|
|
bEnable = FALSE;
|
|
EnableWindow(hWndChild, bEnable);
|
|
}
|
|
|
|
// Enable/disable the hint for close handle, if any
|
|
hWndChild = GetDlgItem(hDlg, IDC_CLOSE_HANDLE_HINT);
|
|
if(hWndChild != NULL)
|
|
{
|
|
if(IsHandleInvalid(hHandle))
|
|
bEnable = FALSE;
|
|
EnableWindow(hWndChild, bEnable);
|
|
}
|
|
|
|
// Set the extra length, if present
|
|
hWndChild = GetDlgItem(hDlg, IDC_IOSTATUS_INFO);
|
|
if(hWndChild != NULL)
|
|
{
|
|
if(pResultLength != NULL)
|
|
{
|
|
Hex2Text64(szText, (ULONGLONG)pResultLength->QuadPart);
|
|
SetWindowText(hWndChild, szText);
|
|
}
|
|
else
|
|
{
|
|
Hex2Text32(szText, (DWORD)ResultLength);
|
|
SetWindowText(hWndChild, szText);
|
|
}
|
|
}
|
|
|
|
// Set the result of NtCreate, if present
|
|
hWndChild = GetDlgItem(hDlg, IDC_NTCREATE_RESULT);
|
|
if(hWndChild != NULL && NT_SUCCESS(Status))
|
|
SetWindowEnumText(hWndChild, szNtCreateResult, _countof(szNtCreateResult), (size_t)ResultLength);
|
|
|
|
// Blink the icon on LastError, if any
|
|
hWndChild = GetDlgItem(hDlg, IDC_LAST_ERROR);
|
|
if(hWndChild != NULL)
|
|
{
|
|
CreateBlinkingIcon(hDlg, hWndChild, nSeverity);
|
|
}
|
|
|
|
// Blink the icon on status, if any
|
|
hWndChild = GetDlgItem(hDlg, IDC_RESULT_STATUS);
|
|
if(hWndChild != NULL)
|
|
{
|
|
CreateBlinkingIcon(hDlg, hWndChild, nSeverity);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Dynamic loaded APIs
|
|
|
|
RTLGETCURRENTTRANSACTION pfnRtlGetCurrentTransaction = NULL;
|
|
RTLSETCURRENTTRANSACTION pfnRtlSetCurrentTransaction = NULL;
|
|
CREATETRANSACTION pfnCreateTransaction = NULL;
|
|
COMMITTRANSACTION pfnCommitTransaction = NULL;
|
|
ROLLBACKTRANSACTION pfnRollbackTransaction = NULL;
|
|
CREATEDIRTRANSACTED pfnCreateDirectoryTransacted = NULL;
|
|
CREATEFILETRANSACTED pfnCreateFileTransacted = NULL;
|
|
CREATEHARDLINK pfnCreateHardLink = NULL;
|
|
ADDMANDATORYACE pfnAddMandatoryAce = NULL;
|
|
|
|
static HINSTANCE hNtdll = NULL;
|
|
static HINSTANCE hKernel32 = NULL;
|
|
static HINSTANCE hAdvapi32 = NULL;
|
|
static HINSTANCE hKtmw32 = NULL;
|
|
|
|
void ResolveDynamicLoadedAPIs()
|
|
{
|
|
// Get imports from Ntdll.dll
|
|
if(hNtdll == NULL)
|
|
{
|
|
hNtdll = LoadLibrary(_T("Ntdll.dll"));
|
|
if(hNtdll != NULL)
|
|
{
|
|
pfnRtlGetCurrentTransaction = (RTLGETCURRENTTRANSACTION)
|
|
GetProcAddress(hNtdll, "RtlGetCurrentTransaction");
|
|
pfnRtlSetCurrentTransaction = (RTLSETCURRENTTRANSACTION)
|
|
GetProcAddress(hNtdll, "RtlSetCurrentTransaction");
|
|
}
|
|
}
|
|
|
|
// Get imports from Kernel32.dll
|
|
if(hKernel32 == NULL)
|
|
{
|
|
hKernel32 = LoadLibrary(_T("Kernel32.dll"));
|
|
if(hKernel32 != NULL)
|
|
{
|
|
#ifdef _UNICODE
|
|
pfnCreateDirectoryTransacted = (CREATEDIRTRANSACTED)GetProcAddress(hKernel32, "CreateDirectoryTransactedW");
|
|
pfnCreateFileTransacted = (CREATEFILETRANSACTED)GetProcAddress(hKernel32, "CreateFileTransactedW");
|
|
pfnCreateHardLink = (CREATEHARDLINK)GetProcAddress(hKernel32, "CreateHardLinkW");
|
|
#else
|
|
pfnCreateDirectoryTransacted = (CREATEDIRTRANSACTED)GetProcAddress(hKernel32, "CreateDirectoryTransactedA");
|
|
pfnCreateFileTransacted = (CREATEFILETRANSACTED)GetProcAddress(hKernel32, "CreateFileTransactedA");
|
|
pfnCreateHardLink = (CREATEHARDLINK)GetProcAddress(hKernel32, "CreateHardLinkA");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Get imports from Advapi32.dll
|
|
if(hAdvapi32 == NULL)
|
|
{
|
|
hAdvapi32 = LoadLibrary(_T("Advapi32.dll"));
|
|
if(hAdvapi32 != NULL)
|
|
{
|
|
pfnAddMandatoryAce = (ADDMANDATORYACE)GetProcAddress(hAdvapi32, "AddMandatoryAce");
|
|
}
|
|
}
|
|
|
|
// Get imports from Ktmw32.dll
|
|
if(hKtmw32 == NULL)
|
|
{
|
|
hKtmw32 = LoadLibrary(_T("Ktmw32.dll"));
|
|
if(hKtmw32 != NULL)
|
|
{
|
|
pfnCreateTransaction = (CREATETRANSACTION)GetProcAddress(hKtmw32, "CreateTransaction");
|
|
pfnCommitTransaction = (COMMITTRANSACTION)GetProcAddress(hKtmw32, "CommitTransaction");
|
|
pfnRollbackTransaction = (ROLLBACKTRANSACTION)GetProcAddress(hKtmw32, "RollbackTransaction");
|
|
}
|
|
}
|
|
}
|
|
|
|
void UnloadDynamicLoadedAPIs()
|
|
{
|
|
if(hKtmw32 != NULL)
|
|
{
|
|
pfnCreateTransaction = NULL;
|
|
pfnCommitTransaction = NULL;
|
|
pfnRollbackTransaction = NULL;
|
|
|
|
FreeLibrary(hKtmw32);
|
|
hKtmw32 = NULL;
|
|
}
|
|
|
|
if(hAdvapi32 == NULL)
|
|
{
|
|
pfnAddMandatoryAce = NULL;
|
|
|
|
FreeLibrary(hAdvapi32);
|
|
hAdvapi32 = NULL;
|
|
}
|
|
|
|
if(hKernel32 != NULL)
|
|
{
|
|
pfnCreateFileTransacted = NULL;
|
|
|
|
FreeLibrary(hKernel32);
|
|
hKernel32 = NULL;
|
|
}
|
|
|
|
if(hNtdll != NULL)
|
|
{
|
|
pfnRtlGetCurrentTransaction = NULL;
|
|
pfnRtlSetCurrentTransaction = NULL;
|
|
|
|
FreeLibrary(hNtdll);
|
|
hNtdll = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Obtaining NT name from a name, either DOS or NT
|
|
|
|
static LPCWSTR szGlobalRootMaskW = L"\\\\.\\GlobalRoot";
|
|
|
|
typedef enum _PATH_TYPE
|
|
{
|
|
PathTypeNative, // Full local path in native format ("\??\")
|
|
PathTypeNativePrefix, // Full local path prefixed with native prefix ("\\?\" or "\\.\")
|
|
PathTypeLocalFull, // Full local path, including drive letter ("C:\Windows\...")
|
|
PathTypeLocalRoot, // Local root of the current drive ("\Windows\...")
|
|
PathTypeLocalRelative, // Relative path ("Windows\...")
|
|
PathTypeNetwork, // Network path ("\\Server\Share\...")
|
|
PathTypeUnknown
|
|
} PATH_TYPE, *PPATH_TYPE;
|
|
|
|
static PATH_TYPE GetDosPathType(LPCWSTR szFileNameW)
|
|
{
|
|
// Does it begin with backslash character?
|
|
if(szFileNameW[0] == L'\\')
|
|
{
|
|
// Check for native-like win32 prefixes and network share paths
|
|
if(szFileNameW[1] == L'\\')
|
|
{
|
|
// "\\?\" or "\\.\"
|
|
if(szFileNameW[2] == '?' && szFileNameW[3] == '\\')
|
|
return PathTypeNativePrefix;
|
|
if(szFileNameW[2] == '.' && szFileNameW[3] == '\\')
|
|
return PathTypeNativePrefix;
|
|
|
|
// Network paths begin with "\\"
|
|
return PathTypeNetwork;
|
|
}
|
|
|
|
// Check for native path ([refixed with "\??\")
|
|
if(szFileNameW[1] == L'?' && szFileNameW[2] == L'?' && szFileNameW[3] == L'\\')
|
|
return PathTypeNative;
|
|
|
|
// Anything else that begins with "\\" is considered a local root path
|
|
return PathTypeLocalRoot;
|
|
}
|
|
else
|
|
{
|
|
// Check for local DOS paths ("C:\Path\File.ext")
|
|
// Note: Do not use "isalpha()" function, because it could fail on exotic characters
|
|
// (and also ASSERTs in debug builds)
|
|
if(('A' <= szFileNameW[0] && szFileNameW[0] <= 'Z') || ('a' <= szFileNameW[0] && szFileNameW[0] <= 'z'))
|
|
{
|
|
if(szFileNameW[1] == ':' && szFileNameW[2] == '\\')
|
|
return PathTypeLocalFull;
|
|
}
|
|
|
|
// Anything else that does not begin with "\\" is considered a local relative path
|
|
return PathTypeLocalRelative;
|
|
}
|
|
}
|
|
|
|
size_t IsGlobalRootPrefix(LPCWSTR szFileNameW)
|
|
{
|
|
// If the path name is a Win32 native-like prefix
|
|
if(GetDosPathType(szFileNameW) == PathTypeNativePrefix)
|
|
{
|
|
// Skip the native prefix
|
|
szFileNameW += 4;
|
|
|
|
// Check for "GlobalRoot".
|
|
// Note: Return length without the backslash after "GlobalRoot"
|
|
if(!_wcsnicmp(szFileNameW, L"GlobalRoot\\", 11))
|
|
return 4 + 10;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOLEAN IsNativeName(LPCWSTR szFileName)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjAttr;
|
|
UNICODE_STRING FileName;
|
|
UNICODE_STRING TempName;
|
|
PATH_TYPE PathType;
|
|
NTSTATUS Status;
|
|
HANDLE Handle;
|
|
|
|
// Determine the path type
|
|
PathType = GetDosPathType(szFileName);
|
|
if(PathType == PathTypeNative)
|
|
return TRUE;
|
|
|
|
// If the path begins with backslash, determine if it really is an NT name,
|
|
// and for example not local root path (e.g. "\Systemroot" is an NT name,
|
|
// but "\Windows" it a local path name)
|
|
if(PathType == PathTypeLocalRoot)
|
|
{
|
|
// Prepare the object attributes
|
|
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
// Find the next path part
|
|
FileName.MaximumLength =
|
|
FileName.Length = 0;
|
|
FileName.Buffer = (PWSTR)szFileName;
|
|
szFileName++;
|
|
|
|
// Go until we find the second backslash
|
|
while(szFileName[0] != 0 && szFileName[0] != L'\\')
|
|
szFileName++;
|
|
|
|
// Set the length to the UNICODE_STRING
|
|
FileName.MaximumLength =
|
|
FileName.Length = (USHORT)((szFileName - FileName.Buffer) * sizeof(WCHAR));
|
|
|
|
// Note: "\Windows" matches both FS directory and NT namespace directory
|
|
RtlInitUnicodeString(&TempName, L"\\Windows");
|
|
if(RtlCompareUnicodeString(&FileName, &TempName, TRUE) == 0)
|
|
return FALSE;
|
|
|
|
// Attempt to open the symlink (Example: "\Systemroot")
|
|
Status = NtOpenSymbolicLinkObject(&Handle, SYMBOLIC_LINK_QUERY, &ObjAttr);
|
|
if(NT_SUCCESS(Status))
|
|
{
|
|
NtClose(Handle);
|
|
return TRUE;
|
|
}
|
|
|
|
// Try to open a directory (Example: "\Device")
|
|
Status = NtOpenDirectoryObject(&Handle, DIRECTORY_TRAVERSE, &ObjAttr);
|
|
if(NT_SUCCESS(Status))
|
|
{
|
|
NtClose(Handle);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Replacement for RtlDosPathNameToNtPathName_U
|
|
BOOLEAN Win32PathNameToNtPathName(PUNICODE_STRING FileName, LPCWSTR szFileNameW)
|
|
{
|
|
LPCWSTR szNtPrefix = NULL;
|
|
LPWSTR szCurrentDir = NULL;
|
|
// LPWSTR szBufferEnd;
|
|
// LPWSTR szBuffer;
|
|
size_t cbFileName = 0;
|
|
size_t cbNtPrefix = 0;
|
|
|
|
// Determine the path type
|
|
switch(GetDosPathType(szFileNameW))
|
|
{
|
|
// DOS native-like paths ("\\?\C:\" and "\\.\C:\") - need to replace prefix
|
|
case PathTypeNativePrefix:
|
|
szNtPrefix = L"\\??\\";
|
|
cbNtPrefix = 8;
|
|
szFileNameW += 4;
|
|
break;
|
|
|
|
// Local full paths need to be prefixed with "\??\"
|
|
case PathTypeLocalFull:
|
|
szNtPrefix = L"\\??\\";
|
|
cbNtPrefix = 8;
|
|
break;
|
|
|
|
// Local root paths need to be prefixed with the current drive
|
|
// case PathTypeLocalRoot:
|
|
// szCurrentDir = Win32PathGetCurrentDir(FALSE);
|
|
// cbNtPrefix = wcslen(szCurrentDir) * sizeof(WCHAR);
|
|
// szNtPrefix = szCurrentDir;
|
|
// break;
|
|
|
|
// Local relative paths need to be prefixed with the current directory
|
|
// case PathTypeLocalRelative:
|
|
// szCurrentDir = Win32PathGetCurrentDir(TRUE);
|
|
// cbNtPrefix = wcslen(szCurrentDir) * sizeof(WCHAR);
|
|
// szNtPrefix = szCurrentDir;
|
|
// break;
|
|
|
|
// Network share paths need to be prefixed with "\??\UNC\"
|
|
case PathTypeNetwork:
|
|
szNtPrefix = L"\\??\\UNC\\";
|
|
cbNtPrefix = 16;
|
|
szFileNameW += 2;
|
|
break;
|
|
}
|
|
|
|
// Do not try to convert the name if it would be too long
|
|
cbFileName = wcslen(szFileNameW) * sizeof(WCHAR);
|
|
if((cbNtPrefix + cbFileName) >= 0xFFFE)
|
|
return FALSE;
|
|
|
|
// Allocate buffer for the path name
|
|
FileName->MaximumLength = (USHORT)(cbNtPrefix + cbFileName + sizeof(WCHAR));
|
|
FileName->Length = (USHORT)(cbNtPrefix + cbFileName);
|
|
FileName->Buffer = (PWSTR)RtlAllocateHeap(RtlProcessHeap(), 0, FileName->MaximumLength);
|
|
if(FileName->Buffer == NULL)
|
|
return FALSE;
|
|
|
|
// Copy the prefix, if any
|
|
if(szNtPrefix != NULL && cbNtPrefix != 0)
|
|
memcpy(FileName->Buffer, szNtPrefix, cbNtPrefix);
|
|
|
|
// Copy the name itself. Also terminate the name with zero
|
|
// (i.e. the same like RtlDosPathNameToNtPathName_U)
|
|
memcpy(FileName->Buffer + (cbNtPrefix / sizeof(WCHAR)), szFileNameW, cbFileName);
|
|
FileName->Buffer[FileName->Length / sizeof(WCHAR)] = 0;
|
|
|
|
// Change slashes to backslashes
|
|
// szBufferEnd = FileName->Buffer + (FileName->Length / sizeof(WCHAR));
|
|
// for(szBuffer = FileName->Buffer; szBuffer < szBufferEnd; szBuffer++)
|
|
// szBuffer[0] = (szBuffer[0] == L'/') ? L'\\' : szBuffer[0];
|
|
|
|
// Free buffers and exit
|
|
delete [] szCurrentDir;
|
|
return TRUE;
|
|
}
|
|
|
|
// Converts file name to UNICODE_STRING
|
|
// Both DOS names and native NT names are processed properly
|
|
// Buffer in "FileName" needs to be freed using FreeFileNameString(&FileName)
|
|
NTSTATUS FileNameToUnicodeString(
|
|
PUNICODE_STRING FileName,
|
|
LPCTSTR szFileName)
|
|
{
|
|
UNICODE_STRING UnicodeName = {0, 0, NULL};
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN Result;
|
|
PWSTR szFileNameW = NULL;
|
|
|
|
// Set the FileName to empty UNICODE_STRING
|
|
FileName->MaximumLength = 0;
|
|
FileName->Length = 0;
|
|
FileName->Buffer = NULL;
|
|
|
|
// Convert the file name to Unicode, if needed
|
|
#ifndef UNICODE
|
|
// CreateUnicodeStringFromAsciiz produces zero-terminated UNICODE_STRING
|
|
Status = RtlCreateUnicodeStringFromAsciiz(&UnicodeName, szFileName);
|
|
if(!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
szFileNameW = UnicodeName.Buffer;
|
|
#else
|
|
UNREFERENCED_PARAMETER(UnicodeName);
|
|
szFileNameW = (PWSTR)szFileName;
|
|
#endif
|
|
|
|
//
|
|
// Several problems of RtlDosPathNameToNtPathName_U
|
|
// ================================================
|
|
//
|
|
// 1) Names longer than about 0x200 characters are not correctly processed
|
|
// 2) If the name ends with space, that space is cut
|
|
// 3) Generally works unpredicably for the following path types:
|
|
//
|
|
// - \??\C:\Dir\File.ext - doesn't work on WinNT 4.0
|
|
// - \\?\C:\Dir\File.ext - works OK on WinNT 4.0
|
|
// - \\Server\Share\Dir\File.ext - works OK on WinNT 4.0
|
|
// - \Device\LanmanRedirector\Server\Share\... - doesn't work on WinNT 4.0
|
|
// - \SystemRoot\System32\Kernel32.dll - doesn't work on WinNT 4.0
|
|
// - \??\UNC\Server\Share\Dir\File.ext - doesn't work on WinNT 4.0
|
|
//
|
|
// From the above reasons, we won't use the RtlDosPathNameToNtPathName_U.
|
|
//
|
|
|
|
// Convert Win32 name using GlobalRoot to native name
|
|
// ("\\?\GlobalRoot\Device\xxxx" to "\Device\xxxx")
|
|
szFileNameW += IsGlobalRootPrefix(szFileNameW);
|
|
|
|
// If it's not an NT path name yet, convert it to an NT name.
|
|
if(IsNativeName(szFileNameW) == FALSE)
|
|
{
|
|
// Use our own method for converting the DOS path name to NT name
|
|
// Result = RtlDosPathNameToNtPathName_U(szFileNameW, FileName, NULL, NULL);
|
|
Result = Win32PathNameToNtPathName(FileName, szFileNameW);
|
|
if(!Result || FileName->Length == 0)
|
|
{
|
|
Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!RtlCreateUnicodeString(FileName, szFileNameW))
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// Free the allocated unicode string with DOS name
|
|
RtlFreeUnicodeString(&UnicodeName);
|
|
return Status;
|
|
}
|
|
|
|
void FreeFileNameString(PUNICODE_STRING FileName)
|
|
{
|
|
if(FileName != NULL)
|
|
{
|
|
if(FileName->Buffer != NULL)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, FileName->Buffer);
|
|
FileName->Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
NTSTATUS ConvertToNtName(HWND hDlg, UINT nIDEdit)
|
|
{
|
|
UNICODE_STRING NtName = {0};
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HWND hEdit = GetDlgItem(hDlg, nIDEdit);
|
|
LPTSTR szFileName = NULL;
|
|
int nLength = GetWindowTextLength(hEdit);
|
|
|
|
// Only do something if the text length is > 0)
|
|
if(nLength > 0)
|
|
{
|
|
// Allocate buffer
|
|
szFileName = new TCHAR[nLength + 1];
|
|
if(szFileName != NULL)
|
|
{
|
|
// Get the window text
|
|
// If it already appears to be an NT name, do nothing
|
|
GetWindowText(hEdit, szFileName, nLength + 1);
|
|
|
|
// Don't convert file ID
|
|
if(nIDEdit == IDC_FILE_NAME && IsPossibleFileId(szFileName))
|
|
{
|
|
SetResultInfo(hDlg, STATUS_FILE_ID_CONVERSION);
|
|
}
|
|
else
|
|
{
|
|
// Convert the name to UNICODE_STRING
|
|
Status = FileNameToUnicodeString(&NtName, szFileName);
|
|
if(NT_SUCCESS(Status))
|
|
{
|
|
SetWindowText(hEdit, NtName.Buffer);
|
|
FreeFileNameString(&NtName);
|
|
}
|
|
}
|
|
|
|
delete [] szFileName;
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
int ConvertToWin32Name(HWND hDlg, UINT nIDEdit)
|
|
{
|
|
LPTSTR szNewFileName;
|
|
LPTSTR szFileName;
|
|
HWND hWndEdit = GetDlgItem(hDlg, nIDEdit);
|
|
size_t nLength = GetWindowTextLength(hWndEdit);
|
|
|
|
// Only do something if the text length is > 0)
|
|
if(nLength > 0)
|
|
{
|
|
// Allocate buffer
|
|
szFileName = new TCHAR[nLength + 1];
|
|
if(szFileName != NULL)
|
|
{
|
|
// Get the window text
|
|
// If it already appears to be an Win32 name, do nothing
|
|
GetWindowText(hWndEdit, szFileName, (int)(nLength + 1));
|
|
|
|
// The "\??\Path" case
|
|
if(!_tcsnicmp(szFileName, _T("\\??\\"), 4))
|
|
{
|
|
// If it looks like the \??\C:\..., just skip the initial NT prefix
|
|
if(szFileName[4] != 0 && szFileName[5] == _T(':') && szFileName[6] == _T('\\'))
|
|
{
|
|
SetWindowText(hWndEdit, szFileName + 4);
|
|
}
|
|
|
|
// Network paths: Remove the "\??\UNC\"
|
|
else if(!_tcsnicmp(szFileName, _T("\\??\\UNC\\"), 8))
|
|
{
|
|
szFileName[6] = _T('\\');
|
|
SetWindowText(hWndEdit, szFileName + 6);
|
|
}
|
|
|
|
// Otherwise, replace "\??\" with "\\.\"
|
|
else
|
|
{
|
|
szFileName[1] = _T('\\');
|
|
szFileName[2] = _T('.');
|
|
SetWindowText(hWndEdit, szFileName);
|
|
}
|
|
}
|
|
|
|
// The "\Device\XXX case can still be solved on WinXP or newer
|
|
// The "\Systemroot case can still be solved on WinXP or newer
|
|
else if(g_dwWinVer >= 0x0501 && IsNativeName(szFileName))
|
|
{
|
|
nLength = nLength + wcslen(szGlobalRootMaskW) + 1;
|
|
szNewFileName = new TCHAR[nLength];
|
|
if(szNewFileName != NULL)
|
|
{
|
|
StringCchPrintf(szNewFileName, nLength, _T("%s%s"), szGlobalRootMaskW, szFileName);
|
|
SetWindowText(hWndEdit, szNewFileName);
|
|
delete [] szNewFileName;
|
|
}
|
|
}
|
|
|
|
// Leave the name be
|
|
delete [] szFileName;
|
|
}
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Conversion flags to the text value
|
|
|
|
LPTSTR FlagsToString(TFlagInfo * pFlags, LPTSTR szBuffer, size_t cchBuffer, DWORD dwFlags, bool bNewLineSeparated)
|
|
{
|
|
LPCTSTR szSeparator = (bNewLineSeparated == false) ? _T(" | ") : _T(" |\r\n");
|
|
LPTSTR szSaveBuffer = szBuffer;
|
|
LPTSTR szBufferEnd = szBuffer + cchBuffer;
|
|
|
|
// As long as we are not at the end, or as long as there are some flags
|
|
while(pFlags->szFlagText && dwFlags)
|
|
{
|
|
// Is that flag set?
|
|
if(IS_FLAG_SET(pFlags, dwFlags))
|
|
{
|
|
// Append the appropriate separator
|
|
if(szBuffer > szSaveBuffer)
|
|
StringCchCopyEx(szBuffer, (szBufferEnd - szBuffer), szSeparator, &szBuffer, NULL, 0);
|
|
|
|
// Append the flag
|
|
StringCchCopyEx(szBuffer, (szBufferEnd - szBuffer), pFlags->szFlagText, &szBuffer, NULL, 0);
|
|
|
|
// Clear the flag from the flags list
|
|
dwFlags &= ~pFlags->dwValue;
|
|
}
|
|
|
|
// Move to the next flag
|
|
pFlags++;
|
|
}
|
|
|
|
// Are there some flags left?
|
|
if(dwFlags != 0)
|
|
{
|
|
// Append the appropriate separator and the hexa value
|
|
if(szBuffer > szSaveBuffer)
|
|
StringCchCopyEx(szBuffer, (szBufferEnd - szBuffer), szSeparator, &szBuffer, NULL, 0);
|
|
StringCchPrintfEx(szBuffer, (szBufferEnd - szBuffer), &szBuffer, NULL, 0, _T("%08X"), dwFlags);
|
|
}
|
|
|
|
// If there is nothing, append simple zero
|
|
if(szBuffer == szSaveBuffer && (szBuffer + 1) < szBufferEnd)
|
|
{
|
|
*szBuffer++ = _T('0');
|
|
*szBuffer = 0;
|
|
}
|
|
|
|
return szSaveBuffer;
|
|
}
|
|
|
|
LPTSTR NamedValueToString(TFlagInfo * pFlags, LPTSTR szBuffer, size_t cchBuffer, LPCTSTR szFormat, DWORD dwFlags)
|
|
{
|
|
LPTSTR szSaveBuffer = szBuffer;
|
|
LPTSTR szBufferEnd = szBuffer + cchBuffer;
|
|
|
|
// Print the format and value
|
|
StringCchPrintfEx(szBuffer, cchBuffer, &szBuffer, NULL, 0, szFormat, dwFlags);
|
|
|
|
// Format the flags as user-friendly value
|
|
if(dwFlags != 0)
|
|
FlagsToString(pFlags, szBuffer, (szBufferEnd - szBuffer), dwFlags, false);
|
|
|
|
// Return the start of the buffer
|
|
return szSaveBuffer;
|
|
}
|
|
|
|
LPTSTR GuidValueToString(LPTSTR szBuffer, size_t cchBuffer, LPCTSTR szFormat, LPGUID PtrGuid)
|
|
{
|
|
LPTSTR szSaveBuffer = szBuffer;
|
|
TCHAR szGuidText[0x40];
|
|
|
|
GuidToString(PtrGuid, szGuidText, _countof(szGuidText));
|
|
StringCchPrintf(szBuffer, cchBuffer, szFormat, szGuidText);
|
|
return szSaveBuffer;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// File ID and object ID support
|
|
|
|
void FileIDToString(TFileTestData * pData, ULONGLONG FileId, LPTSTR szBuffer)
|
|
{
|
|
PATH_TYPE PathType;
|
|
LPTSTR szPath = (pData->szDirName[0] != 0) ? pData->szDirName : pData->szFileName1;
|
|
|
|
// Skip the NT prefix, if any
|
|
PathType = GetDosPathType(szPath);
|
|
if(PathType == PathTypeNative || PathType == PathTypeNativePrefix)
|
|
szPath += 4;
|
|
|
|
// Supply the disk name
|
|
if(isalpha(szPath[0]) && szPath[1] == _T(':') && szPath[2] == _T('\\'))
|
|
{
|
|
*szBuffer++ = *szPath++;
|
|
*szBuffer++ = *szPath++;
|
|
*szBuffer++ = *szPath++;
|
|
}
|
|
|
|
Hex2Text64(szBuffer, FileId);
|
|
}
|
|
|
|
void ObjectIDToString(PBYTE pbObjId, LPCTSTR szFileName, LPTSTR szObjectID)
|
|
{
|
|
// Supply the disk name
|
|
if(szFileName != NULL)
|
|
{
|
|
if(isalpha(szFileName[0]) && szFileName[1] == _T(':') && szFileName[2] == _T('\\'))
|
|
{
|
|
*szObjectID++ = *szFileName++;
|
|
*szObjectID++ = *szFileName++;
|
|
*szObjectID++ = *szFileName++;
|
|
}
|
|
}
|
|
|
|
// Format the object ID
|
|
for(int i = 0; i < 0x10; i++)
|
|
{
|
|
BYTE OneByte = *pbObjId++;
|
|
|
|
*szObjectID++ = ValueToChar[OneByte >> 0x04];
|
|
*szObjectID++ = ValueToChar[OneByte & 0x0F];
|
|
}
|
|
|
|
*szObjectID = 0;
|
|
}
|
|
|
|
int StringToFileID(
|
|
LPCTSTR szFileOrObjId,
|
|
LPTSTR szVolume,
|
|
PVOID pvFileObjId,
|
|
PDWORD pLength)
|
|
{
|
|
PBYTE pbObjectID = (PBYTE)pvFileObjId;
|
|
DWORD dwLength = 0;
|
|
TCHAR OneChar;
|
|
BYTE OneByte;
|
|
int nError = ERROR_SUCCESS;
|
|
|
|
// Is there drive letter?
|
|
if(isalpha(szFileOrObjId[0]) && szFileOrObjId[1] == _T(':') && szFileOrObjId[2] == _T('\\'))
|
|
{
|
|
if(szVolume != NULL)
|
|
{
|
|
szVolume[0] = szFileOrObjId[0];
|
|
szVolume[1] = szFileOrObjId[1];
|
|
szVolume[2] = szFileOrObjId[2];
|
|
szVolume[3] = 0;
|
|
}
|
|
szFileOrObjId += 3;
|
|
}
|
|
|
|
//
|
|
// TODO: Is there a device name?
|
|
//
|
|
|
|
// Perform conversion based of length of the string
|
|
switch(_tcslen(szFileOrObjId))
|
|
{
|
|
// Is the rest a file ID? (16 characters)
|
|
case 0x10:
|
|
dwLength = sizeof(ULONGLONG);
|
|
nError = Text2Hex64(szFileOrObjId, (PLONGLONG)pvFileObjId);
|
|
break;
|
|
|
|
case 0x20:
|
|
for(int i = 0; i < 0x10; i++)
|
|
{
|
|
if(szFileOrObjId[0] > 0x80 || szFileOrObjId[1] > 0x80)
|
|
{
|
|
nError = ERROR_BAD_FORMAT;
|
|
break;
|
|
}
|
|
|
|
// Convert the first character
|
|
OneChar = *szFileOrObjId++;
|
|
if(CharToValue[OneChar] == 0xFF)
|
|
{
|
|
nError = ERROR_BAD_FORMAT;
|
|
break;
|
|
}
|
|
OneByte = CharToValue[OneChar] << 0x04;
|
|
|
|
// Convert the second character
|
|
OneChar = *szFileOrObjId++;
|
|
if(CharToValue[OneChar] == 0xFF)
|
|
{
|
|
nError = ERROR_BAD_FORMAT;
|
|
break;
|
|
}
|
|
OneByte |= CharToValue[OneChar];
|
|
*pbObjectID++ = OneByte;
|
|
}
|
|
dwLength = 0x10;
|
|
break;
|
|
|
|
default:
|
|
nError = ERROR_BAD_FORMAT;
|
|
break;
|
|
}
|
|
|
|
// Give the output length
|
|
if(nError == ERROR_SUCCESS && pLength != NULL)
|
|
*pLength = dwLength;
|
|
return nError;
|
|
}
|
|
|
|
HMENU FindContextMenu(UINT nIDMenu)
|
|
{
|
|
// Search the pre-loaded menu array
|
|
for(size_t i = 0; i < MAX_CONTEXT_MENUS; i++)
|
|
{
|
|
if(g_ContextMenus[i].szMenuName == MAKEINTRESOURCE(nIDMenu))
|
|
return g_ContextMenus[i].hMenu;
|
|
}
|
|
|
|
// Chould never happen, but anyway
|
|
assert(false);
|
|
return LoadMenu(g_hInst, MAKEINTRESOURCE(nIDMenu));
|
|
}
|
|
|
|
int ExecuteContextMenu(HWND hWndParent, HMENU hMainMenu, LPARAM lParam)
|
|
{
|
|
HMENU hSubMenu = GetSubMenu(hMainMenu, 0);
|
|
POINT pt;
|
|
|
|
if(hSubMenu != NULL)
|
|
{
|
|
// If activated by a key, get the left-top window position
|
|
if(lParam == 0xFFFFFFFF)
|
|
{
|
|
pt.x = pt.y = 5;
|
|
ClientToScreen(hWndParent, &pt);
|
|
}
|
|
else
|
|
{
|
|
pt.x = GET_X_LPARAM(lParam);
|
|
pt.y = GET_Y_LPARAM(lParam);
|
|
}
|
|
|
|
// Set the window to foreground due to capture mouse events
|
|
SetForegroundWindow(hWndParent);
|
|
TrackPopupMenu(hSubMenu, (TPM_LEFTBUTTON | TPM_RIGHTBUTTON), pt.x, pt.y, 0, hWndParent, NULL);
|
|
PostMessage(hWndParent, WM_NULL, 0, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int ExecuteContextMenuForDlgItem(HWND hWndParent, HMENU hMainMenu, UINT nIDCtrl)
|
|
{
|
|
LPARAM lParam;
|
|
HWND hWndChild = GetDlgItem(hWndParent, nIDCtrl);
|
|
RECT rect;
|
|
|
|
// Calculate position of the menu
|
|
GetWindowRect(hWndChild, &rect);
|
|
lParam = MAKELPARAM(rect.left, rect.bottom);
|
|
|
|
// Execute the context menu
|
|
return ExecuteContextMenu(hWndParent, hMainMenu, lParam);
|
|
}
|
|
|
|
NTSTATUS NtDeleteReparsePoint(HANDLE ObjectHandle)
|
|
{
|
|
PREPARSE_DATA_BUFFER pReparseData = NULL;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
ULONGLONG ReparseBuffer[0x100]; // ULONGLONG makes sure it's aligned to 8
|
|
NTSTATUS Status;
|
|
ULONG Length = sizeof(ReparseBuffer);
|
|
|
|
// Query the reparse point
|
|
pReparseData = (PREPARSE_DATA_BUFFER)ReparseBuffer;
|
|
Status = NtFsControlFile(ObjectHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
FSCTL_GET_REPARSE_POINT,
|
|
NULL,
|
|
0,
|
|
pReparseData,
|
|
Length);
|
|
|
|
// ... and delete it
|
|
if(NT_SUCCESS(Status))
|
|
{
|
|
pReparseData->ReparseDataLength = 0;
|
|
Status = NtFsControlFile(ObjectHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
FSCTL_DELETE_REPARSE_POINT,
|
|
pReparseData,
|
|
REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,
|
|
NULL,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
if(Status == STATUS_NOT_A_REPARSE_POINT)
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS NtDeleteReparsePoint(POBJECT_ATTRIBUTES PtrObjectAttributes)
|
|
{
|
|
IO_STATUS_BLOCK IoStatus;
|
|
NTSTATUS Status;
|
|
HANDLE FileHandle = NULL;
|
|
|
|
// Open the reparse point
|
|
Status = NtOpenFile(&FileHandle,
|
|
FILE_WRITE_ATTRIBUTES,
|
|
PtrObjectAttributes,
|
|
&IoStatus,
|
|
0,
|
|
FILE_OPEN_REPARSE_POINT);
|
|
if(NT_SUCCESS(Status))
|
|
{
|
|
Status = NtDeleteReparsePoint(FileHandle);
|
|
NtClose(FileHandle);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local functions - Mandatory label ACE
|
|
|
|
BOOL WINAPI MyAddMandatoryAce(PACL pAcl, DWORD dwAceRevision, DWORD dwAceFlags, DWORD MandatoryPolicy, PSID pSid)
|
|
{
|
|
if(pfnAddMandatoryAce == NULL)
|
|
return FALSE;
|
|
|
|
// Call the function
|
|
return pfnAddMandatoryAce(pAcl, dwAceRevision, dwAceFlags, MandatoryPolicy, pSid);
|
|
}
|
|
|
|
// TODO:
|
|
// AddAccessAllowedObjectAce
|
|
// AddAccessDeniedObjectAce
|