I am using ReadFile to read a simple string that I wrote to a file using WriteFile.
Have a simple string: "Test string, testing windows functions".
Used WriteFile to write that to a file.
Now I want to use ReadFile to confirm that it was written to the file. I need to compare what I read to the original string above. To Read from the file I have
DWORD dwBytesRead;
char buff[128];
if(!ReadFile(hFile, buff, 128, &dwBytesRead, NULL))
//Fail
The function returns true so it is reading from the file. The problem is buff is full of just ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ. I've never come across LPVOID before so I don't know if it is something there or what. Is there a way to do this string comparison?
EDIT: The code i use to write to the file is quite simple:
if(!WriteFile(hFile, sentence.c_str(), sentence.length(), &bytesWritten, NULL))
{
//FAIL
}
The file pointer needs rewound after the WriteFile() and before the ReadFile(). As it stands, ReadFile() does not fail but reads zero bytes thus buff is unchanged. As buff is uninitialised it contains junk. To rewind the file pointer to the beginning of the file use SetFilePointer():
#include <windows.h>
#include <iostream>
#include <string>
int main()
{
HANDLE hFile = CreateFile ("myfile.txt",
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile)
{
std::string sentence("a test");
DWORD bytesWritten;
if (WriteFile(hFile,
sentence.c_str(),
sentence.length(),
&bytesWritten,
NULL))
{
if (INVALID_SET_FILE_POINTER != SetFilePointer(hFile,
0,
0,
FILE_BEGIN))
{
char buf[128] = { 0 }; /* Initialise 'buf'. */
DWORD bytesRead;
/* Read one less char into 'buf' to ensure null termination. */
if (ReadFile(hFile, buf, 127, &bytesRead, NULL))
{
std::cout << "[" << buf << "]\n";
}
else
{
std::cerr << "Failed to ReadFile: " <<
GetLastError() << "\n";
}
}
else
{
std::cerr << "Failed to SetFilePointer: " <<
GetLastError() << "\n";
}
}
else
{
std::cerr << "Failed to WriteFile: " << GetLastError() << "\n";
}
CloseHandle(hFile);
}
else
{
std::cerr << "Failed to open file: " << GetLastError() << "\n";
}
return 0;
}
The function returns true so it is reading from the file. The problem is buff is full of just ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ.
ReadFile only fills the buffer up to the value of dwBytesRead. If you're trying to work with a string, you'll have to null terminate it yourself after ReadFile returns:
buff [dwBytesRead] = 0;
You should not use 128 as the nNumberOfBytesToRead, since you can get out of bounds while printing the string (or otherwise considering buff as a 0-terminated string). Also check dwBytesRead if it really reads that many bytes, and 0-terminate the string as suggested by #James McLaughlin.
Related
I am trying to read data all of the data in a PCAPNG file in chunks only using the Windows API's.
I have the first read of the file working with ReadFile and the data that is being returned is correct but on our subsequent call, we are failing the ReadFile with INVALID_HANDLE.
HANDLE hFile;
DWORD dwBytesToRead = 32;
DWORD dwBytesRead = 0;
DWORD ReadBuffer[2] = {0};
hFile = CreateFile(LogPath.c_str(),
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hFile == INVALID_HANDLE_VALUE)
return false;
while(ReadFile(hFile, ReadBuffer, dwBytesToRead, &dwBytesRead, NULL) && dwBytesRead > 0)
{
std::cout << "Data from ReadBuffer[0]: " << ReadBuffer[0] << std::endl;
std::cout << "Data from ReadBuffer[1]: " << ReadBuffer[1] << std::endl;
}
std::cout << "Failed with: " << GetLastError() << std::endl;
CloseHandle(hFile);
During the first iteration of the ReadFile, we are able to successfully read the content of the file but on the second iteration of the loop we run into an error of INVALID_HANDLE.
Basing this ReadFile loop on this documentation: https://learn.microsoft.com/en-us/windows/win32/fileio/appending-one-file-to-another-file
I am not sure what our failure reason is.
I've done some testing with SetFilePointer and the OVERLAPPED structure but I haven't had much success with either.
Am I misunderstanding the ReadFile API or is this not the right way to do what I am looking for?
You're reading 32 bytes into a buffer that is only 8 bytes long.
You need to either change ReadBuffer to be larger, change the type from a DWORD to something that is 16 bytes long, or change dwBytesToRead to be the appropriate size (sizeof(ReadBuffer), and change the declaration order).
I`m struggling for the past many hours with the following problem: I try to read a file using CreateFile and ReadFile methods.
Here is the code:
char* Utils::ReadFromFile(wchar_t* path) {
HANDLE hFile = CreateFile(
path, // long pointer word string file path (16 bit UNICODE char pointer)
GENERIC_READ, // access to file
0, // share mode ( 0 - prevents others from opening/readin/etc)
NULL, // security attributes
OPEN_EXISTING, // action to take on file -- returns ERROR_FILE_NOT_FOUND
FILE_ATTRIBUTE_READONLY, // readonly and offset possibility
NULL // when opening an existing file, this parameter is ignored
);
if (hFile == INVALID_HANDLE_VALUE) {
std::cout << "File opening failed" << endl;
std::cout << "Details: \n" << Utils::GetLastErrorMessage() << endl;
CloseHandle(hFile);
hFile = NULL;
return nullptr;
}
LARGE_INTEGER largeInteger;
GetFileSizeEx(hFile, &largeInteger);
LONGLONG fileSize = largeInteger.QuadPart;
if (fileSize == 0) {
std::cout << "Error when reading file size" << endl;
std::cout << "Details: \n" << Utils::GetLastErrorMessage() << endl;
CloseHandle(hFile);
hFile = NULL;
return nullptr;
}
cout << "File size: " << fileSize << endl;
char* bytesRead;
bytesRead = new char(fileSize);
int currentOffset = 0;
int attempts = 0;
int nBytesToBeRead = BYTES_TO_READ;
//DWORD nBytesRead = 0;
OVERLAPPED overlap{};
errno_t status;
while (currentOffset < fileSize) {
overlap.Offset = currentOffset;
if (fileSize - currentOffset < nBytesToBeRead)
nBytesToBeRead = fileSize - currentOffset;
status = ReadFile(
hFile, // file handler
bytesRead + currentOffset, // byted read from file
nBytesToBeRead, // number of bytes to read
NULL, // number of bytes read
&overlap // overlap parameter
);
if (status == 0) {
std::cout << "Error when reading file at offset: " << currentOffset << endl;
std::cout << "Details: \n" << Utils::GetLastErrorMessage() << endl;
attempts++;
std::cout << "Attempt: " << attempts << endl;
if (attempts == 3) {
cout << "The operation could not be performed. Closing..." << endl;
CloseHandle(hFile);
hFile = NULL;
return nullptr;
}
continue;
}
else {
cout << "Read from offset: " << currentOffset;// << " -- " << overlap.InternalHigh << endl;
currentOffset += nBytesToBeRead;
if (currentOffset == fileSize) {
cout << "File reading completed" << endl;
break;
}
}
}
CloseHandle(hFile);
return bytesRead;
}
When running this method I get some weird results:
One time it worked perfectly
Very often I get Access violation reading location for currentOffset variable and overlap.InternalHigh ( I commented last one), with last method from CallStack being
msvcp140d.dll!std::locale::locale(const std::locale & _Right) Line 326 C++
Sometimes the function runs perfectly, but I get access violation reading location when trying to exit main function with last method from CallStack being
ucrtbased.dll!_CrtIsValidHeapPointer(const void * block) Line 1385 C++
I read the windows documentation thoroughly regarding the methods I use and checked the Internet for any solution I could find, but without any result. I don't understand this behaviour, getting different errors when running cod multiple times, and therefore I can`t get to a solution for this problem.
Note: The reason I am reading the file in repeated calls is not relevant. I tried reading with a single call and the result is the same.
Thank you in advance
You are allocating a single char for bytesRead, not an array of fileSize chars:
char* bytesRead;
bytesRead = new char(fileSize); // allocate a char and initialize it with fileSize value
bytesRead = new char[fileSize]; // allocate an array of fileSize chars
INTRODUCTION:
I am reading from text file with ReadFile. Buffer passed to ReadFile is sent to standard output with cout. Standard output is redirected to a text file.
PROBLEM:
Although my code "works", no data is lost, resulting file is larger than the original one.
When opened in notepad, everything seems fine, but when opened in Notepad++ I can clearly see extra lines added. These lines are new lines (\n).
MVCE that reproduces this behavior is submitted below.
#include <iostream>
#include <Windows.h>
int main()
{
HANDLE hFile = ::CreateFile("C:\\123.txt",
GENERIC_READ,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
return ::GetLastError();
char buffer[256];
DWORD bytesRead = 1, // dummy value so while loop can work
bytesWritten = 0; // needed for WriteFile, not for cout version
//======== so WriteFile outputs to console, not needed for cout version
HANDLE hStandardOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
if (INVALID_HANDLE_VALUE == hStandardOutput)
{
std::cout << "GetStdHandle error code = " << ::GetLastError() << std::endl;
::CloseHandle(hFile);
return ::GetLastError();
}
//============================
while(bytesRead)
{
// '\0' terminate buffer, needed for cout only
::memset(buffer, '\0', sizeof(buffer));
if (!::ReadFile(hFile,
buffer,
sizeof(buffer) - 1, // - 1 for '\0', not needed when using WriteFile
&bytesRead, NULL))
{
std::cout << "ReadFile error code = " << ::GetLastError() << std::endl;
break;
}
/*============= Works fine
if(!::WriteFile(hStandardOutput, buffer, bytesRead, &bytesWritten, NULL))
{
std::cout << "WriteFile error code = " << ::GetLastError() << std::endl;
break;
}*/
//------------- comment out when testing WriteFile
std::cout << buffer; // extra lines...
// std::cout.write(buffer, bytesRead); // extra lines as well...
//----------------------------------------
}
::CloseHandle(hFile);
return 0;
}
QUESTION:
What is causing above described behavior? How to fix it?
MY EFFORTS TO SOLVE THE PROBLEM:
As I type this post I am Googling aimlessly, hoping for some clue to show up.
I suspect that the problem lies when outputting \n, it seems that Windows inserts \r as well, but I am not sure.
The \n character has special meaning to STL character streams. It represents a newline, which gets translated to the platform-specific line break upon output. This is discussed here:
Binary and text modes
A text stream is an ordered sequence of characters composed into lines (zero or more characters plus a terminating '\n'). Whether the last line requires a terminating '\n' is implementation-defined. Characters may have to be added, altered, or deleted on input and output to conform to the conventions for representing text in the OS (in particular, C streams on Windows OS convert \n to \r\n on output, and convert \r\n to \n on input) .
So it is likely that std::cout outputs \r\n when it is given \n, even if a preceding \r was also given, thus an input of \r\n could become \r\r\n on output. It is not standardized behavior on Windows how individual apps handle bare-CR characters. They might be ignored, or they might be treated as line breaks. In your case, it sounds like the latter.
There is no standard way to use std::cout in binary mode so \n is output as \n instead of as \r\n. However, see How to make cout behave as in binary mode? for some possible ways that you might be able to make std::cout output in binary mode on Windows, depending on your compiler and STL implementation. Or, you could try using std::cout.rdbuf() to substitute in your own std::basic_streambuf object that performs binary output to the console.
That being said, the way your code is handling the data buffer is a little off, it should look more like this instead (not accounting for the above info):
#include <iostream>
#include <Windows.h>
int main()
{
HANDLE hFile = ::CreateFile("C:\\123.txt",
GENERIC_READ,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE, // why??
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
return ::GetLastError();
char buffer[256];
DWORD bytesRead, bytesWritten, err;
//======== so WriteFile outputs to console, not needed for cout version
HANDLE hStandardOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
if (INVALID_HANDLE_VALUE == hStandardOutput)
{
err = ::GetLastError();
std::cout << "GetStdHandle error code = " << err << std::endl;
::CloseHandle(hFile);
return err;
}
//============================
do
{
if (!::ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL))
{
err = ::GetLastError();
std::cout << "ReadFile error code = " << err << std::endl;
::CloseHandle(hFile);
return err;
}
if (bytesRead == 0) // EOF reached
break;
/*============= Works fine
if (!::WriteFile(hStandardOutput, buffer, bytesRead, &bytesWritten, NULL))
{
err = ::GetLastError();
std::cout << "WriteFile error code = " << err << std::endl;
::CloseHandle(hFile);
return err;
}
*/
//------------- comment out when testing WriteFile
std::cout.write(buffer, bytesRead);
//----------------------------------------
}
while (true);
::CloseHandle(hFile);
return 0;
}
std::cout << buffer << std::endl;
if (lastTitle != buffer) {
//write new title;
WriteFile(file, "\n\nWindow: ", sizeof("\n\nWindow: "), NULL, NULL);
WriteFile(file, buffer.c_str(), sizeof(buffer), NULL, NULL);
WriteFile(file, "\n", sizeof("\n"), NULL, NULL);
std::cout << GetLastError(); //this is showing 0, which means no error
Cout is outputting:
C:\Users\riseo\Desktop\C++\my_proj\Debug\my_proj.exe
The file that is being written to is showing:
Window: C:\Users\riseo\Desktop\C++\m
I'm not quite sure why this is being truncated, it should be the same as what cout is printing. Im sorry this post doesn't show much research, but I've been getting burned by various string-format related gotchas all day and I have no clue what is happening here. All I can think of is maybe something with c_str() is going wrong.
You are seriously misusing sizeof(). WriteFile() operates on bytes, but you are passing it character data. String literals contain a null terminator that sizeof() would include, which you don't want in this case. And std::string() contains a pointer to the character data, so sizeof(std::string) does not account for the true character length.
You need to do this instead:
//write new title;
WriteFile(file, "\n\nWindow: ", strlen("\n\nWindow: "), NULL, NULL);
WriteFile(file, buffer.c_str(), buffer.length(), NULL, NULL);
WriteFile(file, "\n", strlen("\n"), NULL, NULL);
A wrapper function would be better:
bool WriteStringToFile(HANDLE hFile, const std::string &str)
{
return WriteFile(hFile, str.c_str(), str.length(), NULL, NULL);
}
...
//write new title;
WriteStringToFile(file, "\n\nWindow: ");
WriteStringToFile(file, buffer);
WriteStringToFile(file, "\n");
A std::ofstream would be even better:
std::ofstream ofs("myfile.txt", std::ios_base::binary);
...
ofs << "\n\nWindow: " << buffer << "\n";
In the following function, when I hit the FindVolumeClose() line, I always get the error outlined below. Any ideas why this happens?
Unhandled exception at 0x11000000 in BootFixFFS.exe: 0xC0000005: Access violation (parameters: 0x00000008).
char buffer[MAX_PATH];
HANDLE hVol = FindFirstVolume(buffer, MAX_PATH);
if (hVol == INVALID_HANDLE_VALUE) {
std::cout << "No volumes found\n.";
return FALSE;
}
std::string strVol = buffer;
strVol.pop_back();
HANDLE hPart = CreateFile(strVol.c_str(), 0, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hPart == INVALID_HANDLE_VALUE) {
FindVolumeClose(hVol);
std::cout << "Couldn't create file: " << GetLastError() << "\n";
return FALSE;
}
int numPartitions = 8;
DWORD dwDliSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + numPartitions * sizeof(PARTITION_INFORMATION_EX);
DRIVE_LAYOUT_INFORMATION_EX *dli = (DRIVE_LAYOUT_INFORMATION_EX*) new BYTE[dwDliSize];
DWORD dwSize;
ZeroMemory(&dli, sizeof(dli));
BOOL bSuccess = DeviceIoControl(hPart, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, &dli, dwDliSize, &dwSize, NULL);
CloseHandle(hPart);
FindVolumeClose(hVol);
if (bSuccess == FALSE) {
std::cout << "Couldn't get device information: " << GetLastError() << "\n";
return FALSE;
}
return TRUE;
It seems to me that ZeroMemory(&dli, sizeof(dli)) is not doing what you want it to do. It erases the address from dli, instead of zeroing the content of the byte array. Maybe ZeroMemory(dli, dwDliSize) is what you meant?
Also, the more serious error is using &dli when calling DeviceIoControl. You are basically overwriting your entire stack, and destroying the local variables.