C++ Download File WinInet - 0kb written to file - c++

Can someone tell me what is wrong with my code?
I am trying to download a file from the internet using WinInet. The function connects to the target site just fine, I don't understand why this code isn't working. Can anyone help me out?
Here is my code:
HANDLE hFile = CreateFileW(FilePath, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, NULL, NULL);
if (hFile != INVALID_HANDLE_VALUE || GetLastError() == ERROR_ALREADY_EXISTS)
{
CHAR Buffer[2048];
DWORD BytesRead=0, BytesToRead=0;
DWORD BytesWritten=0, BytesToWrite=0;
SetFilePointer(hFile, 0, 0, FILE_BEGIN);
do
{
if (BytesRead)
{
WriteFile(hFile, Buffer, BytesWritten, &BytesToWrite, FALSE);
}
}
while
(InternetReadFile(hRequest, (LPVOID)Buffer, BytesToRead, &BytesRead) != FALSE);
}
CloseHandle(hFile);
}
hRequest is passed to the function, it is the HINTERNET handle from HttpOpenRequestA.

Your code has some logic problems.
you are misusing GetLastError() when calling CreateFileW(). Regardless of whether the file already exists or not, CreateFileW() will not return INVALID_HANDLE if it successfully creates/opens the file. That is all you need to check for (call GetLastError() only if CreateFileW() fails and you want to find out why). also, there is no need to call SetFilePointer() at all, as CREATE_ALWAYS ensures the opened file is empty, truncating the file if it already exists and has data in it.
your do..while loop should be a while loop instead, so that InternetReadFile() is called first. There is no point in skipping WriteFile() on the first loop iteration. If you use a do..while loop, InternetReadFile() should not be used as the loop condition.
more importantly, you are breaking the loop only if InternetReadFile() fails with an error. You are expecting it to fail when it reaches the end of the response, but it actually returns TRUE and sets BytesRead to 0. This is documented behavior, which you are not handling at all:
InternetReadFile function
InternetReadFile operates much like the base ReadFile function, with a few exceptions. Typically, InternetReadFile retrieves data from an HINTERNET handle as a sequential stream of bytes. The amount of data to be read for each call to InternetReadFile is specified by the dwNumberOfBytesToRead parameter and the data is returned in the lpBuffer parameter. A normal read retrieves the specified dwNumberOfBytesToRead for each call to InternetReadFile until the end of the file is reached. To ensure all data is retrieved, an application must continue to call the InternetReadFile function until the function returns TRUE and the lpdwNumberOfBytesRead parameter equals zero. This is especially important if the requested data is written to the cache, because otherwise the cache will not be properly updated and the file downloaded will not be committed to the cache. Note that caching happens automatically unless the original request to open the data stream set the INTERNET_FLAG_NO_CACHE_WRITE flag.
ReadFile function
When a synchronous read operation reaches the end of a file, ReadFile returns TRUE and sets *lpNumberOfBytesRead to zero.
when calling WriteFile(), you are passing BytesWritten to the nNumberOfBytesToWrite parameter, but BytesWritten is never set to anything other than 0, so nothing gets written to the file. You need to pass BytesRead instead.
With that said, use something more like this:
HANDLE hFile = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// handle error as needed...
}
else
{
BYTE Buffer[2048];
DWORD BytesRead, BytesWritten;
do
{
if (!InternetReadFile(hRequest, Buffer, sizeof(Buffer), &BytesRead))
{
// handle error as needed...
break;
}
if (!BytesRead)
break;
if (!WriteFile(hFile, Buffer, BytesRead, &BytesWritten, FALSE))
{
// handle error as needed...
break;
}
}
while (true);
CloseHandle(hFile);
}
MSDN even has a full example of how to use InternetReadFile():
HOWTO: Using InternetReadFile To Get File
BOOL GetFile (HINTERNET IN hOpen, // Handle from InternetOpen()
CHAR *szUrl, // Full URL
CHAR *szFileName) // Local file name
{
DWORD dwSize;
CHAR szHead[] = "Accept: */*\r\n\r\n";
VOID * szTemp[25];
HINTERNET hConnect;
FILE * pFile;
if ( !(hConnect = InternetOpenUrl ( hOpen, szUrl, szHead,
lstrlen (szHead), INTERNET_FLAG_DONT_CACHE, 0)))
{
cerr << "Error !" << endl;
return 0;
}
if ( !(pFile = fopen (szFileName, "wb" ) ) )
{
cerr << "Error !" << endl;
return FALSE;
}
do
{
// Keep coping in 25 bytes chunks, while file has any data left.
// Note: bigger buffer will greatly improve performance.
if (!InternetReadFile (hConnect, szTemp, 50, &dwSize) )
{
fclose (pFile);
cerr << "Error !" << endl;
return FALSE;
}
if (!dwSize)
break; // Condition of dwSize=0 indicate EOF. Stop.
else
fwrite(szTemp, sizeof (char), dwSize , pFile);
} // do
while (TRUE);
fflush (pFile);
fclose (pFile);
return TRUE;
}

