In Win32, how can a text file be successfully read into memory? - winapi

I am trying to get simple file IO working in Win32. So far the writing is working fine, but the reading is not: although it successfully reads the contents, additional "garbage" is appended to the string. The code I have so far is below. The program has UNICODE defined.
For writing:
DWORD dwTextSize = GetWindowTextLength(hWndTextBox);
WCHAR *lpszText = new WCHAR[dwTextSize];
GetWindowText(hWndTextBox, lpszText, dwTextSize + 1);
hTextFile = CreateFile(lpszTextFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwBytesWritten;
WriteFile(hTextFile, lpszText, 2 * dwTextSize, &dwBytesWritten, NULL); // x2 for 2 bytes per Unicode character
CloseHandle(hTextFile);
DeleteObject(hTextFile);
For this example, Hello, World! is saved successfully as Hello, World!.
For reading:
lpszTextFileName = L"text.txt"; // LPCTSTR Variable
hTextFile = CreateFile(lpszTextFileName, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwFileSize = GetFileSize(hTextFile, &dwFileSize);
DWORD dwBytesRead;
WCHAR *lpszText = new WCHAR[dwFileSize / 2];
ReadFile(hTextFile, lpszText, dwFileSize, &dwBytesRead, NULL);
CloseHandle(hTextFile);
The string is then used to set the text of an EDIT control:
SendMessage(hWndTextBox, WM_SETTEXT, NULL, (LPARAM)lpszText); // SetWindowText() also possible
When Hello, World! is read back in, it reads back in as Hello, World!﷽﷽ꮫꮫꮫꮫﻮ or a visual variation upon this, but basically "garbage"!
I have probably missed something rather obvious, but I cannot see where! Is there a solution to this problem and if so, what is it?

Ok I started this with a comment, but its getting out of control.
For Writing
This:
WCHAR *lpszText = new WCHAR[dwTextSize];
should be:
WCHAR *lpszText = new WCHAR[dwTextSize+1];
This:
DeleteObject(hTextFile);
should not be there at all.. Get rid of it.
I'm assuming you delete [] lpszText; somewhere when you're done with it. if not, do so.
For Reading
The second parameter to GetFileSize() should not be the same variable as your return value. It is the HIGH 32bit of a 64-bit value for large file sizes. If you know you're file size is smaller than 4gB, you can leave it NULL, so change this:
DWORD dwFileSize = GetFileSize(hTextFile, &dwFileSize);
to this:
DWORD dwFileSize = GetFileSize(hTextFile, NULL);
You must account for the null terminator of your file buffer, so this:
WCHAR *lpszText = new WCHAR[dwFileSize / 2];
should be changed to this:
WCHAR *lpszText = new WCHAR[dwFileSize / 2 + 1];
lpszText[dwFileSize / 2] = 0;
and the rest should work as you're hoping it would. No error checking, which is not good, but I've seen worse. And as before, I'm assuming you delete [] lpszText; somewhere when you're done with it. if not, do so.

Related

Capture output from console program with overlapping and events

I know lots of similar questions on this topic have been asked before but so far I have been unable to find a solution that actually works. I want to start a console program from my program and capture its output. My implementation should be in a way that is compatible with WaitForMultipleObjects(), i.e. I want to get notified whenever there is new data to read in the pipe.
My implementation is based on this example from MSDN. However, I had to modify it a little because I need overlapped I/O in order to be able to wait for ReadFile() to finish. So I'm using named pipes created using Dave Hart's MyCreatePipeEx() function from here.
This is my actual code. I have removed error checks for readability reasons.
HANDLE hReadEvent;
HANDLE hStdIn_Rd, hStdIn_Wr;
HANDLE hStdOut_Rd, hStdOut_Wr;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
OVERLAPPED ovl;
HANDLE hEvt[2];
DWORD mask, gotbytes;
BYTE buf[4097];
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
MyCreatePipeEx(&hStdOut_Rd, &hStdOut_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
MyCreatePipeEx(&hStdIn_Rd, &hStdIn_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
SetHandleInformation(hStdOut_Rd, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(hStdIn_Wr, HANDLE_FLAG_INHERIT, 0);
memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION));
memset(&siStartInfo, 0, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hStdOut_Wr;
siStartInfo.hStdOutput = hStdOut_Wr;
siStartInfo.hStdInput = hStdIn_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, "test.exe", NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for(;;) {
int i = 0;
hEvt[i++] = piProcInfo.hProcess;
memset(&ovl, 0, sizeof(OVERLAPPED));
ovl.hEvent = hReadEvent;
if(!ReadFile(hStdOut_Rd, buf, 4096, &gotbytes, &ovl)) {
if(GetLastError() == ERROR_IO_PENDING) hEvt[i++] = hReadEvent;
} else {
buf[gotbytes] = 0;
printf("%s", buf);
}
mask = WaitForMultipleObjects(i, hEvt, FALSE, INFINITE);
if(mask == WAIT_OBJECT_0 + 1) {
if(GetOverlappedResult(hStdOut_Rd, &ovl, &gotbytes, FALSE)) {
buf[gotbytes] = 0;
printf("%s", buf);
}
} else if(mask == WAIT_OBJECT_0) {
break;
}
}
The problem with this code is the following: As you can see, I'm reading in chunks of 4kb using ReadFile() because I obviously don't know how much data the external program test.exe will output. Doing it this way was suggested here:
To read a variable amount of data from the client process just issue
read requests of whatever size you find convenient and be prepared to
handle read events that are shorter than you requested. Don't
interpret a short but non-zero length as EOF. Keep issuing read
requests until you get a zero length read or an error.
However, this doesn't work. The event object passed to ReadFile() as part of the OVERLAPPED structure will only trigger once there are 4kb in the buffer. If the external program just prints "Hello", the event won't trigger at all. There need to be 4kb in the buffer for hReadEvent to actually trigger.
So I thought I should read byte by byte instead and modified my program to use ReadFile() like this:
if(!ReadFile(hStdOut_Rd, buf, 1, &gotbytes, &ovl)) {
However, this doesn't work either. If I do it like this, the read event is not triggered at all which is really confusing me. When using 4096 bytes, the event does indeed trigger as soon as there are 4096 bytes in the pipe, but when using 1 byte it doesn't work at all.
So how am I supposed to solve this? I'm pretty much out of ideas here. Is there no way to have the ReadFile() event trigger whenever there is some new data in the pipe? Can't be that difficult, can it?
Just for the record, while there are some problems with my code (see discussion in comments below the OP), the general problem is that it's not really possible to capture the output of arbitrary external programs because they will typically use block buffering when their output is redirected to a pipe, which means that output will only arrive at the capturing program once that buffer is flushed so real time capturing is not really possible.
Some workarounds have been suggested though:
1) (Windows) Here is a workaround that uses GetConsoleScreenBuffer() to capture the output from arbitrary console programs but it currently only supports one console page of output.
2) (Linux) On Linux, it's apparently possible to use pseudo-terminals to force the external program to use unbuffered output. Here is an example.

Read file from 100th byte

How can I read a file from 100th byte, using ReadFile function and C++
I used this code but it reads the first 100 bytes of the file
I want to read the second 100 bytes
hndl = CreateFileW(L"1.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
SetFilePointer(hndl, 100, NULL, FILE_BEGIN);
ReadFile(hndl, pbytReadBuffer, 100, NULL, &ol);
The ReadFile API offers two distinct ways to set the starting offset when doing synchronous I/O:
By using the implicitly stored file pointer (which can be manipulated through the SetFilePointer API call).
Or by passing an explicit offset through the OVERLAPPED structure.
Your code fails because you are setting the implicitly stored file pointer, but then (presumably) pass a zero-initialized OVERLAPPED structure, which ignores the file pointer (see Synchronization and File Position for more information).
Either of the following solutions will work. First, by using the implicitly stored file pointer. This is useful, when you want to read chunks of a file in consecutive calls:
hndl = CreateFileW(L"1.txt", GENERIC_READ, 0, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
// Move the file pointer to offset 100
SetFilePointer(hndl, 100, NULL, FILE_BEGIN);
// Read contents from the current offset
DWORD dwBytesRead{0};
ReadFile(hndl, pbytReadBuffer, 100, &dwBytesRead, nullptr);
Alternatively, you can pass an OVERLAPPED structure to pass the offset. This ignores the implicitly stored file pointer. It is slightly more efficient, because it doesn't need two calls to the file I/O API.
hndl = CreateFileW(L"1.txt", GENERIC_READ, 0, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
OVERLAPPED ol{};
// Set the offset from the start of the file
ol.Offset = 100;
ReadFile(hndl, pbytReadBuffer, 100, nullptr, &ol);
Note that error handling is elided in these samples for brevity. In real code, you always must check for errors.
you need code like this
HANDLE hFile = CreateFile(L"c:\\windows\\notepad.exe", FILE_GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
OVERLAPPED ov = {};
ov.Offset = 100;
UCHAR buf[100];
ULONG cb;
if (ReadFile(hFile, buf, sizeof(buf), &cb, &ov))
{
}
else
{
GetLastError();
}
CloseHandle(hFile);
}
you not need call SetFilePointer - much better set offset direct in OVERLAPPED structure
EDIT
for every read/write operation we need to specify the starting byte offset. we can do this directed by setting values in Offset and OffsetHigh from OVERLAPPED.
or indirect only if file opened if synchronous mode - I/O Manager can use the current file position in FILE_OBJECT - so we can not direct set offset - it will be get from FILE_OBJECT.CurrentByteOffset. FILE_OBJECT.CurrentByteOffset we can set with SetFilePointer also every read/write operation update this offset - move forward to count of bytes which readed/ written. of course this is correct only if file used in synchronous mode when all operations with file is sequential
if we use direct offset in OVERLAPPED he and used - so FILE_OBJECT.CurrentByteOffset is ignored - this mean that previous call to SetFilePointer - also lost all effect - will be used offset from OVERLAPPED and after read operation FILE_OBJECT.CurrentByteOffset will be updated based on offset + bytes readed

Windows RegQueryValueEx odd return results

I am getting odd results when using RegQueryValueEx and I cannot figure out why.
This is what I had set up before making the RegQueryValueEx
DWORD dataSize;
TCHAR data[256];
The first time I call
LONG ret = RegQueryValueEx( hKey, dataKey, NULL, NULL, (LPBYTE)data, &dataSize);
ret is equal to 234 (ERROR_MORE_DATA)
But when I call the same thing on the next line
LONG ret2 = RegQueryValueEx( hKey, dataKey, NULL, NULL, (LPBYTE)data, &dataSize);
ret2 is equal to 0 (ERROR_SUCCESS)
Why would this function return ERROR_MORE_DATA the first time I call it, then return ERROR_SUCESS on the same call on the very next line?
I attempted to change TCHAR data[1024] but I got the exact same results. Any ideas?
Complete code:
for( int i=0; i<NUM_HISTORY; i++){
CString dataKey = getDataKey(i);
DWORD dataSize = 1024;
TCHAR data[1024];
LONG ret = RegQueryValueEx( hKey, dataKey, NULL, NULL, (LPBYTE)data, &dataSize);
LONG ret2 = RegQueryValueEx( hKey, dataKey, NULL, NULL, (LPBYTE)data, &dataSize);
// Breakpoint to see what ret and ret2 are equal to
int j = 0;
}
This is by design. The first call failed because you specified as size that was too small. But what you didn't count on is that it also updated your dataSize variable. To tell you how much memory to allocate so the call can succeed.
So the second call succeeded since you now specify a size that's exactly correct. But without doing the other thing you needed to do, actually make the buffer bigger. Nothing much good can happen when the call then causes a buffer overflow and corrupt your stack frame, be sure to use the /RTC compile option so you'll get a runtime error from that.
You avoided this problem by increasing the buffer size from 256 to 1024. But your code is still incorrect, your program will fail miserably if the registry value ever gets larger than 1024 bytes. Don't use a local array, use the new operator or malloc() to allocate the buffer so it can never fail like this. Or simply fail the call and declare "bad data".
Also note another bug, the dataSize is in bytes but the buffer is TCHAR, not a byte. Which is probably why you didn't corrupt the stack frame, the buffer was big enough by accident. You don't want to rely on accidents like this. Consider a helper class like CRegKey to avoid these kind of mistakes.

playing files after accepting them through open dialog box

I am a new member and joined this site after referring to it loads of times when i was stuck with some programming problems. I am trying to code a media player (Win32 SDK VC++ 6.0) for my college project and I am stuck. I have searched on various forums and msdn and finally landed on the function GetShortPathName which enables me to play through folders and files which have a whitespace in their names. I will paste the code here so it will be much more clearer as to what i am trying to do.
case IDM_FILE_OPEN :
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = "Media Files (All Supported Types)\0*.avi;*.mpg;*.mpeg;*.asf;*.wmv;*.mp2;*.mp3\0"
"Movie File (*.avi;*.mpg;*.mpeg)\0*.avi;*.mpg;*.mpeg\0"
"Windows Media File (*.asf;*.wmv)\0*.asf;*.wmv\0"
"Audio File (*.mp2;*.mp3)\0*.mp2;*.mp3\0"
"All Files(*.*)\0*.*\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT | OFN_CREATEPROMPT;
ofn.lpstrDefExt = "mp3";
if(GetOpenFileName(&ofn))
{
length = GetShortPathName(szFileName, NULL, 0);
buffer = (TCHAR *) malloc (sizeof(length));
length = GetShortPathName(szFileName, buffer, length);
for(i = 0 ; i < MAX_PATH ; i++)
{
if(buffer[i] == '\\')
buffer[i] = '/';
}
SendMessage(hList,LB_ADDSTRING,0,(LPARAM)buffer);
mciSendString("open buffer alias myFile", NULL, 0, NULL);
mciSendString("play buffer", NULL, 0, NULL);
}
return 0;
using the GetShortPathName function i get the path as : D:/Mp3z/DEEPBL~1/03SLEE~1.mp3
Putting this path directly in Play button case
mciSendString("open D:/Mp3jh/DEEPBL~1/03SLEE~1.mp3 alias myFile", NULL, 0, NULL);
mciSendString("play myFile", NULL, 0, NULL);
the file opens and plays fine. But as soon as i try to open and play it through the open file dialog box, nothing happens. Any input appreciated.
It looks like the problem is that you're passing the name of the buffer variable to the mciSendString function as a string, rather than passing the contents of the buffer.
You need to concatenate the arguments you want to pass (open and alias myFile) with the contents of buffer.
The code can also be much simplified by replacing malloc with an automatic array. You don't need to malloc it because you don't need it outside of the block scope. (And you shouldn't be using malloc in C++ code anyway; use new[] instead.)
Here's a modified snippet of the code shown in your question:
(Warning: changes made using only my eyes as a compiler! Handle with care.)
if(GetOpenFileName(&ofn))
{
// Get the short path name, and place it in the buffer array.
// We know that a short path won't be any longer than MAX_PATH, so we can
// simply allocate a statically-sized array without futzing with new[].
//
// Note: In production code, you should probably check the return value
// of the GetShortPathName function to make sure it succeeded.
TCHAR buffer[MAX_PATH];
GetShortPathName(szFileName, buffer, MAX_PATH);
// Add the short path name to your ListBox control.
//
// Note: In C++ code, you should probably use C++-style casts like
// reinterpret_cast, rather than C-style casts!
SendMessage(hList, LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(buffer));
// Build the argument string to pass to the mciSendString function.
//
// Note: In production code, you probably want to use the more secure
// alternatives to the string concatenation functions.
// See the documentation for more details.
// And, as before, you should probably check return values for error codes.
TCHAR arguments[MAX_PATH * 2]; // this will definitely be large enough
lstrcat(arguments, TEXT("open"));
lstrcat(arguments, buffer);
lstrcat(arguments, TEXT("alias myFile"));
// Or, better yet, use a string formatting function, like StringCbPrintf:
// StringCbPrintf(arguments, MAX_PATH * 2, TEXT("open %s alias myFile"),
// buffer);
// Call the mciSendString function with the argument string we just built.
mciSendString(arguments, NULL, 0, NULL);
mciSendString("play myFile", NULL, 0, NULL);
}
Do note that, as the above code shows, working with C-style strings (character arrays) is a real pain in the ass. C++ provides a better alternative, in the form of the std::string class. You should strongly consider using that instead. To call Windows API functions, you'll still need a C-style string, but you can get one of those by using the c_str method of the std::string class.

How to fix garbled text with using WriteFile on a pipe?

I have a Win32 application that I'm making, and it sends a string from one process to another via a named pipe. However, the process that calls ReadFile on the pipe gets the string with some garbled data in it. It returns the number of bytes written correctly, but the last 8 characters or so of the string are garbled.
Here is the code for creating the pipe, and writing to it:
myPipe = CreateNamedPipe(L"\\\\.\\pipe\\testpipe", PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT, 10, 512, 512, 10, NULL);
TCHAR title[128];
GetWindowText(foundHwnd, title, 128);
wstring windowTitle(title);
vector<wstring> splitVec;
boost::split(splitVec, windowTitle, boost::algorithm::is_any_of(wstring(L"|")));
WriteFile(myPipe, splitVec[0].c_str(), splitVec[0].size(), &wrote, NULL);
And here is the code that reads it:
if (WaitNamedPipe(L"\\\\.\\pipe\\testpipe", 5000) == 0) {
MessageBox(NULL, L"Unable to wait for pipe", L"Error", MB_OK);
return false;
}
myPipe = CreateFile(L"\\\\.\\pipe\\testpipe", GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (myPipe == INVALID_HANDLE_VALUE) {
MessageBox(NULL, L"Unable to open pipe", L"Error", MB_OK);
return false;
}
// Other code here...
TCHAR buf[512];
DWORD read;
success = ReadFile(myPipe, buf, 512, &read, NULL);
if (read > 0)
MessageBox(NULL, buf, L"Got Data", MB_OK);
When MessageBox is shown, the end of the string is garbled and I have no idea why. Any ideas?
Thanks!
I think the key here is to make sure that your strings are null terminated and that you send the termination character as well. You shouldn't have to send the entire buffer if the communications is synchronous or if you set it up in PIPE_READMODE_MESSAGE. ReadFile will return when either the specified number of bytes has been read or a write operation completes on the other end of the pipe. I believe that the "garbled" text is really garbage in the read buffer on the client side of the pipe and because you are not transmitting the string termination character, it is including this in the text sent to the message box. Either clear your read buffer before sending or send the string termination character with the message and I think it will work without the overhead of sending a full buffer.
Here is sample client from MSDN. Note how the client sends exactly the number of characters in the message + 1 (including the termination character) and receives into a fixed buffer of size 512. If you look at a server example, you'll see the same pattern.
Some observations on the code you posted:
You need to either 1) explicitly send the null terminated byte, or 2) append one to the data you read.
Since you are reading 512 bytes, you should also be sending exactly 512 bytes.
You can send variable length strings instead by first sending the size of the string, and then sending that many bytes. That way when you read the data you will know how many bytes to read for the actual string.
The problem with what you did will be seen as soon as you send 2 things over the pipe, and you read past what you really want in the first read.
If you are only sending 1 thing over the pipe, you can keep your code, but send size() + 1 when you write to the pipe.
ReadFile / WriteFile were meant to send binary data, not necessarily strings. So you can make a function called ReadString and WriteString that implements my suggestion about reading/writing first the size then the actual string.
Try something like this:
Here is the code for creating the pipe, and writing to it:
myPipe = CreateNamedPipe(L"\\\\.\\pipe\\testpipe", PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT, 10, 512, 512, 10, NULL);
TCHAR title[128];
GetWindowText(foundHwnd, title, 128);
WriteFile(myPipe, title, 128*sizeof(TCHAR), &wrote, NULL);//<---In this case we are sending a null terminated string buffer.
And here is the code that reads it:
if (WaitNamedPipe(L"\\\\.\\pipe\\testpipe", 5000) == 0) {
MessageBox(NULL, L"Unable to wait for pipe", L"Error", MB_OK);
return false;
}
myPipe = CreateFile(L"\\\\.\\pipe\\testpipe", GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (myPipe == INVALID_HANDLE_VALUE) {
MessageBox(NULL, L"Unable to open pipe", L"Error", MB_OK);
return false;
}
// Other code here...
TCHAR buf[128];
DWORD read;
success = ReadFile(myPipe, buf, 128*sizeof(TCHAR), &read, NULL);
if (read > 0)
MessageBox(NULL, buf, L"Got Data", MB_OK);
I ran into this problem with "garbage in the pipe" when writing a generic function to read stdout from any process executed at the command prompt. Therefore, I couldn't alter what was being written to the pipe (as is commonly suggested), I could only alter the read side. So, I "cheated".
If the pipe data didn't end in a null terminator, I replaced the last char with one! It seemed to work for me. I saw this work perfectly where there were nulls and where there were not at the end of my data chunks.
I worried that I might lose a critical last char (and it's possible that you might!), but for my immediate purposes, that didn't happen. You might consider adding a null rather than replacing the end under some circumstances...
Here's code snippit:
const unsigned int MAX_PIPE_PEEKS = 100;
DWORD bytesInPipe = 0;
unsigned int pipePeeks=0;
while( (bytesInPipe==0) && (pipePeeks < MAX_PIPE_PEEKS) )
{
bSuccess = PeekNamedPipe( g_hChildStd_OUT_Rd, NULL, 0, NULL,
&bytesInPipe, NULL );
if( !bSuccess ) return bSuccess; // Bail on critical failure
++pipePeeks;
}
if( bytesInPipe > 0 )
{
// Read the data written to the pipe (and implicitly clear it)
DWORD dwRead;
CHAR *pipeContents = new CHAR[ bytesInPipe ];
bSuccess = ReadFile( g_hChildStd_OUT_Rd, pipeContents,
bytesInPipe, &dwRead, NULL );
if( !bSuccess || dwRead == 0 ) return FALSE; // Bail on critical failure
// "Cheat" - eliminate garbage at the end of the pipe
if( pipeContents[ bytesInPipe ] != '\0' )
pipeContents[ bytesInPipe ] = '\0';
}
UPDATE:
After further testing, I found that this is not quite reliable (shocking, huh?). I think I'm on the right track though for a relatively simple solution. Any ideas for getting this quick patch to work?

Resources