cuFFT cannot recover after an error - c++

I cannot find a way to start cuFFT processing after a previous unsuccessful launch.
Here is a minimal example. The main idea is as follows: we create a simple cuFTT processor which can manage its resources ( device memory and cuFFT plans). We check that this processor does make FFT. Then we ask to create too many plans, thus we enforce cuFFT error. Then we release all resources and try to repeat the successful launch. However, the processor can do nothing after the failure.
Firstly, here is a rather long preamble:
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
#include <vector>
using std::vector;
#include "cuda_runtime.h"
#include "cufft.h"
// cuFFT API errors
static char* _cufftGetErrorEnum( cufftResult_t error )
{
switch ( error )
{
case CUFFT_SUCCESS:
return "CUFFT_SUCCESS";
case CUFFT_INVALID_PLAN:
return "cuFFT was passed an invalid plan handle";
case CUFFT_ALLOC_FAILED:
return "cuFFT failed to allocate GPU or CPU memory";
// No longer used
case CUFFT_INVALID_TYPE:
return "CUFFT_INVALID_TYPE";
case CUFFT_INVALID_VALUE:
return "User specified an invalid pointer or parameter";
case CUFFT_INTERNAL_ERROR:
return "Driver or internal cuFFT library error";
case CUFFT_EXEC_FAILED:
return "Failed to execute an FFT on the GPU";
case CUFFT_SETUP_FAILED:
return "The cuFFT library failed to initialize";
case CUFFT_INVALID_SIZE:
return "User specified an invalid transform size";
// No longer used
case CUFFT_UNALIGNED_DATA:
return "CUFFT_UNALIGNED_DATA";
case CUFFT_INCOMPLETE_PARAMETER_LIST:
return "Missing parameters in call";
case CUFFT_INVALID_DEVICE:
return "Execution of a plan was on different GPU than plan creation";
case CUFFT_PARSE_ERROR:
return "Internal plan database error";
case CUFFT_NO_WORKSPACE:
return "No workspace has been provided prior to plan execution";
case CUFFT_NOT_IMPLEMENTED:
return "CUFFT_NOT_IMPLEMENTED";
case CUFFT_LICENSE_ERROR:
return "CUFFT_LICENSE_ERROR";
}
return "<unknown>";
}
// check cuda runtime calls
bool cudaCheck( cudaError_t err )
{
if ( err != cudaSuccess )
{
cudaDeviceSynchronize();
cerr << cudaGetErrorString( cudaGetLastError() ) << endl;
return false;
}
return true;
}
// check cuFFT calls
bool cufftCheck( cufftResult_t err )
{
if ( err != CUFFT_SUCCESS )
{
cerr << _cufftGetErrorEnum( err ) << endl;
return false;
}
return true;
}
Next, we define a simple cuFFT processor which can manage its resources (device memory and cuFFT plans)
class CCuFFT_Processor
{
vector<cufftHandle> _plans;
cufftComplex *_data;
size_t _data_bytes;
// Release resouces
bool ReleaseAll();
bool ReleaseMemory();
bool ReleasePlans();
public:
CCuFFT_Processor() :
_data( NULL ),
_data_bytes( 0 )
{
_plans.reserve( 32 );
_plans.clear();
}
~CCuFFT_Processor()
{
ReleaseAll();
}
bool Run();
bool Alloc( size_t data_len, size_t batch_len );
};
Here is how we are going to release resources:
bool CCuFFT_Processor::ReleaseMemory()
{
bool chk = true;
if ( _data != NULL )
{
chk = cudaCheck( cudaFree( _data ) );
_data = NULL;
_data_bytes = 0;
}
return chk;
}
bool CCuFFT_Processor::ReleasePlans()
{
bool chk = true;
for ( auto & p : _plans )
chk = chk && cufftCheck( cufftDestroy( p ) );
_plans.clear();
return chk;
}
bool CCuFFT_Processor::ReleaseAll()
{
bool chk = true;
chk = chk && cudaCheck( cudaDeviceSynchronize() );
chk = chk && ReleaseMemory();
chk = chk && ReleasePlans();
chk = chk && cudaCheck( cudaDeviceReset() );
return chk;
}
Here is the implementation of the main functionality:
bool CCuFFT_Processor::Alloc( size_t data_len, size_t batch_len )
{
bool chk = true;
size_t bytes = sizeof( cufftComplex ) * data_len * batch_len;
// CUDA resources
if ( _data_bytes < bytes )
chk = chk && ReleaseMemory();
if ( _data == NULL )
{
chk = chk && cudaCheck( cudaMalloc( (void **)&_data, bytes ) );
_data_bytes = bytes;
}
// cuFFT resources
chk = chk && ReleasePlans();
for ( size_t b = 1; chk && ( b <= batch_len ); b *= 2 )
{
cufftHandle new_plan;
chk = cufftCheck(
cufftPlan1d( &new_plan, int(data_len), CUFFT_C2C, int(b) ) );
if ( chk )
_plans.push_back( new_plan );
}
if ( !chk )
ReleaseAll();
return chk;
}
bool CCuFFT_Processor::Run()
{
bool chk = true;
chk = cufftCheck(
cufftExecC2C( *_plans.rbegin(), _data, _data, CUFFT_FORWARD ) );
if ( !chk )
ReleaseAll();
chk = chk && cudaCheck( cudaDeviceSynchronize() );
return chk;
}
Finally, the program
int main()
{
size_t batch = 1 << 5;
size_t length = 1 << 21;
CCuFFT_Processor proc;
// Normal run
if ( proc.Alloc( length, batch ) )
proc.Run();
// Run with error
length *= 4;
if ( proc.Alloc( length, batch ) )
proc.Run();
// Normal run : check recovery
length /= 4;
if ( proc.Alloc( length, batch ) )
proc.Run();
return EXIT_SUCCESS;
}
If I use a small length = 1 << 18, then no errors occur. However, for the large length = 1 << 21 two errors appear:
cuFFT failed to allocate GPU or CPU memory
Failed to execute an FFT on the GPU
The first error is an expected one, we have done this intentionally. But the second one is not. Although the device was reset and new resources were successfully allocated, cuFFT failed to execute an FFT.
I use GTX 970. I tried all combinations of: cuda 6.5, cuda 7.5, 32-bit platform, 64-bit platform etc, but unsuccessfully.

