Files
2024-12-29 19:01:43 +01:00

647 lines
22 KiB
C++

/*****************************************************************************/
/* Page03ReadWrite.cpp Copyright (c) Ladislav Zezula 2004 */
/*---------------------------------------------------------------------------*/
/* This module handles both "ReadFile" and "WriteFile" pages */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 06.03.04 1.00 Lad The first version of Page03ReadWrite.cpp */
/*****************************************************************************/
#include "FileTest.h"
#include "resource.h"
//-----------------------------------------------------------------------------
// Local defines
#define INITIAL_DATA_BUFFER_SIZE 0x10000
typedef NTSTATUS (NTAPI * NTREADWRITE)(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset OPTIONAL,
IN PULONG Key OPTIONAL
);
typedef BOOL (WINAPI * READWRITE)(
IN HANDLE hFile,
IN LPVOID lpBuffer,
IN DWORD nNumberOfBytesToRead,
OUT LPDWORD lpNumberOfBytesRead,
IN LPOVERLAPPED lpOverlapped
);
//-----------------------------------------------------------------------------
// Helper functions
static void CompleteAsynchronousOperation(HWND hDlg, TApcEntry * pApc, DWORD dwErrCode, DWORD dwTransferred)
{
TFileTestData * pData = GetDialogData(hDlg);
HWND hWndEditor;
// Convert the error code and number of bytes transferred
if(pApc->bAsynchronousCompletion)
{
if(pApc->bHasIoStatus == false)
{
if(!GetOverlappedResult(pData->hFile, &pApc->Overlapped, &dwTransferred, TRUE))
dwErrCode = GetLastError();
else
dwErrCode = ERROR_SUCCESS;
}
else
{
dwErrCode = RtlNtStatusToDosError(pApc->IoStatus.Status);
dwTransferred = (DWORD)pApc->IoStatus.Information;
}
}
// If the operation has succeeded, update few UI elements
if(dwErrCode == ERROR_SUCCESS)
{
// If the "increase position" is checked, increment the byte position
if(pApc->bIncrementPosition)
{
pApc->ByteOffset.QuadPart += dwTransferred;
Hex2DlgText64(hDlg, IDC_BYTE_OFFSET, pApc->ByteOffset.QuadPart);
}
// If we have an output buffer, copy it to the data buffer
if(pApc->BufferLength)
{
// Copy the data buffer from the APC to our user buffer
pData->RdWrData.SetLength(pApc->BufferLength);
memcpy(pData->RdWrData.pbData, (pApc + 1), pData->RdWrData.cbData);
hWndEditor = GetDlgItem(hDlg, IDC_FILE_DATA);
DataEditor_SetData(hWndEditor, 0, pData->RdWrData.pbData, pData->RdWrData.cbData);
}
}
// Set the information about the operation
if(pApc->bHasIoStatus)
{
SetResultInfo(hDlg, pApc->UserParam, pApc->IoStatus.Status, &pApc->IoStatus);
}
else
{
SetResultInfo(hDlg, pApc->UserParam, dwErrCode, dwTransferred);
}
// Free the APC entry
FreeApcEntry(pApc);
}
//-----------------------------------------------------------------------------
// Message handlers
static TAnchors * pAnchors = NULL;
static int OnInitDialog(HWND hDlg, LPARAM lParam)
{
PROPSHEETPAGE * pPage = (PROPSHEETPAGE *)lParam;
TFileTestData * pData = (TFileTestData *)pPage->lParam;
HWND hWndChild;
SetDialogData(hDlg, pPage->lParam);
// Configure dialog resizing
if(pData->bEnableResizing)
{
pAnchors = new TAnchors(hDlg);
pAnchors->AddAnchor(hDlg, IDC_BYTE_OFFSET_TITLE, akLeft | akTop | akRightCenter);
pAnchors->AddAnchor(hDlg, IDC_BYTE_OFFSET, akLeft | akTop | akRightCenter);
pAnchors->AddAnchor(hDlg, IDC_BYTE_OFFSET_SPIN, akTop | akRightCenter);
pAnchors->AddAnchor(hDlg, IDC_LENGTH_TITLE, akLeftCenter | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_LENGTH, akLeftCenter | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_LENGTH_SPIN, akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_INCREASE_FILEPOS, akLeft | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_READ_FILE, akLeft | akTop);
pAnchors->AddAnchor(hDlg, IDC_WRITE_FILE, akTop | akRightCenter);
pAnchors->AddAnchor(hDlg, IDC_NTREAD_FILE, akLeftCenter | akTop);
pAnchors->AddAnchor(hDlg, IDC_NTWRITE_FILE, akRight | akTop);
pAnchors->AddAnchor(hDlg, IDC_LOCK_FILE, akLeft | akTop);
pAnchors->AddAnchor(hDlg, IDC_UNLOCK_FILE, akTop | akRightCenter);
pAnchors->AddAnchor(hDlg, IDC_NTLOCK_FILE, akLeftCenter | akTop);
pAnchors->AddAnchor(hDlg, IDC_NTUNLOCK_FILE, akRight | akTop);
pAnchors->AddAnchor(hDlg, IDC_FILL_DATA_MENU, akLeft | akTop);
pAnchors->AddAnchor(hDlg, IDC_GET_FILE_SIZE, akTop | akRightCenter);
pAnchors->AddAnchor(hDlg, IDC_SET_FILE_POINTER, akLeftCenter | akTop);
pAnchors->AddAnchor(hDlg, IDC_SET_END_OF_FILE, akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_FILE_DATA, akAll);
pAnchors->AddAnchor(hDlg, IDC_RESULT_FRAME, akLeft | akRight | akBottom);
pAnchors->AddAnchor(hDlg, IDC_ERROR_CODE_TITLE, akLeft | akBottom);
pAnchors->AddAnchor(hDlg, IDC_ERROR_CODE, akLeft | akRight | akBottom);
pAnchors->AddAnchor(hDlg, IDC_INFORMATION_TITLE, akLeft | akBottom);
pAnchors->AddAnchor(hDlg, IDC_INFORMATION, akLeft | akRight | akBottom);
}
// If there's field for data buffer, sets it to 8 bytes per line
hWndChild = GetDlgItem(hDlg, IDC_FILE_DATA);
if(hWndChild != NULL)
DataEditor_SetDataFormat(hWndChild, PtrPointer32Bit, 0x08);
// Set initial values for file position and data
Hex2DlgText64(hDlg, IDC_BYTE_OFFSET, 0);
Hex2DlgText32(hDlg, IDC_LENGTH, INITIAL_DATA_BUFFER_SIZE);
return TRUE;
}
static int OnSetActive(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
BOOL bEnable = FALSE;
if(IsHandleValid(pData->hFile))
bEnable = TRUE;
EnableDlgItems(hDlg, bEnable, IDC_READ_FILE,
IDC_WRITE_FILE,
IDC_NTREAD_FILE,
IDC_NTWRITE_FILE,
IDC_LOCK_FILE,
IDC_UNLOCK_FILE,
IDC_NTLOCK_FILE,
IDC_NTUNLOCK_FILE,
IDC_GET_FILE_SIZE,
IDC_SET_FILE_POINTER,
IDC_SET_END_OF_FILE,
0);
return TRUE;
}
static int OnUpdateByteOffset(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
LONGLONG BaseAddress = 0;
HWND hWndChild;
// Convert the byte offset
if(DlgText2Hex64(hDlg, IDC_BYTE_OFFSET, &BaseAddress) == ERROR_SUCCESS)
{
if((hWndChild = GetDlgItem(hDlg, IDC_FILE_DATA)) != NULL)
{
DataEditor_SetData(hWndChild, BaseAddress, pData->RdWrData.pbData, pData->RdWrData.cbData);
}
}
return TRUE;
}
static int UpdateLength(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
DWORD DataLength = 0;
HWND hWndChild;
// Convert the length
if(DlgText2Hex32(hDlg, IDC_LENGTH, &DataLength) == ERROR_SUCCESS)
{
if((hWndChild = GetDlgItem(hDlg, IDC_FILE_DATA)) != NULL)
{
pData->RdWrData.SetLength(DataLength);
DataEditor_SetData(hWndChild, 0, pData->RdWrData.pbData, pData->RdWrData.cbData);
}
}
return TRUE;
}
static int OnFillData(HWND hDlg, UINT FillPattern)
{
TFileTestData * pData = GetDialogData(hDlg);
LPCSTR WritePattern = "BAADF00D";
HWND hWndChild;
// If the new pattern is zero, it means to fill the same pattern like before
if(FillPattern == 0)
FillPattern = pData->FillPattern;
switch(FillPattern)
{
case IDC_FILL_DATA_ZEROS:
memset(pData->RdWrData.pbData, 0, pData->RdWrData.cbData);
break;
case IDC_FILL_DATA_PATTERN:
for(ULONG i = 0; i < pData->RdWrData.cbData; i++)
pData->RdWrData.pbData[i] = WritePattern[i % 8];
break;
case IDC_FILL_DATA_RANDOM:
srand(GetTickCount());
for(ULONG i = 0; i < pData->RdWrData.cbData; i++)
pData->RdWrData.pbData[i] = (BYTE)(rand() % 0x100);
break;
}
// Remember the current pattern
pData->FillPattern = FillPattern;
// Apply the new file data
if((hWndChild = GetDlgItem(hDlg, IDC_FILE_DATA)) != NULL)
{
DataEditor_SetData(hWndChild, 0, pData->RdWrData.pbData, pData->RdWrData.cbData);
}
return ERROR_SUCCESS;
}
static int OnDeltaPos(HWND hDlg, NMUPDOWN * pNMUpDown)
{
LONGLONG ByteOffset = 0;
DWORD Length = 0;
if(pNMUpDown->hdr.idFrom == IDC_BYTE_OFFSET_SPIN)
{
DlgText2Hex64(hDlg, IDC_BYTE_OFFSET, &ByteOffset);
ByteOffset -= pNMUpDown->iDelta * 0x10000;
if(ByteOffset < 0)
ByteOffset = 0;
Hex2DlgText64(hDlg, IDC_BYTE_OFFSET, ByteOffset);
}
if(pNMUpDown->hdr.idFrom == IDC_LENGTH_SPIN)
{
DlgText2Hex32(hDlg, IDC_LENGTH, &Length);
Length -= pNMUpDown->iDelta * 0x1000;
if((int)Length < 0)
Length = 0;
Hex2DlgText32(hDlg, IDC_LENGTH, Length);
}
return TRUE;
}
static int OnDataPaste(HWND hDlg, PDTE_PASTE_DATA pPasteData)
{
// Ask the user whether to cut the data or not
if(MessageBoxRc(hDlg, IDS_QUESTION, IDS_WANT_TRIM_DATA) == IDYES)
{
TFileTestData * pData = GetDialogData(hDlg);
SIZE_T nLength = strlen(pPasteData->szPasteText);
// Paste the data to the view
if(pData->RdWrData.SetLength(pPasteData->PasteOffset + nLength) == ERROR_SUCCESS)
{
memcpy(pData->RdWrData.pbData + pPasteData->PasteOffset, pPasteData->szPasteText, nLength);
DataEditor_SetData(GetDlgItem(hDlg, IDC_FILE_DATA), 0, pData->RdWrData.pbData, pData->RdWrData.cbData);
Hex2DlgText32(hDlg, IDC_LENGTH, (DWORD)(pPasteData->PasteOffset + nLength));
}
// Mark the request as handled, so the data editor stops fuhrter processing
pPasteData->bHandled = TRUE;
}
return TRUE;
}
static int OnReadWriteFile(HWND hDlg, int nIDCtrl)
{
TFileTestData * pData = GetDialogData(hDlg);
LARGE_INTEGER ByteOffset = {0};
NTREADWRITE NtReadWrite;
READWRITE ReadWrite;
TApcEntry * pApc;
NTSTATUS Status;
LPBYTE DataBuffer;
LPBYTE ApcBuffer;
DWORD dwTransferred = 0;
DWORD OutputLength = 0;
DWORD dwErrCode = ERROR_SUCCESS;
DWORD Length = 0;
// Get the start position
DlgText2Hex64(hDlg, IDC_BYTE_OFFSET, &ByteOffset.QuadPart);
DlgText2Hex32(hDlg, IDC_LENGTH, &Length);
assert(pData->RdWrData.cbData >= Length);
// For read operation, we have data in the APC
if(nIDCtrl == IDC_READ_FILE || nIDCtrl == IDC_NTREAD_FILE)
OutputLength = Length;
// Create new APC entry
pApc = CreateApcEntry(pData, APC_TYPE_READ_WRITE, OutputLength);
if(pApc != NULL)
{
// Remember the state of "Increment position"
pApc->ByteOffset = ByteOffset;
pApc->bIncrementPosition = (IsDlgButtonChecked(hDlg, IDC_INCREASE_FILEPOS) == BST_CHECKED);
pApc->BufferLength = OutputLength;
ApcBuffer = (LPBYTE)(pApc + 1);
// Prepare the OVERLAPPED structure
pApc->Overlapped.OffsetHigh = ByteOffset.HighPart;
pApc->Overlapped.Offset = ByteOffset.LowPart;
// Perform the appropriate API
switch(nIDCtrl)
{
case IDC_READ_FILE:
case IDC_WRITE_FILE:
// Set the data buffer and appropriate API function
pApc->UserParam = (nIDCtrl == IDC_READ_FILE) ? (RSI_LAST_ERROR | RSI_READ) : (RSI_LAST_ERROR | RSI_WRITTEN);
DataBuffer = (nIDCtrl == IDC_READ_FILE) ? ApcBuffer : pData->RdWrData.pbData;
ReadWrite = (nIDCtrl == IDC_READ_FILE) ? ReadFile : (READWRITE)WriteFile;
// Perform the read operation using ReadFile
if(!ReadWrite(pData->hFile, DataBuffer, Length, &dwTransferred, &pApc->Overlapped))
dwErrCode = GetLastError();
// If the read operation ended with ERROR_IO_PENDING, queue the APC
if(dwErrCode == ERROR_IO_PENDING)
{
SetResultInfo(hDlg, pApc->UserParam, ERROR_IO_PENDING, 0);
InsertApcEntry(pData, pApc);
return TRUE;
}
break;
case IDC_NTREAD_FILE:
case IDC_NTWRITE_FILE:
// Set the data buffer and appropriate API function
pApc->UserParam = RSI_NTSTATUS | RSI_INFORMATION;
DataBuffer = (nIDCtrl == IDC_NTREAD_FILE) ? ApcBuffer : pData->RdWrData.pbData;
NtReadWrite = (nIDCtrl == IDC_NTREAD_FILE) ? NtReadFile : NtWriteFile;
pApc->bHasIoStatus = TRUE;
// Perform the read/write operation
Status = NtReadWrite(pData->hFile,
pApc->hEvent,
NULL,
NULL,
&pApc->IoStatus,
DataBuffer,
Length,
&ByteOffset,
NULL);
// If the read operation ended with STATUS_PENDING, queue the APC
if(Status == STATUS_PENDING)
{
SetResultInfo(hDlg, pApc->UserParam, STATUS_PENDING, &pApc->IoStatus);
InsertApcEntry(pData, pApc);
return TRUE;
}
pApc->IoStatus.Status = Status;
break;
}
// Complete the read/write operation
CompleteAsynchronousOperation(hDlg, pApc, dwErrCode, dwTransferred);
}
else
{
SetResultInfo(hDlg, RSI_LAST_ERROR | RSI_NOINFO, GetLastError());
}
return TRUE;
}
static int OnLockUnlockFile(HWND hDlg, int nReadWriteType)
{
TFileTestData * pData = GetDialogData(hDlg);
LARGE_INTEGER ByteOffset = {0};
LARGE_INTEGER LockLength = {0};
TApcEntry * pApc;
NTSTATUS Status;
DWORD dwErrCode = ERROR_SUCCESS;
// Get the start position
DlgText2Hex64(hDlg, IDC_BYTE_OFFSET, &ByteOffset.QuadPart);
DlgText2Hex32(hDlg, IDC_LENGTH, &LockLength.LowPart);
assert((LONGLONG)pData->RdWrData.cbData >= LockLength.QuadPart);
// Create new APC entry
pApc = CreateApcEntry(pData, APC_TYPE_LOCK_UNLOCK);
if(pApc != NULL)
{
// Perform the I/O operation
switch(nReadWriteType)
{
case IDC_LOCK_FILE:
// Prepare flags for completion
pApc->UserParam = RSI_LAST_ERROR | RSI_NOINFO;
// Perform the lock operation using LockFile
if(!LockFile(pData->hFile, ByteOffset.LowPart, ByteOffset.HighPart, LockLength.LowPart, LockLength.HighPart))
dwErrCode = GetLastError();
break;
case IDC_UNLOCK_FILE:
// Prepare flags for completion
pApc->UserParam = RSI_LAST_ERROR | RSI_NOINFO;
// Perform the unlock operation using UnlockFile
if(!UnlockFile(pData->hFile, ByteOffset.LowPart, ByteOffset.HighPart, LockLength.LowPart, LockLength.HighPart))
dwErrCode = GetLastError();
break;
case IDC_NTLOCK_FILE:
// Remember that this is a native call
pApc->UserParam = RSI_NTSTATUS | RSI_NOINFO;
pApc->bHasIoStatus = TRUE;
// Perform the lock operation using NtLockFile
Status = NtLockFile(pData->hFile,
pApc->hEvent,
NULL,
NULL,
&pApc->IoStatus,
&ByteOffset,
&LockLength,
0,
TRUE,
TRUE);
// If the lock operation ended with STATUS_PENDING, queue the APC
if(Status == STATUS_PENDING)
{
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_NOINFO, STATUS_PENDING);
InsertApcEntry(pData, pApc);
return TRUE;
}
// Save the error
pApc->IoStatus.Status = Status;
break;
case IDC_NTUNLOCK_FILE:
// Remember that this is a native call
pApc->UserParam = RSI_NTSTATUS | RSI_NOINFO;
pApc->bHasIoStatus = TRUE;
// Perform the unlock operation using NtUnlockFile
Status = NtUnlockFile(pData->hFile,
&pApc->IoStatus,
&ByteOffset,
&LockLength,
0);
// Save the error
assert(Status != STATUS_PENDING);
pApc->IoStatus.Status = Status;
break;
}
// Set the information about the operation
CompleteAsynchronousOperation(hDlg, pApc, dwErrCode, 0);
}
else
{
SetResultInfo(hDlg, RSI_LAST_ERROR | RSI_NOINFO, GetLastError());
}
return TRUE;
}
static int OnGetFileSizeClick(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
LARGE_INTEGER FileSize = {0};
DWORD dwErrCode = ERROR_SUCCESS;
// Get the file size
SetLastError(ERROR_SUCCESS);
FileSize.LowPart = GetFileSize(pData->hFile, (LPDWORD)&FileSize.HighPart);
dwErrCode = GetLastError();
SetResultInfo(hDlg, RSI_LAST_ERROR | RSI_FILESIZE, dwErrCode, &FileSize);
return TRUE;
}
static int OnSetFilePointerClick(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
LARGE_INTEGER ByteOffset;
DWORD dwErrCode = ERROR_SUCCESS;
// Get the file offset from the dialog
DlgText2Hex64(hDlg, IDC_BYTE_OFFSET, &ByteOffset.QuadPart);
// Apply the file size
SetLastError(ERROR_SUCCESS);
ByteOffset.LowPart = SetFilePointer(pData->hFile, ByteOffset.LowPart, &ByteOffset.HighPart, FILE_BEGIN);
dwErrCode = GetLastError();
SetResultInfo(hDlg, RSI_LAST_ERROR | RSI_FILEPOS, dwErrCode, &ByteOffset);
return TRUE;
}
static int OnSetEndOfFileClick(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
DWORD dwErrCode = ERROR_SUCCESS;
if(!SetEndOfFile(pData->hFile))
dwErrCode = GetLastError();
SetResultInfo(hDlg, RSI_LAST_ERROR | RSI_NOINFO, dwErrCode);
return TRUE;
}
static int OnCommand(HWND hDlg, UINT nNotify, UINT nIDCtrl)
{
if(nNotify == BN_CLICKED)
{
switch(nIDCtrl)
{
case IDC_READ_FILE:
case IDC_WRITE_FILE:
case IDC_NTREAD_FILE:
case IDC_NTWRITE_FILE:
return OnReadWriteFile(hDlg, nIDCtrl);
case IDC_LOCK_FILE:
case IDC_UNLOCK_FILE:
case IDC_NTLOCK_FILE:
case IDC_NTUNLOCK_FILE:
return OnLockUnlockFile(hDlg, nIDCtrl);
case IDC_FILL_DATA_MENU:
return ExecuteContextMenuForDlgItem(hDlg, FindContextMenu(IDR_FILL_DATA_MENU), IDC_FILL_DATA_MENU);
case IDC_FILL_DATA_ZEROS:
case IDC_FILL_DATA_PATTERN:
case IDC_FILL_DATA_RANDOM:
return OnFillData(hDlg, nIDCtrl);
case IDC_GET_FILE_SIZE:
return OnGetFileSizeClick(hDlg);
case IDC_SET_FILE_POINTER:
return OnSetFilePointerClick(hDlg);
case IDC_SET_END_OF_FILE:
return OnSetEndOfFileClick(hDlg);
}
}
if(nNotify == EN_CHANGE)
{
if(nIDCtrl == IDC_BYTE_OFFSET)
return OnUpdateByteOffset(hDlg);
if(nIDCtrl == IDC_LENGTH)
UpdateLength(hDlg);
}
return FALSE;
}
static int OnNotify(HWND hDlg, NMHDR * pNMHDR)
{
switch(pNMHDR->code)
{
case PSN_SETACTIVE:
return OnSetActive(hDlg);
case UDN_DELTAPOS:
return OnDeltaPos(hDlg, (NMUPDOWN *)pNMHDR);
case DEN_PASTE:
return OnDataPaste(hDlg, (PDTE_PASTE_DATA)pNMHDR);
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Public functions
INT_PTR CALLBACK PageProc03(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
return OnInitDialog(hDlg, lParam);
case WM_SIZE:
return pAnchors->OnMessage(uMsg, wParam, lParam);
case WM_APC:
CompleteAsynchronousOperation(hDlg, (TApcEntry *)lParam, 0, 0);
return TRUE;
case WM_COMMAND:
return OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam));
case WM_NOTIFY:
return OnNotify(hDlg, (NMHDR *)lParam);
case WM_DESTROY:
if(pAnchors != NULL)
delete pAnchors;
pAnchors = NULL;
return FALSE;
}
return FALSE;
}