Wrong error code - c++

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().

Related

WriteFile with an overlapped occasionally gives me ERROR_INVALID_HANDLE. Am I doing memset correct?

I am using WriteFile to write to a file and I am getting an error of ERROR_INVALID_HANDLE sometimes. I read that this could be because the value of HANDLE in Overlapped is invalid.
But I am just having a hard time figuring this out. I wonder if something is going out of scope. Would appreciate it if someone can take a look. I can always add more code here
const LPOVERLAPPED lpOverlapped = GetOverlapped(true, hFile, ulBufSize, &ullFileOffset,volumeName);
if (lpOverlapped == nullptr)
{
CloseHandle(hFile);
return false;
}
if (!WriteFile(hFile,(const PVOID)(((UINT64) s_pMemoryBuffer[volumeName]) + iBufferOffset),ulBufSize,&dwBytesWritten,lpOverlapped))
{
DWORD errCode = GetLastError(); //Error here
//Why do I get an error code 6 here every now and then
}
Now this is the method that returns the overlapped structure
LPOVERLAPPED foo::GetOverlapped(bool useOverlappedIo, HANDLE hFile, UINT32 ulBufSize, UINT64* ullFileOffset,const std::string& volumeName)
{
if (useOverlappedIo)
{
while (true)
{
int index = 0;
while (index < cMaxOverlappedIOS)
{
if (!OverlappedInUse[volumeName][index])
{
OverlappedInUse[volumeName][index] = true;
LPOVERLAPPED overlapped = &(OverlappedArray[volumeName][index]);
if (overlapped->hEvent == nullptr) // Need Event
{
overlapped->hEvent = CreateEvent(
nullptr,
TRUE,
TRUE,
nullptr);
if (overlapped->hEvent == nullptr)
{
printf("Error creating event (error code: %u)\n", GetLastError());
return nullptr;
}
}
overlapped->Offset = (UINT32)(*ullFileOffset & 0xFFFFFFFF); // Low order 32 bits
overlapped->OffsetHigh = (UINT32)(*ullFileOffset >> 32); // High order 32 bits
*ullFileOffset += ulBufSize; // Update pointer to next record
return overlapped;
}
// Else Keep looking
index++;
}
// None available, wait for at least one to free up
if (WaitForPendingIOs(hFile, FALSE,volumeName) != ERROR_SUCCESS)
{
return nullptr;
}
} // Now start loop over again
}
else
{
return nullptr;
}
}
This is how I am initializing the array before this code gets called
for(auto vol : readVolumes)
{
OVERLAPPED* oarray = new OVERLAPPED[cMaxOverlappedIOS];
memset(oarray, 0, sizeof(oarray));
OverlappedArray[vol] = oarray;
bool* boolinuse = new bool[cMaxOverlappedIOS]{false};
OverlappedInUse[vol] = boolinuse;
s_pMemoryBuffer[vol] = nullptr;
s_uDataBufferSize[vol] = 0;
}
Any suggestions on why I would get that error ?

Wrap audio data of the pcm_alaw type into an MKA audio file using the ffmpeg API