This was apparently a problem limited to the out-of-memory error recovery behaviour of older versions of cuFFT, and was rectified during the CUDA 8 release cycle. If (6 years later) you are still using a pre-CUDA 8 version of cuFFT, please update to something more modern and this issue will be resolved.
[answer assembled from comments and added as a community wiki entry to get the question off the unanswered list for the CUDA and cuFFT tags]

Related

C++ most robust way to copy a file

Okay, so I know that disk writing errors are very rare, so please just look past that because the data I am working with is very incredibly important (like SSIDs kind of important). So, I want to copy a file in the absolute most robust way using the absolute minimal amount of memory to do so. So far, this is was far as I have got. It sucks up a lot of memory, but I can't find the source. The way it works is by rechecking tons of times until it gets a confirmed result (it may increase the number of false positives for errors by a lot, but it might reduce the chance of an actual error by a big margin). Also, the sleep at the bottom is so you have time to analyze the programs overall performance using the windows task manager.
#include <cstdio> // fopen, fclose, fread, fwrite, BUFSIZ
#include <cstdlib>
#include <unistd.h>
#include <iostream>
using namespace std;
__inline__ bool copy_file(const char* From, const char* To)
{
FILE infile = (*fopen(From, "rb"));
FILE outfile = (*fopen(To, "rwb+"));
setvbuf( &infile, nullptr, _IONBF, 0);
setvbuf( &outfile, nullptr, _IONBF, 0);
fseek(&infile,0,SEEK_END);
long int size = ftell(&infile);
fseek(&infile,0,SEEK_SET);
unsigned short error_amount;
bool success;
char c;
char w;
char l;
for ( fpos_t i=0; (i != size); ++i ) {
error_amount=0;
fsetpos( &infile, &i );
c = fgetc(&infile);
fsetpos( &infile, &i );
success=true;
for ( l=0; (l != 126); ++l ) {
fsetpos( &infile, &i );
success = ( success == ( fgetc(&infile)==c ) );
}
while (success==false) {
fsetpos( &infile, &i );
if (error_amount==32767) {
cerr << "There were 32768 failed attemps at accessing a part of the file! exiting the program...";
return false;
}
++error_amount;
//cout << "an error has occured at position ";
//printf("%d in the file.\n", (int)i);
c = fgetc(&infile);
fsetpos( &infile, &i );
success=true;
for ( l=0; (l != 126); ++l ) {
fsetpos( &infile, &i );
success = ( success == ( fgetc(&infile)==c ) );
}
}
fsetpos( &infile, &i );
fputc( c, &outfile);
fsetpos( &outfile, &i );
error_amount=0;
w = fgetc(&infile);
fsetpos( &outfile, &i );
success=true;
for ( l=0; (l != 126); ++l ) {
fsetpos( &outfile, &i );
success = ( success == ( fgetc(&outfile)==w ) );
}
while (success==false) {
fsetpos( &outfile, &i );
fputc( c, &outfile);
if (error_amount==32767) {
cerr << "There were 32768 failed attemps at writing to a part of the file! exiting the program...";
return false;
}
++error_amount;
w = fgetc(&infile);
fsetpos( &infile, &i );
success=true;
for ( l=0; (l != 126); ++l ) {
fsetpos( &outfile, &i );
success = ( success == ( fgetc(&outfile)==w ) );
}
}
fsetpos( &infile, &i );
}
fclose(&infile);
fclose(&outfile);
return true;
}
int main( void )
{
int CopyResult = copy_file("C:\\Users\\Admin\\Desktop\\example file.txt","C:\\Users\\Admin\\Desktop\\example copy.txt");
std::cout << "Could it copy the file? " << CopyResult << '\n';
sleep(65535);
return 1;
}
So, if my code is on the right track with the best way, then what can be done with my code to improve it? But, if my code is totally off with the best solution, then what is the best solution? Please note that this question is essentially about detection of rare disk writing errors for the application of copying very very very very (etc.) important data.
I would just copy the file without any special checks, and in the end I would read the file and compare its hash value to the expected one. For a hash function, I would use MD5 or SHA-1.
#include <boost/filesystem.hpp>
#include <iostream>
int main()
{
try
{
boost::filesystem::copy_file( "C:\\Users\\Admin\\Desktop\\example file.txt",
"C:\\Users\\Admin\\Desktop\\example copy.txt" );
}
catch ( boost::filesystem::filesystem_error const & ex )
{
std::cerr << "Copy failed: " << ex.what();
}
}
This will call the arguably most robust implementation available -- the one provided by the operating system -- and report any failure.
My point being:
The chance of having your saved data end up corrupted are astronomically small to begin with.
Any application where this might actually be an issue should be running on redundant storage, a.k.a. RAID arrays, filesystems doing checksums (like Btrfs, ZFS) etc., again reducing chance of failure significantly.
Doing complex things in home-grown I/O functions, on the other hand, increases the probability of mistakes and / or false negatives immensely.