Related

Crash when calling ReadFile after LockFileEx

I have several processes that try to read and write the same file. I want each of them to lock the file so that only one of them accesses it at a time.
I tried this (edit: this is a complete test code this time):
#include "stdafx.h"
#include "Windows.h"
bool test()
{
const char* path = "test.txt";
HANDLE hFile = CreateFileA(path,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("ERROR: Cannot open file %s\n", path);
return false;
}
// Lock the file
{
OVERLAPPED overlapped = {0};
BOOL res = LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, ~0, &overlapped);
if (!res)
{
printf("ERROR: Cannot lock file %s\n", path);
return false;
}
}
DWORD fileSize = GetFileSize(hFile, NULL);
if (fileSize > 0)
{
char* content = new char[fileSize+1];
// Read the file
BOOL res = ReadFile(hFile, content, fileSize, NULL, NULL);
if (!res)
{
printf("ERROR: Cannot read file %s\n", path);
}
delete[] content;
}
const char* newContent = "bla";
int newContentSize = 3;
// Write the file
BOOL res = WriteFile(hFile, newContent, newContentSize, NULL, NULL);
if (!res)
{
//int err = GetLastError();
printf("ERROR: Cannot write to file\n");
}
// Unlock the file
{
OVERLAPPED overlapped = {0};
UnlockFileEx(hFile, 0, ~0, ~0, &overlapped);
}
CloseHandle(hFile);
return true;
}
int _tmain(int argc, _TCHAR* argv[])
{
bool res = test();
return 0;
}
This works fine on my computer, which has Windows 8. But on my colleague's computer, which has Windows 7, it crashes. Specifically, the calls to ReadFile and WriteFile crash, always.
Note that it never enters the code paths with the error printfs. This code triggers no error except for a write at location 0x00000000 in ReadFile (when run on Windows 7).
We tried to also pass the overlapped struct to the ReadFile and WriteFile calls. It prevents the crash but the lock doesn't work anymore, the file is all scrambled (not with this test code, with the real code).
What am I doing wrong?
Looks like your problem is:
lpNumberOfBytesRead [out, optional] argument is null in your call.
This parameter can be NULL only when the lpOverlapped parameter is not NULL.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx
Heres your problem :
You are missing a necessary struct-member and:
0 and ~0 and {0} are all bad code, constant expressions like these will always produce unepected results -- WINAPI doesnt work like libc, parameters are not always compared against constants, instead they are tested against/via macros and other preprocessor-definitions themselves so passing constant values or initializing WINAPI structs with constants will often lead to errors like these.
After years of experimenting i have found that there is only one surefire way of avoiding them, i will express it in corrected code :
OVERLAPPED overlapped;
overlapped.hEvent = CreateEvent( ........... ); // put valid parameters here!
UnlockFileEx(hFile, 0 /*"reserved"*/, ULONG_MAX, ULONG_MAX, &overlapped);
please read this carefully : http://msdn.microsoft.com/en-us/library/windows/desktop/aa365716%28v=vs.85%29.aspx

C++ WriteFile only writing 4 bytes

