Files
ladislav-zezula-FileTest/Utils.cpp
T
Zezula Ladislav fbf207fb2b + Added support for CreateHardLink
+ Deleting directory tree improved: Now uses Win10's FILE_DISPOSITION_INFORMATION_EX if necessary.
2017-11-07 15:24:14 +01:00

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