Branching or ftell() that is Slowing?

I was viewing the file agents.h on my windows OS,and I wanted to see the c++ code without comments.i striped them out to see the code more clearly with my old program but i was surprised that it took like 2 seconds to finish.the size of the file is 605KB,so it isn't so bad.Why is it that slow.I suspect it is the function ftell() that is doing it,but i can't really tell.Is it branching that is slowing or ftell()?, if ftell(),then what is a better way to throw the FILE pointer back?
EDIT
#include <stdio.h>
#include <time.h>
#define NOT_COMMENT (!DOUBLESLASH_Comment && !ASTERISK_SLASH_Comment)
int main(int argc,char *argv[])
{
clock_t t1 = clock();
FILE *input , *output;
if( fopen_s(&input,argv[1],"r") )
{
printf("error opening file %s\n",argv[1]);
return 0;
}
if( fopen_s(&output,argv[2],"w") )
{
printf("error opening file %s\n",argv[2]);
return 0;
}
char c , d;
//escape flag
bool DOUBLESLASH_Comment = 0 , ASTERISK_SLASH_Comment = 0 , flag = 0;
/* single quotes / double quotes */
int s_QUOTED = 0 , d_QUOTED = 0;
while( (c=getc(input)) != EOF )
{
switch(c)
{
case '\\':
{
if( NOT_COMMENT )
{
if( flag == 1 )
flag = 0;
else
flag = 1;
}
}break;
case '\'':
{
if( NOT_COMMENT && !d_QUOTED )
{
if( !flag )
{
s_QUOTED++;
}
}
}break;
case '"':
{
if( NOT_COMMENT && !flag )
{
if( !s_QUOTED )
{
d_QUOTED++;
}
}
}break;
case '/':
{
if( NOT_COMMENT && !d_QUOTED )
{
if( (d=getc(input)) == '*' )
{
ASTERISK_SLASH_Comment = 1;
}
else if( d == '/' )
{
DOUBLESLASH_Comment = 1;
}
else
{
if( d != EOF )
{
ungetc(d,input);
}
}
}
}break;
case '*':
{
if( ASTERISK_SLASH_Comment )
{
if( (d=getc(input)) == '/')
{
if( (c=getc(input)) == EOF )
return 0;
ASTERISK_SLASH_Comment = 0;
}
else
{
if( d != EOF )
{
ungetc(d,input);
}
}
}
}break;
case '\n':
{
if( DOUBLESLASH_Comment )
{
DOUBLESLASH_Comment = 0;
}
}break;
}
if( NOT_COMMENT && c != '\\' ) flag = 0;
if( d_QUOTED == 2 ) d_QUOTED = 0;
if( s_QUOTED == 2 ) s_QUOTED = 0;
if( NOT_COMMENT )
{
fprintf(output,"%c",c);
}
}
fclose(input);
fclose(output);
clock_t t2 = clock();
double elapsed = (double)(t2 - t1) / CLOCKS_PER_SEC;
printf("time elapsed : %f\n",elapsed);
}
Without actually measuring the speed of your code in a profiler (and with the file you use as input, since one I use may have a different set of comments, etc that trigger a different behaviour), it's hard to say for sure. But it looks like you use fseek( ... ) simply to move back one character. In which case writing your own function for a one character lookahead would be a much better choice.
Something like this:
char lookahead = ' ';
bool havelookahead = false;
char getNextChar(FILE *input)
{
if (havelookahead)
{
havelookahead = false;
return lookahead;
}
return getc(input);
}
char peekChar(FILE *input)
{
if (!havelookahead)
{
lookahead = getc(input);
havelookahead = true;
}
return lookahead;
}
Then replace your getc with getNextChar in the beginning of the loop, and where you check the next character with peekChar (followed by a dummy getNextChar() to consume it).
This is a useful pattern in general for parsing - both at character level and at token level, so it's good learning to understand how this works.
You can also use the standard ungetc to "put back" your character that you looked at.
Whether this makes your code run significantly faster or not is hard to say, as I said in the beginning.
I cannot compile your code, so I cannot make tests. But I suspect that the bottleneck is fseek rather than ftell. Rejecting a character is a common task in parsing files... and should be implemented by the library or some intermediate layer with some buffering. In this case (rejection of a single character) you can use ungetc to achieve that.
So you should replace
fseek( file , ( ftell(file) - 1 ) , SEEK_SET );
with
ungetc('*', file); // ungetc('/', file); the second time.