Imagine that in my project, I receive RTP packets with the payload type-8, for later saving this load as the Nth part of the audio track. I extract this load from the RTP packet and save it to a temporary buffer:
...
while ((rtp = receiveRtpPackets()).withoutErrors()) {
payloadData.push(rtp.getPayloadData());
}
audioGenerator.setPayloadData(payloadData);
audioGenerator.recordToFile();
...
After filling a temporary buffer of a certain size with this payload, I process this buffer, namely, extract the entire payload and encode it using ffmpeg for further saving to an audio file in Matroska format. But I have a problem. Since the payload of the RTP packet is type 8, I have to save the raw audio data of the pcm_alaw format to mka audio format. But when saving raw data pcm_alaw to an audio file, I get these messages from the library:
...
[libopus # 0x18eff60] Queue input is backward in time
[libopus # 0x18eff60] Queue input is backward in time
[libopus # 0x18eff60] Queue input is backward in time
[libopus # 0x18eff60] Queue input is backward in time
...
When you open an audio file in vlc, nothing is played (the audio track timestamp is missing).
The task of my project is to simply take pcm_alaw data and pack it in a container, in mka format. The best way to determine the codec is to use the av_guess_codec() function, which in turn automatically selects the desired codec ID. But how do I pack the raw data into the container correctly, I do not know.
It is important to note that I can get as raw data any format of this data (audio formats only) defined by the RTP packet type (All types of RTP packet payload). All I know is that in any case, I have to pack the audio data in an mka container.
I also attach the code (borrowed from this resource) that I use:
audiogenerater.h
extern "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
}
class AudioGenerater
{
public:
AudioGenerater();
~AudioGenerater() = default;
void generateAudioFileWithOptions(
QString fileName,
QByteArray pcmData,
int channel,
int bitRate,
int sampleRate,
AVSampleFormat format);
private:
// init Format
bool initFormat(QString audioFileName);
private:
AVCodec *m_AudioCodec = nullptr;
AVCodecContext *m_AudioCodecContext = nullptr;
AVFormatContext *m_FormatContext = nullptr;
AVOutputFormat *m_OutputFormat = nullptr;
};
audiogenerater.cpp
AudioGenerater::AudioGenerater()
{
av_register_all();
avcodec_register_all();
}
AudioGenerater::~AudioGenerater()
{
// ...
}
bool AudioGenerater::initFormat(QString audioFileName)
{
// Create an output Format context
int result = avformat_alloc_output_context2(&m_FormatContext, nullptr, nullptr, audioFileName.toLocal8Bit().data());
if (result < 0) {
return false;
}
m_OutputFormat = m_FormatContext->oformat;
// Create an audio stream
AVStream* audioStream = avformat_new_stream(m_FormatContext, m_AudioCodec);
if (audioStream == nullptr) {
avformat_free_context(m_FormatContext);
return false;
}
// Set the parameters in the stream
audioStream->id = m_FormatContext->nb_streams - 1;
audioStream->time_base = { 1, 8000 };
result = avcodec_parameters_from_context(audioStream->codecpar, m_AudioCodecContext);
if (result < 0) {
avformat_free_context(m_FormatContext);
return false;
}
// Print FormatContext information
av_dump_format(m_FormatContext, 0, audioFileName.toLocal8Bit().data(), 1);
// Open file IO
if (!(m_OutputFormat->flags & AVFMT_NOFILE)) {
result = avio_open(&m_FormatContext->pb, audioFileName.toLocal8Bit().data(), AVIO_FLAG_WRITE);
if (result < 0) {
avformat_free_context(m_FormatContext);
return false;
}
}
return true;
}
void AudioGenerater::generateAudioFileWithOptions(
QString _fileName,
QByteArray _pcmData,
int _channel,
int _bitRate,
int _sampleRate,
AVSampleFormat _format)
{
AVFormatContext* oc;
if (avformat_alloc_output_context2(
&oc, nullptr, nullptr, _fileName.toStdString().c_str())
< 0) {
qDebug() << "Error in line: " << __LINE__;
return;
}
if (!oc) {
printf("Could not deduce output format from file extension: using mka.\n");
avformat_alloc_output_context2(
&oc, nullptr, "mka", _fileName.toStdString().c_str());
}
if (!oc) {
qDebug() << "Error in line: " << __LINE__;
return;
}
AVOutputFormat* fmt = oc->oformat;
if (fmt->audio_codec == AV_CODEC_ID_NONE) {
qDebug() << "Error in line: " << __LINE__;
return;
}
AVCodecID codecID = av_guess_codec(
fmt, nullptr, _fileName.toStdString().c_str(), nullptr, AVMEDIA_TYPE_AUDIO);
// Find Codec
m_AudioCodec = avcodec_find_encoder(codecID);
if (m_AudioCodec == nullptr) {
qDebug() << "Error in line: " << __LINE__;
return;
}
// Create an encoder context
m_AudioCodecContext = avcodec_alloc_context3(m_AudioCodec);
if (m_AudioCodecContext == nullptr) {
qDebug() << "Error in line: " << __LINE__;
return;
}
// Setting parameters
m_AudioCodecContext->bit_rate = _bitRate;
m_AudioCodecContext->sample_rate = _sampleRate;
m_AudioCodecContext->sample_fmt = _format;
m_AudioCodecContext->channels = _channel;
m_AudioCodecContext->channel_layout = av_get_default_channel_layout(_channel);
m_AudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
// Turn on the encoder
int result = avcodec_open2(m_AudioCodecContext, m_AudioCodec, nullptr);
if (result < 0) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
// Create a package
if (!initFormat(_fileName)) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
// write to the file header
result = avformat_write_header(m_FormatContext, nullptr);
if (result < 0) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
// Create Frame
AVFrame* frame = av_frame_alloc();
if (frame == nullptr) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
int nb_samples = 0;
if (m_AudioCodecContext->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) {
nb_samples = 10000;
}
else {
nb_samples = m_AudioCodecContext->frame_size;
}
// Set the parameters of the Frame
frame->nb_samples = nb_samples;
frame->format = m_AudioCodecContext->sample_fmt;
frame->channel_layout = m_AudioCodecContext->channel_layout;
// Apply for data memory
result = av_frame_get_buffer(frame, 0);
if (result < 0) {
av_frame_free(&frame);
{
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
}
// Set the Frame to be writable
result = av_frame_make_writable(frame);
if (result < 0) {
av_frame_free(&frame);
{
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
}
int perFrameDataSize = frame->linesize[0];
int count = _pcmData.size() / perFrameDataSize;
bool needAddOne = false;
if (_pcmData.size() % perFrameDataSize != 0) {
count++;
needAddOne = true;
}
int frameCount = 0;
for (int i = 0; i < count; ++i) {
// Create a Packet
AVPacket* pkt = av_packet_alloc();
if (pkt == nullptr) {
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
return;
}
av_init_packet(pkt);
if (i == count - 1)
perFrameDataSize = _pcmData.size() % perFrameDataSize;
// Synthesize WAV files
memset(frame->data[0], 0, perFrameDataSize);
memcpy(frame->data[0], &(_pcmData.data()[perFrameDataSize * i]), perFrameDataSize);
frame->pts = frameCount++;
// send Frame
result = avcodec_send_frame(m_AudioCodecContext, frame);
if (result < 0)
continue;
// Receive the encoded Packet
result = avcodec_receive_packet(m_AudioCodecContext, pkt);
if (result < 0) {
av_packet_free(&pkt);
continue;
}
// write to file
av_packet_rescale_ts(pkt, m_AudioCodecContext->time_base, m_FormatContext->streams[0]->time_base);
pkt->stream_index = 0;
result = av_interleaved_write_frame(m_FormatContext, pkt);
if (result < 0)
continue;
av_packet_free(&pkt);
}
// write to the end of the file
av_write_trailer(m_FormatContext);
// Close file IO
avio_closep(&m_FormatContext->pb);
// Release Frame memory
av_frame_free(&frame);
avcodec_free_context(&m_AudioCodecContext);
if (m_FormatContext != nullptr)
avformat_free_context(m_FormatContext);
}
main.cpp
int main(int argc, char **argv)
{
av_log_set_level(AV_LOG_TRACE);
QFile file("rawDataOfPcmAlawType.bin");
if (!file.open(QIODevice::ReadOnly)) {
return EXIT_FAILURE;
}
QByteArray rawData(file.readAll());
AudioGenerater generator;
generator.generateAudioFileWithOptions(
"test.mka",
rawData,
1,
64000,
8000,
AV_SAMPLE_FMT_S16);
return 0;
}
It is IMPORTANT you help me find the most appropriate way to record pcm_alaw or a different data format in an MKA audio file.
I ask everyone who knows anything to help (there is too little time left to implement this project)
These useful links will help you:
A good overview of the data processing sequence in libav: ffmpeg-libav-tutorial
Examples from the ffmpeg developers themselves: avio_reading, resampling_audio, transcode_aac

libusb_get_device_list seg fault

I am writing a file explorer application in Qt C++ and have a libUSB function (QList UsbDevice::getDeviceList()) which gets all attached USB devices, checks each one for my products vendor and product ID's, claims them and the returns them in an array. This all works fine and I get the device I want, however I have added a refresh button which should update the device list shown in a drop-down list (it basically calls the getDeviceList function again) but it seg faults when calling:
int numDevices = libusb_get_device_list(NULL, &usbDevices);
the second time around and I can't for the life of me see why. If someone could check over the code below and see if I have missed something stupid that would be very helpful.
QList<UsbDevice*> UsbDevice::getDeviceList()
{
unsigned char manf[256] = {'\0'};
QList<UsbDevice*> usbDeviceList;
libusb_device **usbDevices;
struct libusb_device_descriptor desc;
int numDevices = libusb_get_device_list(NULL, &usbDevices);
if(numDevices < 0)
{
libusb_free_device_list(usbDevices, 1);
return usbDeviceList;
}
QString error;
for(int i=0; i!=numDevices; ++i)
{
libusb_device *dev = usbDevices[i];
libusb_get_device_descriptor(dev, &desc);
if((desc.idVendor != VendorUsbId) && (desc.idProduct != ProductUsbId))
continue;
libusb_device_handle *handle = NULL;
libusb_config_descriptor *conf_desc = NULL;
int result = 0;
result = libusb_open(dev, &handle);
if(result < 0)
{
if(result == -3)
{
}
error = QString(libusb_error_name(result));
continue;
}
int config = 1;
if( handle == NULL)
{
continue;
}
result = libusb_set_configuration(handle, config);
if(result < 0)
{
error = QString(libusb_error_name(result));
continue;
}
result = libusb_get_config_descriptor(dev, 0, &conf_desc);
if(result < 0)
{
error = QString(libusb_error_name(result));
continue;
}
result = libusb_claim_interface(handle, 0);
if(result < 0)
{
error = QString(libusb_error_name(result));
continue;
}
result = libusb_get_string_descriptor_ascii(handle, desc.iProduct, manf, sizeof(manf));
if(result < 0)
{
error = QString(libusb_error_name(result));
continue;
}
UsbDevice *newDevice = new UsbDevice();
newDevice->setDeviceName(QString((char*)manf));
newDevice->setHandle(handle);
usbDeviceList << newDevice;
}
libusb_free_device_list(usbDevices, 1);
return usbDeviceList;
}
You are calling libusb_init() at the beginning of your program, but you are also calling libusb_exit() at the beginning : before calling a.exec().
Your first call probably happens in MainWindow constructor ?
You could instead subclass QApplication, call libusb_init() in the constructor and libusb_exit() in the destructor.

PortAudio: Pa_StartStream() crashes Game Maker when used in DLL

I want to use PortAudio in Game Maker by using a DLL.
I made the callback function in C++ and used that to calculate the frequency of the incoming sample stream. In the DLL I made a few export functions: one that initiates the stream, one that closes the stream and one that gets the frequency variable generated by the callback.
Now when I called the PortAudioStart() function in Game Maker, the game shut down without any warning/error/message of any kind. I put MessageBox()'es in between the lines of code, to check what was the source of the crash. It showed the first, second and third ones, and then it crashed, not even showing the error MessageBox. So it turned out that Pa_StartStream() caused it.
Now I wonder: why does this crash occur, and how can I fix it?
Note: I tested this DLL by letting a C++ program call it, and in that case it worked totally as planned.
I am using GM8.0 and NetBeans 7.3 with the gcc compiler from Cygwin 4.
The start and end functions (leaving out all includes and definitions of global variables):
#define GMEXPORT extern "C" __declspec (dllexport)
GMEXPORT double __cdecl PortAudioStart() {
err = Pa_Initialize();
if( err != paNoError ) goto error;
MessageBoxA(NULL, "1", "PortAudio DLL", MB_ICONINFORMATION);
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
if (inputParameters.device == paNoDevice) {
goto error;
}
inputParameters.channelCount = 1; /* mono input */
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
MessageBoxA(NULL, "2", "PortAudio DLL", MB_ICONINFORMATION);
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL, //&outputParameters
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0,
PaCallback,
NULL );
if( err != paNoError ) goto error;
MessageBoxA(NULL, "3", "PortAudio DLL", MB_ICONINFORMATION);
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
MessageBoxA(NULL, "4", "PortAudio DLL", MB_ICONINFORMATION);
return 1;
error:
MessageBoxA(NULL, "Apparently it doesn't work!", "PortAudio DLL", MB_ICONINFORMATION);
Pa_Terminate();
return 0;
}
GMEXPORT double __cdecl PortAudioEnd() {
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
return 1;
error:
MessageBoxA(NULL, "Apparently it doesn't work!", "PortAudio DLL", MB_ICONINFORMATION);
Pa_Terminate();
return 0;
}
GMEXPORT double __cdecl getFreq()
{
if (Pa_IsStreamStopped(stream) == 0)
{
return frequency; // this variable is constantly changed in the callback function
}
else
{
return 0;
}
}
void calculateFreq(bool sign)
{
unsigned int j;
bool check;
diffsamp = maxsamp - minsamp;
if (!sign)
{
diffsampmax = max((diffsampmax*.85), (double)diffsamp);
maxsamp = 0;
}
if (sign)
{
diffsampmax = max((diffsampmax*.85), (double)diffsamp);
minsamp = 0;
}
check = ( diffsamp > max(25.0,diffsampmax*.90) );
if (sign == lastsign)
{
check = false;
}
if (check)
{
if (timepassed - peaks[0] < 500)
{
for ( j=numpeaks-1; j>0; j-- )
{
peaks[j] = peaks[j-1];
}
}
else
{
for ( j=0; j<numpeaks; j++ )
{
peaks[j] = 0;
}
}
peaks[0] = timepassed;
double diff = peaks[0]-peaks[numpeaks-1];
double peaktime = diff/(numpeaks-1)*2; //*2 because maxdiff is at +>- and ->+
frequency = 1/((double)peaktime/(double)SAMPLE_RATE);
if (peaks[numpeaks-1] <= 0 || frequency < 20) frequency = 0;
lastsign = sign;
}
}
static int PaCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
const SAMPLE *in = (const SAMPLE*)inputBuffer;
unsigned int i;
unsigned int j;
(void) timeInfo; // Prevent unused variable warnings.
(void) statusFlags;
(void) userData;
SAMPLE samp;
if( inputBuffer == NULL )
{
//nothing happens
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
timepassed += 1;
samp = *in++;
changed = false;
if (samp > 0)
{
if (!sign)
{
sign = true;
changed = true;
}
maxsamp = max(maxsamp,samp);
}
else
{
if (sign)
{
sign = false;
changed = true;
}
minsamp = min(minsamp,samp);
}
if (changed)
{
calculateFreq(sign);
}
}
}
return paContinue;
}
GML:
//// Script: pa_init()
globalvar _pa_freq,_pa_start,_pa_end;
var dll_name;
dll_name = "c:\Users\<Username>\Documents\NetBeansProjects\GMDLLtest\dist\Debug\Cygwin_4.x-Windows\libGMDLLtest.dll";
_pa_freq = external_define(dll_name, "getFreq", dll_cdecl, ty_real, 0);
_pa_start = external_define(dll_name, "PortAudioStart", dll_cdecl, ty_real, 0);
_pa_end = external_define(dll_name, "PortAudioEnd", dll_cdecl, ty_real, 0);
////--------------------------------------------------------------------------------
//// Script: pa_start()
return external_call(_pa_start);
////--------------------------------------------------------------------------------
//// Script: pa_end()
return external_call(_pa_end);
////--------------------------------------------------------------------------------
//// Script: pa_freq()
return external_call(_pa_freq);
P.S: If anything is unclear, please ask, my knowledge of C++ isn't really outstanding.

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" )