I am new in programming, especially in Windows System Programming and I 'm reading a relevant book. Currently I 'm playing arround with GetFileSizeEx, SetFilePointer and SetFilePointerEx in order to get the file size of a file.
I have created this code that works until line 65 where I can't get SetFilePointerEx to work to get the size.
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#define BUFF_SIZE 0x100
// program to test file size
int _tmain(DWORD argc, LPTSTR argv[])
{
HANDLE hIn;
HANDLE hOut;
LARGE_INTEGER liSize;
LONG lSize, lDistance = 0;
TCHAR szMsgGetFile[BUFF_SIZE];
TCHAR szMsgSetFile[BUFF_SIZE];
DWORD nIn;
LARGE_INTEGER liPt;
PLARGE_INTEGER pLi;
pLi = &liPt;
SecureZeroMemory(&liSize, sizeof(LARGE_INTEGER));
SecureZeroMemory(&pLi, sizeof(LARGE_INTEGER));
SecureZeroMemory(szMsgGetFile, _tcslen(szMsgGetFile));
SecureZeroMemory(szMsgSetFile, _tcslen(szMsgSetFile));
//get input and output handles
hIn = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hIn == INVALID_HANDLE_VALUE)
_tprintf(_T("[ERROR] CreateFile to get file input handle failed. Error code %d.\n"), GetLastError());
hOut = CreateFile(_T("CONOUT$"), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hOut == INVALID_HANDLE_VALUE)
_tprintf(_T("[ERROR] CreateFile to get file output handle failed. Error code %d.\n"), GetLastError());
//get the size of the file with GetFileSizeEx, acquired from hIn that is argv1
if (!GetFileSizeEx(hIn, &liSize))
_tprintf(_T("[ERROR] GetFileSizeEx failed. Error code %d\n"), GetLastError());
//get the size of the file with SetFilePointer
//You can obtain the file length by specifying a zero-length move from the end of
//file, although the file pointer is changed as a side effect
lSize = SetFilePointer(hIn, lDistance, NULL, FILE_END);
if (lSize == INVALID_SET_FILE_POINTER)
_tprintf(_T("[ERROR] SetFilePointer failed. Error code %d\n"), GetLastError());
//output the size with WriteConsole (and sprintf)
//and with _tprintf. Notice the usage of the liSize LARGE_INTEGER
_stprintf_s(szMsgGetFile, BUFF_SIZE, "[*] GetFileSizeEx (WriteConsole): The size is %I64d Bytes.\n", liSize.QuadPart);
if (!WriteConsole(hOut, szMsgGetFile, _tcslen(szMsgGetFile), &nIn, NULL))
_tprintf(_T("[ERROR] WriteConsole failed. Error code %d\n"), GetLastError());
_tprintf(_T("[*] GetFileSizeEx (tprintf): The size is %I64d Bytes.\n"), liSize.QuadPart);
//output the size with WriteConsole (and sprintf)
//and _tprintf
_stprintf_s(szMsgSetFile, BUFF_SIZE, "[*] SetFilePointer (WriteConsole): The size is %ld Bytes.\n", lSize);
if (!WriteConsole(hOut, szMsgSetFile, _tcslen(szMsgSetFile), &nIn, NULL))
_tprintf(_T("[ERROR] WriteConsole failed. Error code %d\n"), GetLastError());
_tprintf(_T("[*] SetFilePointer (tprintf): The size is %ld Bytes.\n"), lSize);
//get the size of the file with SetFilePointerEx
//Determine a file’s size by positioning 0 bytes from the end and using the file
//pointer value returned by SetFilePointerEx.
SecureZeroMemory(&liPt, sizeof(LARGE_INTEGER));
SetFilePointerEx(hIn, liPt, pLi, FILE_END);
_tprintf(_T("[*] SetFilePointerEx: %lld Bytes.\n"), pLi->QuadPart);
return 0;
}
MSDN says that
You can use SetFilePointerEx to determine the length of a file. To do this, use FILE_END for dwMoveMethod and seek to location zero. The file offset returned is the length of the file.
However, SetFilePointerEx is of type BOOL. The "Windows System Programming" book says that the "Determine a file’s size by positioning 0 bytes from the end and using the file pointer value returned by SetFilePointerEx.". I am guessing that this parameter is the _Out_opt_ PLARGE_INTEGER lpNewFilePointer according to MSDN.
I would like help on how to get the file size of the file by using SetFilePointerEx.
You have a number of errors in your code. Here's an example of SetFilePointerEx that works. In general, Win32 functions don't allocate the memory to store their output (some do). It's up to the caller to allocate memory. In this case, the memory for the output of SetFilePointerEx is allocated on the stack by declaring size2 to be a LARGE_INTEGER. A pointer to that LARGE_INTEGER is then provided to SetFilePointerEx.
auto hIn = CreateFile(_T("C:\\foo"), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
/* check for errors */
LARGE_INTEGER size;
GetFileSizeEx(hIn, &size);
/* check for errors */
LARGE_INTEGER size2;
LARGE_INTEGER offset;
ZeroMemory(&offset, sizeof offset);
SetFilePointerEx(hIn, offset, &size2, FILE_END);
/* check for errors */
Optionally, DWORD dwFileSize = GetFileSize(hFile, NULL); can get the size of the file opened by HANDLE hFile = CreateFileA/W(...);.
Related
I am writing a small program to communicate with a USB to UART bridge (CP210x).
The program is fairly simple, but when ever I decrease the size of the outbound message array below (wm_size < 25) I get a segmentation fault.
The code yielding the segmentation fault looks as follows:
HANDLE prog_com_port;
prog_com_port = CreateFileA("\\\\.\\COM4",
GENERIC_READ | GENERIC_WRITE, //Request Read and Write Acess
0, //Block others from sharing Acess
NULL, //Disable Security features
OPEN_EXISTING, //Only open existing device
FILE_ATTRIBUTE_NORMAL, //Used to set atributes and flags
NULL);
//Handle to
if(prog_com_port == INVALID_HANDLE_VALUE)
{
printf("Opening COM port failed - INVALID_HANDLE_VALUE\n");
CloseHandle(prog_com_port);
return 1;
}
else
printf("COM port was opened successfully \n");
SetupCommState(prog_com_port);
Sleep(2);
#define wm_size 25
int messageLength = 2;
char W_message[wm_size] = {0x01,0x01};
long unsigned int * bytes_sent;
BOOL Status;
Status = WriteFile(prog_com_port, // Handle to the Serial port
&W_message, // Pointer to message buffer
messageLength, // No of bytes to write
bytes_sent, // Pointer to number of bytes written
NULL);
if(!Status)
{
printf("Failed to write to COM port - 0x00 \n");
CloseHandle(prog_com_port);
return 2;
}
CloseHandle(prog_com_port);
My logic tells me that setting wm_size = 2 is enough but apparently that is wrong. Can someone tell me why?
I played around with the size of wm_size and found experiemtally that 25 fixed the problem.
wm_size = 2 is enough, the problem is elsewhere: bytes_sent is a pointer that points nowhere.
Your "fix" didn't fix anything. You are experiencing undefined behaviour (which includes apparently working fine).
You want this (all comments are mine):
DWORD messageLength = 2; // use DWORD
DWORD bytes_sent; // use DWORD
char W_message[] = {0x01,0x01}; // you don't need wm_size
Status = WriteFile(prog_com_port,
W_message, // remove &, W_message decays into a pointer
messageLength,
&bytes_sent, // put &, you need a pointer here
NULL);
or even better: you don't need messageLength:
Status = WriteFile(prog_com_port,
W_message, // remove &
sizeof(W_message), // assuming W_message contains chars
&bytes_sent, // put &
NULL);
// now bytes_sent contains the number of bytes sent (hopefully 2),
// providing WriteFile succeeded (Status is TRUE)
The usage of DWORD is highly recommended, so the types of the arguments passed to to WriteFile actually match the declaration (and the documentation). Also be aware that LPDWORD in all Microsoft documentation and header files is is the
same thing as DWORD*.
On POSIX systems, I am able to use the mmap function to read the contents of a file faster than getline, getc, etc. This is important in the program that I am developing as it is expected to read very large files into memory; iteratively collecting lines using getline is too costly. Portability is also a requirement of my software, so if I use mmap, I need to find a way to memory map files using the WinApi, as I'd rather not compile through cygwin/msys. From a cursory search I identified this MSDN article which describes very briefly a way to map files into memory, however, from trawling through documentation I can't make head nor tails of how to actually implement it, and I'm stuck on finding example snippets of code, like there are for POSIX mmap.
How do I use the WinApi's memory mapping options to read a file into a char*?
How do I use the WinApi's memory mapping options to read a file into a
char*?
Under Windows, when you map a file in memory, you get a pointer to the memory location where the first byte of the file has been mapped. You can cast that pointer to whatever datatype you like, including char*.
In other words, it is Windows which decide where the mapped data will be in memory. You cannot provide a char* and expect Windows will load data there.
This means that if you already have a char* and want the data from the file in the location pointed by that char*, then you have to copy it. Not a good idea in terms of performances.
Here is a simple program dumping a text file by mapping the file into memory and then displaying all ASCII characters. Tested with MSVC2019.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
TCHAR *lpFileName = TEXT("hello.txt");
HANDLE hFile;
HANDLE hMap;
LPVOID lpBasePtr;
LARGE_INTEGER liFileSize;
hFile = CreateFile(lpFileName,
GENERIC_READ, // dwDesiredAccess
0, // dwShareMode
NULL, // lpSecurityAttributes
OPEN_EXISTING, // dwCreationDisposition
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
0); // hTemplateFile
if (hFile == INVALID_HANDLE_VALUE) {
fprintf(stderr, "CreateFile failed with error %d\n", GetLastError());
return 1;
}
if (!GetFileSizeEx(hFile, &liFileSize)) {
fprintf(stderr, "GetFileSize failed with error %d\n", GetLastError());
CloseHandle(hFile);
return 1;
}
if (liFileSize.QuadPart == 0) {
fprintf(stderr, "File is empty\n");
CloseHandle(hFile);
return 1;
}
hMap = CreateFileMapping(
hFile,
NULL, // Mapping attributes
PAGE_READONLY, // Protection flags
0, // MaximumSizeHigh
0, // MaximumSizeLow
NULL); // Name
if (hMap == 0) {
fprintf(stderr, "CreateFileMapping failed with error %d\n", GetLastError());
CloseHandle(hFile);
return 1;
}
lpBasePtr = MapViewOfFile(
hMap,
FILE_MAP_READ, // dwDesiredAccess
0, // dwFileOffsetHigh
0, // dwFileOffsetLow
0); // dwNumberOfBytesToMap
if (lpBasePtr == NULL) {
fprintf(stderr, "MapViewOfFile failed with error %d\n", GetLastError());
CloseHandle(hMap);
CloseHandle(hFile);
return 1;
}
// Display file content as ASCII charaters
char *ptr = (char *)lpBasePtr;
LONGLONG i = liFileSize.QuadPart;
while (i-- > 0) {
fputc(*ptr++, stdout);
}
UnmapViewOfFile(lpBasePtr);
CloseHandle(hMap);
CloseHandle(hFile);
printf("\nDone\n");
}
I have mapped a file of unknown size (around 4-6 GiB) in Windows platform and got a pointer to the start of the file data returned from the MapFileView function. But how can I know that I have reached the end of the file when I access the data using the pointer sequentially?
Here is the code I have so far written and it successfully maps the file and returns the pointer:
#include <Windows.h>
#include <stdio.h>
#include <inttypes.h>
int main()
{
HANDLE hFile = CreateFile("Test.bin",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (!hFile)
{
printf("Could not create file (%lu).\n", GetLastError());
exit(1) ;
}
HANDLE hMapFile = CreateFileMappingA(hFile,
NULL,
PAGE_READWRITE,
0,
0,
NULL);
if (!hMapFile)
{
printf("Could not create file mapping object (%lu).\n", GetLastError());
CloseHandle(hFile);
exit(1);
}
int32_t* pBuf = (int32_t*) MapViewOfFile(hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
if (!pBuf)
{
printf("Could not map file (%lu).\n", GetLastError());
CloseHandle(hFile);
CloseHandle(hMapFile);
exit(1);
};
UnmapViewOfFile(pBuf);
CloseHandle(hFile);
CloseHandle(hMapFile);
exit(0);
}
So I wanted to read equal sized different parts of the file simultaneously in multiple threads. I believe mapped file is the right choice for this purpose. Advice about any other faster and possible approaches is highly appreciated.
I have researched some similar questions in the forum and I suppose this is the closest topic I could find:
Read all contents of memory mapped file or Memory Mapped View Accessor without knowing the size of it
But this answer is using C# and is not written using the WinAPI, therefore, I couldn't understand their process.
Thanks in advance :)
Call GetFileSizeEx to get the size of a file, and use this in combination with the base address and the current read address to determine where the end address is.
I'm trying to get to work ReadFile function. Here's my code:
#define BUFFERSIZE 5
int main(int argc, char* argv[])
{
OVERLAPPED overlapIn = {};
HANDLE tHandle;
char buf[BUFFERSIZE] = {};
DWORD lpNumberOfBytesRead;
tHandle = CreateFile(
L"\\\\.\\D:",
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (tHandle == INVALID_HANDLE_VALUE)
{
DWORD error = GetLastError();
assert(0);
}
if (ReadFile(tHandle, &buf, BUFFERSIZE - 1, &lpNumberOfBytesRead, NULL) == 0)
{
int error = GetLastError();
printf("Terminal failure: Unable to read from disk.\n GetLastError=%d\n", error);
CloseHandle(tHandle);
return 1;
}
The GetLastError function returns code 87, which is ERROR_INVALID_PARAMETER.
It's clear that one of the parameters is wrong, but I have no idea which one, since I tried to do everything like it's written in the documentation.
This is described in the documentation for CreateFile:
Volume handles can be opened as noncached at the discretion of the particular file system, even when the noncached option is not specified in CreateFile. You should assume that all Microsoft file systems open volume handles as noncached.
The MSDN article on File Buffering describes the requirements for noncached handles:
File access sizes, including the optional file offset in the OVERLAPPED structure, if specified, must be for a number of bytes that is an integer multiple of the volume sector size. For example, if the sector size is 512 bytes, an application can request reads and writes of 512, 1,024, 1,536, or 2,048 bytes, but not of 335, 981, or 7,171 bytes.
File access buffer addresses for read and write operations should be physical sector-aligned, which means aligned on addresses in memory that are integer multiples of the volume's physical sector size. Depending on the disk, this requirement may not be enforced.
Rigorous code should check the sector size for the file system in question, then use this approach to allocate the memory. However, in my experience, the sector size has always been less than or equal to the allocation granularity, so you can get away with just using VirtualAlloc() to allocate a memory block.
buffer size needs to be aligned with hdd sectorsize
WIN32_FIND_DATA atr = {0};
DWORD BYTES_PER_SECTOR;
char path[MAX_PATH];
/* get path length current dir */
const size_t len = GetCurrentDirectory(0, 0);
/* set path to path char array */
GetCurrentDirectory(len, path);
/* windows function to get disk details */
GetDiskFreeSpace(NULL, NULL, &BYTES_PER_SECTOR, NULL, NULL);
/* find first file in dir */
find = FindFirstFile(path, &atr);
for(;find != INVALID_HANDLE_VALUE;){
/* get the file size */
DWORD filesize = atr.nFileSizeLow;
if(atr.nFileSizeHigh > 0){
filesize = atr.nFileSizeHigh;
filesize = (filesize << 31);
filesize = atr.nFileSizeLow;
}
/* sector size aligned file size */
size_t buffer_size = ((BYTES_PER_SECTOR + ((filesize + BYTES_PER_SECTOR)-1)) & ~(BYTES_PER_SECTOR -1));
/* create buffer */
DWORD buffer[buffer_size];
/* create a new file or open an existing file */
handle = CreateFile(&path[0], GENERIC_READ | GENERIC_WRITE, 0 , NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, NULL))!=INVALID_HANDLE_VALUE)
/* read the file in to buffer */
ReadFile(handle, (void*)&buffer, buffer_size, &bytesread, NULL)
if(FindNextFile(find, &atr)==0){ printf("last file processed, leaving\n");break;};
}
CloseHandle(file);
FindClose(find);
I am trying to monitor a directory e:\test using ReadDirectoryChangesW API.
My Code :
#define UNICODE
#define WIN32_WINNT 0x0500
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
HANDLE hDir;
int _tmain(int argc, _TCHAR* argv[])
{
FILE_NOTIFY_INFORMATION fniDir;
DWORD i = 0;
hDir = CreateFile(_T("e:\\test"), GENERIC_READ , FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
ReadDirectoryChangesW(hDir, &fniDir, sizeof(fniDir), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME, &i, NULL, NULL);
while(TRUE)
{
if(i>0)
wprintf(L"%s", fniDir.FileName);
}
CloseHandle(hDir);
return 0;
}
I don't know what's wrong with my code as I haven't understood ReadDirectoryChangesW documentation completely, specially the LPOVERLAPPED parameters.
When I run the code I don't get any output, except for a blank console window. Can someone point me in a right direction?
Thanks.
You only need the overlapped struct if you plan on catching the changes notifications asynchronously. In your code you don't need it.
Here's how you do it.
HANDLE hDir = CreateFile(
p.string().c_str(), /* pointer to the file name */
FILE_LIST_DIRECTORY, /* (this is important to be FILE_LIST_DIRECTORY!) access (read-write) mode */
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* (file share write is needed, or else user is not able to rename file while you hold it) share mode */
NULL, /* security descriptor */
OPEN_EXISTING, /* how to create */
FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
NULL /* file with attributes to copy */
);
if(hDir == INVALID_HANDLE_VALUE){
throw runtime_error(string("Could not open ").append(p.string()).append(" for watching!"));
}
FILE_NOTIFY_INFORMATION buffer[1024];
DWORD BytesReturned;
while( ReadDirectoryChangesW(
hDir, /* handle to directory */
&buffer, /* read results buffer */
sizeof(buffer), /* length of buffer */
TRUE, /* monitoring option */
FILE_NOTIFY_CHANGE_LAST_WRITE, /* filter conditions */
&BytesReturned, /* bytes returned */
NULL, /* overlapped buffer */
NULL)){
do{
//CANT DO THIS! FileName is NOT \0 terminated
//wprintf("file: %s\n",buffer.FileName);
buffer += buffer.NextEntryOffset;
}while(buffer.NextEntryOffset);
}