Wrong error code

I'm using portaudio to play a sound. I want to be able to select the output via the UI. I managed it like that :
PaError err = Pa_Initialize();
if( err != paNoError )
return false;
qDebug() <<"Port audio succeed initialization !";
int numDevices;
numDevices = Pa_GetDeviceCount();
if( numDevices <= 0 )
{
qDebug() << "ERROR: Pa_CountDevices returned " << numDevices;
return false;
}
const PaDeviceInfo *deviceInfo;
bool isThereOutput = false;
int i = 0;
while(i < numDevices and !isThereOutput)
{
deviceInfo = Pa_GetDeviceInfo( i );
isThereOutput = (deviceInfo->maxOutputChannels > 0);
i++;
}
if(!isThereOutput)
{
qDebug() << "No output device";
return false;
}
PaError errorOpening;
if(outputDevice != "")
{
PaStreamParameters outputDeviceInfo;
int numDevices = Pa_GetDeviceCount();
const PaDeviceInfo *deviceInfo;
for(int i = 0; i<numDevices; i++ )
{
deviceInfo = Pa_GetDeviceInfo( i );
if(deviceInfo->maxOutputChannels > 0 && deviceInfo->name == outputDevice)
{
outputDeviceInfo.device = i;
outputDeviceInfo.channelCount = 1;
outputDeviceInfo.sampleFormat = paInt8;
outputDeviceInfo.suggestedLatency = deviceInfo->defaultLowOutputLatency;
}
}
if(outputDeviceInfo.channelCount > 1)
{
errorOpening = Pa_OpenStream(&stream, NULL, &outputDeviceInfo, SAMPLE_RATE, FRAME_PER_BUFFER, paNoFlag, audioCallback, this);
}
}
if(outputDevice == "" or errorOpening != paNoError)
{
if(errorOpening != paNoError)
qDebug() << "Can't open selected device ("<< outputDevice <<"), switching to the default one. Error : " << Pa_GetErrorText(errorOpening);
errorOpening = Pa_OpenDefaultStream( &stream,
0, /* no input channels */
1, /* mono output */
paInt8, /* 8 bits output */
SAMPLE_RATE,
FRAME_PER_BUFFER, /* frames per buffer, i.e. the number
of sample frames that PortAudio will
request from the callback. Many apps
may want to use
paFramesPerBufferUnspecified, which
tells PortAudio to pick the best,
possibly changing, buffer size.*/
audioCallback, /* this is your callback function */
this ); /*This is a pointer that will be passed to
your callback*/
}
if(errorOpening != paNoError)
return false;
if(Pa_StartStream( stream ) != paNoError)
return false;
And it fails :
Can't open selected device ( "Sortie intégr" ), switching to the default one. Error : Invalid error code (value greater than zero)
But I can't figure why OpenStream fails with a strange error code and Pa_OpenDefaultStream works like a charm.
So :
Why does it fails ?
Why does it throw a wrong error code ?
I assume you use C++ (though there are several curious and and or in your code.)
If your for loop didn't find any PaDeviceInfo which satisfies eviceInfo->maxOutputChannels > 0 && deviceInfo->name == outputDevice, then your outputDeviceInfo is left un-initialized. That means its channelConnect can have any values including large negative values. Then Pa_OpenStream isn't invoked and your errorOpening is also left un-initialized. I bet that's the reason of Invalid error code (value greater than zero) when you feed it into Pa_GetErrorText().