Here's what I'm trying to achieve; I'm hooking onto the HttpSendRequest function (on Xbox it's XHttp) and trying dump the certificate that's in pcszHeaders which has the size of 0x1F0E.
Now the problem; it only seems to write 4 bytes, I've even tried allocating extra memory and setting each bit to 0 to see if it's the size of Headers and it continues to only write 4 bytes. I've been able to dump pcszHeaders remotely because I got the address whilst debugging but I need to dump it at run-time.
Something I notice whilst debugging - The address of pcszHeaders only shows in locals until it reaches;
printf("XHttpSendRequest: %s\n", "Creating Certificate.bin...");
Once it reaches the printf() above the address changes to 0x00000000 (bad ptr) but it still writes the first byte of correct data of pcszHeaders correctly but nothing more.
Here is the entire hook;
BOOL XHTTP_SEND_REQUEST_HOOK(
HINTERNET hRequest,
const CHAR *pcszHeaders,
DWORD dwHeadersLength,
const VOID *lpOptional,
DWORD dwOptionalLength,
DWORD dwTotalLength,
DWORD_PTR dwContext)
{
if(pcszHeaders != XHTTP_NO_ADDITIONAL_HEADERS)
{
printf("XHttpSendRequest: %s\n", "Creating Certificate.bin...");
// Setup expansion
doMountPath("Hdd:", "\\Device\\Harddisk0\\Partition1");
//create our file
HANDLE fileHandle = CreateFile("Hdd:\\Certificate.bin", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
//does file exist?
if(GetLastError()!=ERROR_ALREADY_EXISTS
||fileHandle!=INVALID_HANDLE_VALUE)
{
printf("XHttpSendRequest: %s\n", "Writing to file...");
DWORD wfbr;
//write to our file
if(WriteFile(fileHandle, pcszHeaders, 0x2000, &wfbr, NULL))
{
printf("XHttpSendRequest: %s\n", "File written!");
printf("%s\n", "Request has ended.");
CloseHandle(fileHandle);
return XHttpSendRequest(hRequest, pcszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength, dwContext);
}
}
}
}
EDIT: I've changed the code slightly and I've copied pcszHeaders data into another section of memory that I've created and my pointers seems to have all the correct data and I've tried Writing it to file and it still only writes 4 bytes. I've even used sizeof() instead of hard-coded 0x2000.
pcszHeaders is a char* pointer. sizeof(pcszHeaders) is 4 in a 32bit app (8 in a 64bit app). You need to use the dwHeadersLength parameter instead, which tells you how many characters are in pcszHeaders.
Also, your GetLastError() check after CreateFile() is wrong. If CreateFile() fails for any reason other than ERROR_ALREADY_EXISTS, you are entering the code block and thus writing data to an invalid file handle. When using CREATE_NEW, CreateFile() returns INVALID_HANDLE_VALUE if the file already exists. You don't need to check GetLastError() for that, checking for INVALID_HANDLE_VALUE by itself is enough. If you want to overwrite the existing file, use CREATE_ALWAYS instead.
You are also leaking the file handle if WriteFile() fails.
And you are calling the original HttpSendRequest() only if you successfully write headers to your file. If there are no headers, or the create/write fails, you are not allowing the request to proceed. Is that what you really want?
Try this instead:
BOOL XHTTP_SEND_REQUEST_HOOK(
HINTERNET hRequest,
const CHAR *pcszHeaders,
DWORD dwHeadersLength,
const VOID *lpOptional,
DWORD dwOptionalLength,
DWORD dwTotalLength,
DWORD_PTR dwContext)
{
if (pcszHeaders != XHTTP_NO_ADDITIONAL_HEADERS)
{
printf("XHttpSendRequest: Creating Certificate.bin...\n");
// Setup expansion
doMountPath("Hdd:", "\\Device\\Harddisk0\\Partition1");
//create our file
HANDLE fileHandle = CreateFile("Hdd:\\Certificate.bin", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
//is file open?
if (fileHandle != INVALID_HANDLE_VALUE)
{
printf("XHttpSendRequest: Writing to file...\n");
DWORD wfbr;
//write to our file
if (WriteFile(fileHandle, pcszHeaders, dwHeadersLength, &wfbr, NULL))
printf("XHttpSendRequest: File written!\n");
else
printf("XHttpSendRequest: Error writing to file: %u\n", GetLastError());
CloseHandle(fileHandle);
}
else
printf("XHttpSendRequest: Error creating file: %u\n", GetLastError());
}
printf("Request has ended.\n");
return XHttpSendRequest(hRequest, pcszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength, dwContext);
}
Finally the problem has been solved!
First I created an empty array for the data to be stored.
CHAR xtoken[0x2000];
memset(xtoken, 0, 0x2000);
The first part of the hook is to store the header data.
DWORD bufferLength = dwHeadersLength;
memcpy(xtoken, pcszHeaders, bufferLength);
I then write the data to file
WriteFile(fileHandle, (void*)&xtoken, bufferLength, &wfbr, NULL))
Success! I guess the problem was that parameter 2 of WriteFile() was incorrect.

how to append text to a file in windows?

Everytime this function is called the old text data is lost?? Tell me how to maintain previous data and appending new data.
This function is called 10 times:
void WriteEvent(LPWSTR pRenderedContent)
{
HANDLE hFile;
DWORD dwBytesToWrite = ((DWORD)wcslen(pRenderedContent)*2);
DWORD dwBytesWritten = 0;
BOOL bErrorFlag = FALSE;
printf("\n");
hFile = CreateFile(L"D:\\EventsLog.txt", FILE_ALL_ACCESS, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("Terminal failure: Unable to open file \"EventsLog.txt\" for write.\n");
return;
}
printf("Writing %d bytes to EventsLog.txt.\n", dwBytesToWrite);
bErrorFlag = WriteFile(
hFile, // open file handle
pRenderedContent, // start of data to write
dwBytesToWrite, // number of bytes to write
&dwBytesWritten, // number of bytes that were written
NULL); // no overlapped structure
if (FALSE == bErrorFlag)
{
printf("Terminal failure: Unable to write to file.\n");
}
else
{
if (dwBytesWritten != dwBytesToWrite)
{
printf("Error: dwBytesWritten != dwBytesToWrite\n");
}
else
{
printf("Wrote %d bytes to EventsLog.txt successfully.\n",dwBytesWritten);
}
}
CloseHandle(hFile);
}
You should pass FILE_APPEND_DATA as the dwDesiredAccess to CreateFile, as documented under File Access Rights Constants (see sample code at Appending One File to Another File). While this opens the file using the correct access rights, your code is still responsible for setting the file pointer. This is necessary, because:
Each time a file is opened, the system places the file pointer at the beginning of the file, which is offset zero.
The file pointer can be set using the SetFilePointer API after opening the file:
hFile = CreateFile( L"D:\\EventsLog.txt", FILE_APPEND_DATA, 0x0, nullptr,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr );
if ( hFile == INVALID_HANDLE_VALUE ) {
printf( "Terminal failure: Unable to open file \"EventsLog.txt\" for write.\n" );
return;
}
// Set the file pointer to the end-of-file:
DWORD dwMoved = ::SetFilePointer( hFile, 0l, nullptr, FILE_END );
if ( dwMoved == INVALID_SET_FILE_POINTER ) {
printf( "Terminal failure: Unable to set file pointer to end-of-file.\n" );
return;
}
printf("Writing %d bytes to EventsLog.txt.\n", dwBytesToWrite);
bErrorFlag = WriteFile( // ...
Unrelated to your question, the calculation of dwBytesToWrite should not use magic numbers. Instead of * 2 you should probably write * sizeof(*pRenderedContent). The parameter to WriteEvent should be constant as well:
WriteEvent(LPCWSTR pRenderedContent)
The parameter for appending data to a file is FILE_APPEND_DATA instead of FILE_ALL_ACCESS in the CreateFile function.
Here is an example: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363778(v=vs.85).aspx

Async call to ReadFile function returns 6 error code

I wrote the c++ code below in order to read a file asynchronously:
#define BUF_SIZE 1024
HANDLE hFile;
DWORD NumberOfBytesRead = 0;
BYTE *buf = (BYTE*)malloc(BUF_SIZE*sizeof(BYTE));
OVERLAPPED overlapped;
overlapped.Offset = overlapped.OffsetHigh = 0;
hFile = CreateFile("C:\\Users\\Desktop\\FOO.cpp",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
printf("invalid hfile\n");
int i;
i= ReadFile(hFile,
&buf,
BUF_SIZE,
&NumberOfBytesRead,
&overlapped);
if(!i && GetLastError() != ERROR_IO_PENDING)
printf ("ReadFile failed with error %d.\n", GetLastError());
else
{
WaitForSingleObject(&(overlapped.hEvent), 0);
printf("here it is %d",NumberOfBytesRead );
}
CloseHandle(hFile);
But the return value from ReadFile is 0, and the last error equals to 6.
Does anyone have any idea why? and what does 6 error code say?
Thanks!.
Maybe your problem is with OVERLAPPED structure, which you don't initialize properly.
See: "Any unused members of this structure should always be initialized to zero before the structure is used in a function call. Otherwise, the function may fail and return ERROR_INVALID_PARAMETER." in remarks here: here. You can use memset to clear all of it.
Error code 6 means invalid handle.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
Which seems odd since you check for invalid handle value. Not sure if this helped you but I might try opening the file synchronously first.

how in C++ send file to browser

I need to send file from my directory to user. The problem file was not send.
Can any one help me?
My code is like:
CHttpServerContext* pCtxt;
// ... there i set headers for open
DWORD dwRead;
CString fileName = "c:\txt.doc";
HANDLE hFile = CreateFile (fileName, GENERIC_READ, FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, (HANDLE) NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return;
}
int c = 0;
CHAR szBuffer [2048];
do
{
if (c++ > 20) {
break;
return;
}
// read chunk of the file
if (!ReadFile (hFile, szBuffer, 2048, &dwRead, NULL))
{
return;
}
if (!dwRead)
// EOF reached, bail out
break;
// Send binary chunk to the browser
if (!pCtxt->WriteClient( szBuffer, &dwRead, 0))
{
return;
}
}
while (1);
CloseHandle (hFile);
}
Doctor, I'm sick. What's wrong with me?
I mean, you give almost no information about what happened.
Do you know if some function returned you an error in your code?
Why do you abort the loop after 20 iterations? This limits you to 40KB.
How exactly do you initialize CHttpServerContext?
You might use high-performance TransmitFile function if you just send the file as-is.
What is your client? How do you know it didn't get the file?
No point in re-inventing the wheel - just use the TransmitFile API instead - it is built into CHttpServerContent::TransmitFile().