Determine if an number (encoded as a string) will fit in a 64-bit integer in C++?

I'm looking for a portable way to a) convert a string to a 64-bit signed integer (int64_t), and b) determine if it won't fit (overflows). Is there any way to do this?
strtoll is pretty portable anymore. And if not in your case, you could always crib the GNU C runtime library and add that to your project...
errno = 0;
long long val = strtoll (string, NULL, 0);
if (errno == ERANGE)
// there was an overflow conversion error
Run through the characters of the string one at a time and make your integer. if the character you're parsing will cause an overflow, then you know you're about to overflow. this code is the basic idea- doesn't handle errors or negative numbers, but should give you the idea...
bool ConvertToInt( const char* inString, int64_t& outInt )
{
int64_t kInt64Max = 0x7fffffffffffffff;
const char* c = inString;
outInt = 0;
while( *c != '\0' )
{
int charValue = *c - '0';
//outInt will be assigned outInt * 10 + charValue, so to check if that will overflow
//use algebra and move stuff around so that you can do the math without overflowing
if( outInt > ( kInt64Max - charValue ) / 10 )
{
//overflow
return false;
}
outInt = outInt * 10 + charValue;
++c;
}
return true;
}
if you want full credit on your homework, make sure to handle negative numbers and non-numeric characters. [ Edited to increment c ptr- thanks for the tip :) )
So a 'long long'? An signed int64_ can hold from –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, and you can just see that from the string. For example, with std::string:
int stringLength;
string myString("123456789");
stringLength = myString.length();
That code gets the length of your string. To determine whether it overflows just check the number of digits, and if there might be an overflow, check the first digit. To convert to int64_, use casting:
http://www.learncpp.com/cpp-tutorial/44-type-conversion-and-casting/
That link should answer your question. (However it's for C-style strings.) And one last clarification, is your string a std::string or not?
To cater for Visual C++ 10.0 (as I write this 11.0 is in beta), which apparently does not have strtoll or any equivalent,
#include <assert.h> // assert
#include <errno.h> // errno
#include <stdint.h> // int64_t
#include <string> // std::string
#include <stdexcept> // std::runtime_error, std::range_error
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, strtoll
#include <iostream>
using namespace std;
#if defined( _MSC_VER )
# if _MSC_VER <= 1600
# include <ole2.h>
inline long long strtoll( char const *str, char **str_end, int base )
{
assert(( "Only base 10 for Visual C++ 10 and earlier", base == 10 ));
std::wstring const ws( str, str + strlen( str ) );
LONG64 result;
HRESULT const hr = VarI8FromStr(
ws.c_str(), 0, LOCALE_NOUSEROVERRIDE, &result
);
switch( hr )
{
case S_OK:
if( str_end != 0 )
{
*str_end = const_cast<char*>( str + strlen( str ) );
}
return result;
case DISP_E_OVERFLOW:
errno = ERANGE;
if( str_end != 0 )
{
*str_end = const_cast<char*>( str );
}
return (*str == '-'? LLONG_MIN : LLONG_MAX);
default:
errno = EILSEQ;
if( str_end != 0 )
{
*str_end = const_cast<char*>( str );
}
return 0;
}
}
# endif
#endif
template< class Type >
bool hopefully( Type const& v ) { return !!v; }
bool throwX( string const& s ) { throw runtime_error( s ); }
bool throwRangeX( string const& s ) { throw range_error( s ); }
int64_t int64From( string const& s )
{
errno = 0;
int64_t const result = strtoll( s.c_str(), nullptr, 10 );
if( errno == ERANGE )
throwRangeX( "int64From: specificed nr too large" );
else if( errno != 0 )
throwX( "int64From: parsing failed" );
return result;
}
int main( int argc, char** argv )
{
try
{
int64_t const x = int64From( argv[argc - 1] );
wcout << x << endl;
return EXIT_SUCCESS;
}
catch( runtime_error const& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
Then for Visual C++ 10 and earlier, link with [oleaut32.lib].
I tested this with MinGW g++ and Visual C++.
PS: Alternatively you can just an istringstream, but it does not reliably tell you why it failed when it fails – and it seems to be a requirement to detect overflow as such.
Based on a helpful response from Joshua Glazer, I came up with the following solution which does error checking and also works for negative integers:
#define __STDC_LIMIT_MACROS
#include <stdint.h>
// convert a string to an integer, return whether successful
bool string_to_int(string in, int64_t &out) {
size_t pos = 0;
if (in.size() == 0)
return false;
if (in[pos] == '+')
pos++;
out = 0;
if (in[pos] == '-') {
pos++;
while (pos < in.size()) {
if (in[pos] < '0' || in[pos] > '9')
return false;
int c = in[pos]-'0';
if (out < (INT64_MIN+c)/10)
return false;
out = out*10-c;
pos++;
}
} else {
while (pos < in.size()) {
if (in[pos] < '0' || in[pos] > '9')
return false;
int c = in[pos]-'0';
if (out > (INT64_MAX-c)/10)
return false;
out = out*10+c;
pos++;
}
}
return true;
}

Downloading Binary Files With Wininet

I am currently programming a simple program, I want to distribute to my friends. What I am trying to accomplish, is to write some external binary files to a buffer from the internet, upon starting the program. To do this, I am using windows internet(wininet). Currently, I am using InternetReadFile to write the file to a buffer which I use later in the program. However, the File is not read completely, as in, the resulting size is much smaller than the size of the file on the server, when it should be the same.
I would like to do this, without using any external libraries.
Any idea of what could solve my problem?
Thanks,
Andrew
The documentation makes the following remarks:
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.
Basically, there is no guarantee that the function to read exactly dwNumberOfBytesToRead. Check out how many bytes were actually read using the lpdwNumberOfBytesRead parameter.
Moreover, as soon as the total file size is larger than dwNumberOfBytesToRead, you will need to invoke the call multiple times. Because it cannot read more than dwNumberOfBytesToRead at once.
If you have the total file size in advance, the loop takes the following form:
::DWORD error = ERROR_SUCCESS;
::BYTE data[SIZE]; // total file size.
::DWORD size = 0;
::DWORD read = 0;
do {
::BOOL result = ::InternetReadFile(stream, data+size, SIZE-size, &read);
if ( result == FALSE ) {
error = ::GetLastError();
}
}
while ((error == ERROR_SUCCESS) && (read > 0) && ((size+=read) < SIZE));
// check that `SIZE` was correct.
if (size != SIZE) {
}
If not, then you need to write the data in the buffer to another file instead of accumulating it.
EDIT (SAMPLE TEST PROGRAM):
Here's a complete program that fetches StackOverflow's front page. This downloads about 200K of HTML code in 1K chunks and the full page is retrieved. Can you run this and see if it works?
#include <Windows.h>
#include <Wininet.h>
#include <iostream>
#include <fstream>
namespace {
::HINTERNET netstart ()
{
const ::HINTERNET handle =
::InternetOpenW(0, INTERNET_OPEN_TYPE_DIRECT, 0, 0, 0);
if ( handle == 0 )
{
const ::DWORD error = ::GetLastError();
std::cerr
<< "InternetOpen(): " << error << "."
<< std::endl;
}
return (handle);
}
void netclose ( ::HINTERNET object )
{
const ::BOOL result = ::InternetCloseHandle(object);
if ( result == FALSE )
{
const ::DWORD error = ::GetLastError();
std::cerr
<< "InternetClose(): " << error << "."
<< std::endl;
}
}
::HINTERNET netopen ( ::HINTERNET session, ::LPCWSTR url )
{
const ::HINTERNET handle =
::InternetOpenUrlW(session, url, 0, 0, 0, 0);
if ( handle == 0 )
{
const ::DWORD error = ::GetLastError();
std::cerr
<< "InternetOpenUrl(): " << error << "."
<< std::endl;
}
return (handle);
}
void netfetch ( ::HINTERNET istream, std::ostream& ostream )
{
static const ::DWORD SIZE = 1024;
::DWORD error = ERROR_SUCCESS;
::BYTE data[SIZE];
::DWORD size = 0;
do {
::BOOL result = ::InternetReadFile(istream, data, SIZE, &size);
if ( result == FALSE )
{
error = ::GetLastError();
std::cerr
<< "InternetReadFile(): " << error << "."
<< std::endl;
}
ostream.write((const char*)data, size);
}
while ((error == ERROR_SUCCESS) && (size > 0));
}
}
int main ( int, char ** )
{
const ::WCHAR URL[] = L"http://stackoverflow.com/";
const ::HINTERNET session = ::netstart();
if ( session != 0 )
{
const ::HINTERNET istream = ::netopen(session, URL);
if ( istream != 0 )
{
std::ofstream ostream("output.txt", std::ios::binary);
if ( ostream.is_open() ) {
::netfetch(istream, ostream);
}
else {
std::cerr << "Could not open 'output.txt'." << std::endl;
}
::netclose(istream);
}
::netclose(session);
}
}
#pragma comment ( lib, "Wininet.lib" )