Memory leak when read from file - c++

I'm trying to read data from XML file and store every element ("< some data/>") in vector container vector<TCHAR*> , why the Task Manager shows the memory usage much greater than vector size(~80mb instead of ~59mb) :
#define _UNICODE
#include<tchar.h>
#include<iostream>
#include<windows.h>
#include<vector>
using namespace std;
HANDLE hFile;
HANDLE hThread;
vector<TCHAR*> tokens;
DWORD tokensSize;
DWORD WINAPI Thread(LPVOID lpVoid);
void main()
{
tokensSize = 0;
hFile = CreateFile("db.xml",GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hFile == INVALID_HANDLE_VALUE) {
cout<<"CreateFile Error # "<<GetLastError()<<endl;
}
DWORD fileSize = GetFileSize(hFile,NULL);
cout<<"fileSize = "<<fileSize<<" bytes = "<<fileSize/1024/1024<<" mb"<<endl;
TCHAR* buffer = new TCHAR[fileSize / sizeof(TCHAR) + 1];
ZeroMemory(buffer,fileSize);
DWORD bytesRead;
if(!ReadFile(hFile,buffer,fileSize,&bytesRead,NULL)){
cout<<"ReadFile Error # "<<GetLastError()<<endl;
}
CloseHandle(hFile);
hThread = CreateThread(NULL,0,Thread,(LPVOID)buffer,0,NULL);
WaitForSingleObject(hThread,INFINITE);
for(int i=0;i<tokens.size();i++)
tokensSize+=(_tcslen(tokens[i])+1)*sizeof(TCHAR);
cout<<"vector size = "<<tokensSize<<" bytes = "<<tokensSize/1024/1024<<" mb"<<endl;
cin.get();
}
DWORD WINAPI Thread(LPVOID lpVoid)
{
wstring entireDB = (TCHAR*)lpVoid;
delete[]lpVoid;
wstring currentElement;
wstring::size_type lastPos = 0;
wstring::size_type next;
next = entireDB.find(_T(">"),lastPos);
TCHAR* szStr;
do
{
currentElement = entireDB.substr(lastPos,next+1-lastPos);
szStr = new TCHAR[currentElement.length()+1];
_tcscpy(szStr,currentElement.c_str());
tokens.push_back(szStr);
lastPos = next+1;
next = entireDB.find(_T(">"),lastPos);
}
while(next != wstring::npos);
entireDB.clear();
return 0;
}
OUTPUT:~
fileSize = 57mb
vectorSize = 58mb
but the TaskManager shows ~ 81mb.
What am I doing wrong?
THNX!

First, as Aesthete as pointed out, you never clear the token vector once you're finished with it. This should be done, or change the token vector to utilize self-cleaning content like std::string or std::wstring.
Which brings me to the side-by-side below. Please review this against your existing code. There are a number of changes you'll want to compare. The one you will likely not see until you cmopile+run is the memory footprint difference, which may surprise you.
Major Changes
Global tokens is now a vector of std::wstring rather than raw wchar_t pointers
Uses MultiByteToWideChar to translate the input file.
Allocates a std::wstring dynamically as the thread parameter. This removes one full copy of the file image. The thread is responsible for deleteing the wstring once finished parsing the content.
Uses _beginthreadex() for starting the thread. The fundamental reason for this is because of the C/C++ runtime usage. In the past the runtime sets up various thread-local-storage that must be properly cleaned, and are so when using _beginthreadex(). It is almost identical to CreateThread(), but honestly I look forward to the day when MS has their stuff together and gives us std::thread officially like the rest of the civilized world.
Minor/Meaningless Changes
Global variables are brought to local scope where appropriate. this means the only real global now is the tokens vector.
The thread procedure now pushes substrings straight to the tokens vector.
uses argv[1] for the filename (easy to debug that way, no other special reason). can be changed back to your hard-coded filename as needed.
I hope this gives you some ideas on cleaning this up, and more importantly, how yoy can do almost the entire task you're given without having to go new and delete nuts.
Notes: this does NOT check the input file for a byte-order-mark. I'm taking it on faith that your claim it is UTF8 is straight-up and doesn't have a BOM at the file beginning. If your input file does have a BOM, you need to adjust the code that reads the file in to account for this.
#include <windows.h>
#include <tchar.h>
#include <process.h>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// global map of tokens
vector<wstring> tokens;
// format required by _beginthreadex()
unsigned int _stdcall ThreadProc(void *p);
int main(int argc, char *argv[])
{
HANDLE hThread = NULL;
std::string xml;
std::wstring* pwstr = NULL;
// check early exit
if (argc != 2)
{
cout << "Usage: " << argv[0] << " filename" << endl;
return EXIT_FAILURE;
}
// use runtime library for reading the file content. the WIN32 CreateFile
// API is required for some things, but not for general file ops.
HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize > 0)
{
// allocate a string large enough for the whole file.
std::string xml(dwFileSize, 0);
DWORD bytesRead = 0;
if (ReadFile(hFile, &xml.at(0), dwFileSize, &bytesRead, NULL) && (bytesRead == dwFileSize))
{
// invoke MB2WC to determine wide-char requirements
int ires = MultiByteToWideChar(CP_UTF8, 0, xml.c_str(), -1, NULL, 0);
if (ires > 0)
{
// allocate a wstring for our thread parameter.
pwstr = new wstring(ires, 0);
MultiByteToWideChar(CP_UTF8, 0, xml.c_str(), -1, &pwstr->at(0), ires);
// launch thread. it own the wstring we're sending, including cleanup.
hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, pwstr, 0, NULL);
}
}
}
// release the file handle
CloseHandle(hFile);
}
// wait for potential thread
if (hThread != NULL)
{
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
// report space taken by tokens
size_t tokensSize = 0;
for (vector<wstring>::const_iterator it = tokens.begin(); it != tokens.end(); ++it)
tokensSize += it->size()+1;
cout << "tokens count = " << tokens.size() << endl
<< "tokens size = "<< tokensSize <<" bytes" << endl;
cin.get();
}
// our thread parameter is a dynamic-allocated wstring.
unsigned int _stdcall ThreadProc(void *p)
{
// early exit on null insertion
if (p == NULL)
return EXIT_FAILURE;
// use string passed to us.
wstring* pEntireDB = static_cast<wstring*>(p);
wstring::size_type last = 0;
wstring::size_type next = pEntireDB->find(L'>',last);
while(next != wstring::npos)
{
tokens.push_back(pEntireDB->substr(last, next-last+1));
last = next+1;
next = pEntireDB->find(L'>', last);
}
// delete the wstring (no longer needed)
delete pEntireDB;
return EXIT_SUCCESS;
}

You allocate memory here, in the do-while loop:
szStr = new TCHAR[currentElement.length()+1];
And you never release it with the delete operator

Related

C++ Serial COM Port Access

in uPyCraft IDE or Putty, just sending km.press('a') then it works fine,
but in my C++, i tried to writefile with km.press('a'), it doesn't work.
i can't find what is wrong
uPyCraft Successfull
`bool CSerialPort::OpenPort(CString portname)
{
m_hComm = CreateFile(L"//./" + portname,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if (m_hComm == INVALID_HANDLE_VALUE)
{
std::cout << "INVALID HANDLE" << std::endl;
return false;
}
else
return true;
}
bool CSerialPort::WriteByte(const char * bybyte)
{
byte iBytesWritten = 0;
if (WriteFile(m_hComm, &bybyte, 1, &m_iBytesWritten, NULL) == 0)
return false;
else
return true;
}
int main()
{
CSerialPort _serial;
_serial.OpenPort(L"COM4");
_serial.WriteByte("km.press('a')");
}`
i tried this,
but it doesn't work, i also check _serial Isn't INVALID HANDLE.
someone help me for sending "km.press('a')" to serial
and sending km.move(0,1) with using Putty and uPyCraft,
it works fine but
string test = "km.move(0,1)";
DWORD dwBytesWritten;
WriteFile(m_hComm,&test,sizeof(test),dwBytesWritten,NULL);
it doesn't work. just changing km.move(0,1) to km.move(0,10), then i don't know why but it works fine.
what is different with uPyCraft(Putty) and C++?
By the looks of it, I'm assuming your class definition looks something like this:
class CSerialPort {
public:
bool OpenPort(CString portname);
bool WriteByte(const char* bybyte);
private:
HANDLE m_hComm;
byte m_iBytesWritten;
};
byte is not the proper type. DWORD is.
CString may be used, but you are using wide string literals anyway so you could just use CreateFileW, std::wstrings and std::wstring_views.
WriteByte implies that you only want to write one byte - and indeed, your implementation does only write one byte - but it's the wrong byte. It writes one byte out of the memory of the bybyte variable, not the memory it points at.
A minor redefinition of the class:
#include <string_view> // added header
class CSerialPort {
public:
// take a `std::wstring` instead
bool OpenPort(const std::wstring& portname);
// WriteBytes instead of WriteByte:
bool WriteBytes(const void* bytesPtr, DWORD bytesToWrite);
// write both wide and non-wide string_views
bool WriteString(std::string_view str);
bool WriteString(std::wstring_view str);
private:
HANDLE m_hComm;
DWORD m_iBytesWritten; // the proper type
};
The implementation in the .cpp file then becomes:
bool CSerialPort::OpenPort(const std::wstring& portname) {
// Use CreateFileW since you've hardcoded wide string literals anyway:
m_hComm = CreateFileW((L"//./" + portname).c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
return m_hComm != INVALID_HANDLE_VALUE;
}
bool CSerialPort::WriteBytes(const void* bytesPtr, DWORD bytesToWrite)
{
return
WriteFile(m_hComm, bytesPtr, bytesToWrite, &m_iBytesWritten, nullptr) != 0;
}
// the WriteString overloads taking string_views pass on the pointer
// and length to `WriteBytes`:
bool CSerialPort::WriteString(std::string_view str) {
return WriteBytes(str.data(), str.size());
}
bool CSerialPort::WriteString(std::wstring_view str) {
return WriteBytes(str.data(), str.size() * // wchar_t's are more than 1 byte:
sizeof(std::wstring_view::value_type));
}
And your main would then use the WriteString overload taking a std::string_view (by passing a const char* to WriteString):
int main()
{
CSerialPort _serial;
if(_serial.OpenPort(L"COM4")) {
_serial.WriteString("km.press('a')");
} else {
std::cerr << "failed opening COM4\n";
}
}
Note: The section you added at the end has several errors:
string test = "km.move(0,1)";
DWORD dwBytesWritten;
WriteFile(m_hComm,&test,sizeof(test),dwBytesWritten,NULL);
&test takes the address of the std::string object. You should use test.c_str() to get a const char* to the first character in the string.
sizeof(test) gets the size of the std::string object, not the length of the actual string. You should use test.size() instead.
dwBytesWritten is passed by value but the function expects a pointer to a DWORD that it can write to. You should use &dwBytesWritten instead.
WriteFile(m_hComm, test.c_str(), test.size(), &dwBytesWritten, NULL);

Does the close method of ofstream also close the underlying handle

On Windows platform, a file handle is got from calling CreateFile, and then the handle is used to initialize an ofstream object. A minimal example is as below:
#include"stdafx.h"
#include <tchar.h>
#include <iostream>
#include <fstream>
#include <windows.h>
#include <io.h>
#include <fcntl.h>
class CSV_writer {
public:
std::ofstream my_ofstream;
private:
HANDLE my_handle = INVALID_HANDLE_VALUE;
int file_descriptor = -1;
FILE * my_file = nullptr; //FILE type is actually a IO buff.
const static unsigned int fl = 256;
public:
explicit CSV_writer(const TCHAR * file_name_) {
//get current directory
TCHAR current_path[MAX_PATH];
GetCurrentDirectory(MAX_PATH, current_path);
TCHAR filename[fl]{ 0 };
_tcscat_s(filename, file_name_);
_tcscat_s(filename, _T(".csv"));
if (current_path[_tcslen(current_path) - 1] != _T('\\') && _tcslen(current_path) < MAX_PATH - 1) {
_tcscat_s(current_path, _T("\\"));
}
else {
throw std::exception("path length exceeding limit.");
}
if (_tcslen(current_path) + _tcslen(filename) + 1 < MAX_PATH) {
_tcscat_s(current_path, filename);
}
else {
//current path exceeds the max path length defined in MAX_PATH
throw std::exception("path length exceeding limit.");
}
this->my_handle = CreateFile(
current_path,
GENERIC_READ | GENERIC_WRITE, //access permit, both read and write
0, //cannot be shared and cannot be opened again until the handle to the file or device is closed
nullptr, //returned handle can not be inherited by child process
CREATE_ALWAYS, //always create a new file, overwrite old one if it exists
FILE_ATTRIBUTE_NORMAL,
nullptr
);
if (my_handle != INVALID_HANDLE_VALUE) {
int file_descriptor = _open_osfhandle((intptr_t)my_handle, _O_TEXT);
if (file_descriptor != -1) {
this->my_file = _fdopen(file_descriptor, "w");
if (this->my_file != nullptr) {
this->my_ofstream = std::ofstream(this->my_file);
}
}
}
}
~CSV_writer() {
// Closes stream, file, file_descriptor, and file_handle.
this->my_ofstream.flush();
this->my_ofstream.close();
this->my_file = nullptr;
this->file_descriptor = -1;
this->my_handle = INVALID_HANDLE_VALUE;
}
};
int main(int argc, char* argv[])
{
CSV_writer csv_writer(L"memory_layout");
csv_writer.my_ofstream << "Type,\t" << "Size,\t" << "Offset,\t" << "Address\n";
return 0;
}
My question is, after calling "my_ofstream.close()" afterwards, will the underlying file handle also released? Or I have to call Windows API CloseHandle() manually after calling close()?
Update: to those who say that there is no constructor of ofstream taking FILE*, actually there is, kind of,
I hope you're already aware that the constructor you are using:
std::ofstream(FILE * fp)
is a non-standard, undocumented Microsoft extension, unguaranteed even by Microsoft.
In that case, Microsoft does not promise you even that:
int fd = ...;
...
FILE * fp = _fdopen(fd, "w");
...
std::osftream ofs(fp);
...
ofs.close();
will do fclose(fp) - never mind _close(fd).
If however you take it as given that ofs.close() does fclose(fp) - and evidently you do - then Microsoft
does promise you that it will also _close(fd). From the documentation
Remarks
...
File descriptors passed into _fdopen are owned by the returned FILE * stream.
If _fdopen is successful, do not call _close on the file descriptor.
Calling fclose on the returned FILE * also closes the file descriptor.
(My emphasis.)

Updating the PATH environment variable in Windows using C++

I am trying to launch a new process from my current process. I am using CreateProcess() to launch it. The issue is that I need to have certain directories in my PATH to successfully do so. Here is my current implementation but it doesn't work. What am I doing wrong?
// Environment variables
char *env = new char[2048];
char *ptr = env;
char temp[MAX_PATH] = "PATH=";
strcpy(ptr, strcat(temp, plugin_path));
ptr += strlen(ptr) + 1;
char temp2[MAX_PATH] = "PATH=";
strcpy(ptr, strcat(temp, lib_path));
ptr += strlen(ptr) + 1;
*ptr = '\0';
// Execute
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// error checking required
if(!CreateProcess(
NULL, // application name
command_path, // app.exe
NULL,
NULL,
TRUE,
0,
env, // environment
NULL,
&si,
&pi)) {
std::cout << GetLastError();
return 1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
std::cout << "Process Started!";
Please let me know if anything else is required.
EDIT: Somebody mentioned below that I need to be a little more specific. It doesn't work in the sense that the environment variables don't get passed. It fails because the library path is not in PATH. The createProcess does actually launch it though.
EDIT2: Here's the updated code. Same problem. Further, CreateProcess throws error 1087 which doesn't seem to exist in the docs.
// Environment variables
char env[2048];
char *ptr = env;
char *path_path = getenv("PATH");
// copy original path
memcpy(ptr, path_path, strlen(path_path));
ptr += strlen(ptr) + 1;
memcpy(ptr, ";", 1);
ptr++;
// copy plugin path
memcpy(ptr, plugin_path, strlen(plugin_path));
ptr += strlen(plugin_path) + 1;
memcpy(ptr, ";", 1);
ptr++;
// copy libpath
memcpy(ptr, lib_path, strlen(lib_path));
ptr += strlen(lib_path) + 1;
memcpy(ptr, ";", 1);
ptr++;
// double null terminated
memcpy(ptr, "\0\0", 2);
std::cout << "ENV : " << env << std::endl;
// error checking required
if(!CreateProcess(
NULL, // application name
command_path, // app.exe
NULL,
NULL,
TRUE,
0,
env, // environment
NULL,
&si,
&pi)) {
std::cout << GetLastError();
return 1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
std::cout << "Process Started!";
The PATH variable is a single variable. Different directories are listed in that variable, separated by semi-colons. But you've attempted to define the variable twice. That is the mistake.
The code should be something like this (assuming that you want to extend the existing path):
char *env = new char[2048]; // fingers crossed this is enough
strcpy(env, "PATH=");
strcat(env, getenv("PATH"));
strcat(env, ";");
strcat(env, plugin_path);
strcat(env, ";");
strcat(env, lib_path);
env[strlen(env)+1] = '\0';
Although this code (as is yours in the question) is simply begging for a buffer overrun.
It would be so much easier if you used C++ facilities to build your strings. For instance:
std::stringstream ss;
ss << "PATH=" << getenv("PATH");
ss << ";" << plugin_path;
ss << ";" << lib_path;
ss << '\0';
std::string env = ss.str();
Then pass env.c_str() to CreateProcess.
Not only does this make the code easier to read and verify, you know that you won't overrun any buffers.
I also note that you are passing an environment that has only one variable defined in it, namely PATH. It might be better if you started from the environment of the calling process, added the extra directories to PATH, and then passed that as the environment for the new process.
#include <iostream>
#include <windows.h>
#include <cstring>
#include "tchar.h"
void SetUserVariablePath(){
HKEY hkey;
long regOpenResult;
const char key_name[] = "Environment";
const char path[]="D:/custom_command"; //new_value path need to update
regOpenResult = RegOpenKeyEx(HKEY_CURRENT_USER,key_name, 0, KEY_ALL_ACCESS, &hkey);
LPCSTR stuff = "VVS_LOGGING_PATH"; //Variable Name
RegSetValueEx(hkey,stuff,0,REG_SZ,(BYTE*) path, strlen(path));
RegCloseKey(hkey);
}
void GetUserVariablePath(){
static const char path[] = "VVS_LOGGING_PATH" ; //Variable Name
static BYTE buffer1[1000000] ;
DWORD buffsz1 = sizeof(buffer1) ;
{
//HKEY_CURRENT_USER\Environment
const char key_name[] = "Environment";
HKEY key ;
if( RegOpenKeyExA( HKEY_CURRENT_USER, key_name, 0, KEY_QUERY_VALUE, std::addressof(key) ) == 0 &&
RegQueryValueExA( key, path, nullptr, nullptr, buffer1, std::addressof(buffsz1) ) == 0 )
{
std::cout << "The updated value of the user variable is : " << reinterpret_cast<const char*>(buffer1) << '\n' ;
}
}
}
int main()
{
SetUserVariablePath();
GetUserVariablePath();
return 0;
}

InternetReadFile not getting entire file

I have the following code to download some rss files from servers, but so far I'm just getting incomplete version of my rss file.(?) The code is as follows -
#include<iostream>
#include<conio.h>
#include<stdio.h>
#include<string>
#include<cstring>
#include<wininet.h>
using namespace std;
const int _SIZE = 307200;
int WEB_GET_DATA(char* WEB_URL){
HINTERNET WEB_CONNECT = InternetOpen("Default_User_Agent",INTERNET_OPEN_TYPE_PRECONFIG,NULL, NULL, 0);
if(!WEB_CONNECT){
cout<<"Connection Failed or Syntax error";
return 0;
}
HINTERNET WEB_ADDRESS = InternetOpenUrl(WEB_CONNECT,WEB_URL, NULL, 0, INTERNET_FLAG_KEEP_CONNECTION, 0);
if(!WEB_ADDRESS){
cout<<"ERROR...\n";
return 0;
}
char _DATA_RECIEVED[_SIZE];
DWORD NO_BYTES_READ = 0;
while(InternetReadFile(WEB_ADDRESS,_DATA_RECIEVED,_SIZE,&NO_BYTES_READ)&&(NO_BYTES_READ)){
cout<<_DATA_RECIEVED;
}
InternetCloseHandle(WEB_ADDRESS);
InternetCloseHandle(WEB_CONNECT);
return 0;
}
int main(){
WEB_GET_DATA("http://themoneyconverter.com/rss-feed/AED/rss.xml");
getch();
return 0;
}
I'm getting only almost half of my file and not from start but my output is seeming to be starting from somewhere in between the file and then to it's end.
So where I'm going wrong? I checked that my rss file is at least gonna be 30kb large. So I have given the _SIZE const 307200 (300kb) and still it is not working? Please help me.
Try this instead:
int WEB_GET_DATA(char* WEB_URL)
{
HINTERNET WEB_CONNECT = InternetOpen("Default_User_Agent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (!WEB_CONNECT)
{
cout << "Connection Failed or Syntax error" << endl;
return 0;
}
HINTERNET WEB_ADDRESS = InternetOpenUrl(WEB_CONNECT, WEB_URL, NULL, 0, INTERNET_FLAG_KEEP_CONNECTION, 0);
if (!WEB_ADDRESS)
{
cout << "ERROR..." << endl;
InternetCloseHandle(WEB_CONNECT);
return 0;
}
DWORD DATA_SIZE = _SIZE;
char *_DATA_RECIEVED = new char[DATA_SIZE];
DWORD NO_BYTES_READ = 0;
do
{
if (InternetReadFile(WEB_ADDRESS, _DATA_RECIEVED, DATA_SIZE, &NO_BYTES_READ))
{
if (NO_BYTES_READ == 0)
break;
cout << string(_DATA_RECIEVED, NO_BYTES_READ);
}
else
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
cout << "Read error" << endl;
break;
}
delete[] _DATA_RECIEVED;
DATA_SIZE += _SIZE;
_DATA_RECIEVED = new char[DATA_SIZE];
}
}
while (true);
InternetCloseHandle(WEB_ADDRESS);
InternetCloseHandle(WEB_CONNECT);
return 0;
}
char buffer[200000];
DWORD bytes_read = 0;
DWORD currbytes_read;
do
{
bRead = InternetReadFile(file_handle, buffer + bytes_read, 200000 - bytes_read, &currbytes_read);
bytes_read += currbytes_read;
} while (bRead && currbytes_read);
buffer[bytes_read] = 0;
First of all, the problem you are having is that you are overwriting the same buffer and you are not clearing the data before each call of InternetReadFile. You also have not cleared the buffer before your first call. You are then throwing a potentially garbled mess of string and memory into a cout. This is very bad.
A quick fix would be to do this:
BYTE _DATA_RECIEVED[_SIZE]; // BYTE is a char, but its clearer now its not guaranteed to be a string!
BOOL ret = TRUE;
DWORD NO_BYTES_READ = 0;
while(ret){
memset(_DATA_RECIEVED, 0, _SIZE); // clear the buffer
ret = InternetReadFile(WEB_ADDRESS,_DATA_RECIEVED,_SIZE,&NO_BYTES_READ);
if(NO_BYTES_READ > 0)
cout<<_DATA_RECIEVED;
}
This is not the most elegant way of doing it (far from it), but at least you should get the data you expect back.
Remember, InternetReadFile passes back a buffer of data, not necessarily a string! It could be an image, junk, and even if it is a string, in your case, it wont have a null byte to close it off. InternetReadFile reads raw bytes, NOT text.
A more elegant solution might start like this:
std::string resultRss;
BYTE _DATA_RECIEVED[_SIZE];
DWORD NO_BYTES_READ = 0;
while(InternetReadFile(WEB_ADDRESS,_DATA_RECIEVED,_SIZE,&NO_BYTES_READ)){
resultRss.append((char*)_DATA_RECIEVED, NO_BYTES_READ); //doesn't matter about null-byte because we are defining the number of bytes to append. This also means we don't NEED to clear the memory, although you might want to.
}
//output final result
cout << resultRss;
Also, as a commenter added, you need to lay off the ALLCAPS for variables.
Hope this helps.

How to download an image from an URL to a local dir?

I'm using C++ without .NET on Win32, how can I download an image over HTTP from a website without having to re-invent the wheel? Is there an API or library that provides a single function to do this?
http://mywebsite/file.imgext --> C:\path\to\dir\file.imgext
WinInet APIs are easier than you think
Here is a complete win32 console program. Can be built with with VS 2010 Express and down loading windows SDK to get WinInit.
// imaged.cpp : Defines the entry point for the console application.
//
// Copy file from internet onto local file
// Uses Wininet API
// program takes 1 mandatory command line argument - URL string
// it downloads ito the current directory, or whatever is passed
// as the second parameter to DownloadURLImage.
// optional parameter, the name of the file (excluding path), by default it uses the
// filename from the URL string.
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <WinInet.h> // from SDK
#include "string.h"
//#include<TCHAR.H>
//#include "Tchar.h"
using namespace std ;
int convertURLtofname (TCHAR * szURL, TCHAR * szname )
// extract the filename from the URL
{
char aszfilename [100];
HRESULT result;
char achar[3], aszURL [100];
size_t nchars, i, j;
int fresult;
fresult = 0;
nchars= _tcslen(szURL);
i= nchars -1;
while ((i > 0) && (szURL[i] != '/') && (szURL[i] != '\\')) {i--;}
j= 0; i++;
while (i < nchars) { szname [j++]= szURL[i++]; }
szname[j]=_T('\0');
// wcstombs ( aszfilename, szname, 100 );
// cout << aszfilename << endl;
//----------------------------------------------
return fresult ;
}
int determinepathfilename (TCHAR * szURL, TCHAR * szpath, TCHAR * szname, TCHAR * szpathfilename)
{
// use path and filename when supplied. If filename (e.g. funkypic.jpg) is not supplied, then the
// filename will be extracted from the last part of the URL
int result ;
result= 0;
TCHAR szname_copy [100] ;
if ((szname == NULL) || (szname[0] == '\0'))
convertURLtofname (szURL, szname_copy);
else
_tcscpy (szname_copy, szname);
if ((szpath == NULL) || (szpath[0] == '\0'))
_tcscpy (szpathfilename, szname_copy);
else
{
_tcscpy (szpathfilename, szpath);
_tcscat (szpathfilename, szname_copy);
}
return result ;
}
bool GetFile (HINTERNET hOpen, // Handle from InternetOpen()
TCHAR *szURL, // Full URL
TCHAR * szpath,
TCHAR * szname)
{
DWORD dwSize;
TCHAR szHead[15];
BYTE * szTemp[1024];
HINTERNET hConnect;
FILE * pFile;
TCHAR szpathfilename [100] ;
szHead[0] = '\0';
if ( !(hConnect = InternetOpenUrl( hOpen, szURL, szHead, 15, INTERNET_FLAG_DONT_CACHE, 0)))
{
std::cout << "Error: InternetOpenUrl" << std::endl;
return 0;
}
determinepathfilename (szURL, szpath, szname, szpathfilename);
if ( !(pFile = _tfopen (szpathfilename, _T("wb") ) ) )
{
std::cerr << "Error _tfopen" << std::endl;
return false;
}
do
{
// Keep copying in 1024 bytes chunks, while file has any data left.
// Note: bigger buffer will greatly improve performance.
if (!InternetReadFile (hConnect, szTemp, 1024, &dwSize) )
{
fclose (pFile);
std::cerr << "Error InternetReadFile" << std::endl;
return FALSE;
}
if (!dwSize)
break; // Condition of dwSize=0 indicate EOF. Stop.
else
fwrite(szTemp, sizeof (BYTE), dwSize , pFile);
} // do
while (TRUE);
fflush (pFile);
fclose (pFile);
return TRUE;
}
int DownloadURLImage (TCHAR * szURL, TCHAR * szpath, TCHAR * szname)
{ int result ;
HINTERNET hInternet;
result= 0;
hInternet= InternetOpen (_T("imaged"),
INTERNET_OPEN_TYPE_DIRECT, //__in DWORD dwAccessType
NULL, //__in LPCTSTR lpszProxyName,
NULL, //__in LPCTSTR lpszProxyBypass,
NULL //_in DWORD dwFlags
);
GetFile (hInternet, szURL, szpath, szname) ;
InternetCloseHandle(hInternet);
return result ;
}
int _tmain(int argc, _TCHAR* argv[])
{
if (argc == 2)
{
DownloadURLImage (argv[1], NULL, NULL);
//DownloadURLImage (argv[1], _T"C:/", NULL);
}
else if (argc == 3)
{
DownloadURLImage (argv[1], NULL, argv[2]);
//DownloadURLImage (argv[1], _T"C:/", argv[2]);
}
else
{
cout << "Usage: imaged <image URL>" << endl ;
}
system("pause") ;
return 0;
}
You could use cURLpp
I havn't used it yet, but example20 looks like it could solve your problem.
If you want an EASY solution, use this amazingly simple one liner:
system("C:\\Path\\To\\Wget\\wget.exe http://pixelcaster.com/yosemite/webcams/ahwahnee2.jpg -O C:\\Users\\Me\\Desktop\\ahwahnee2.jpg");
With wget for windows
choco install wget
See chocolatey.org
Use Windows Http Services API.
You could use the WinInet or WinHTTP classes in C++. These are native Win32 APIs the abstract some of the work of getting sending and receiving files from the Internet.
I've used WinInet with great success to do just what you're trying to do.
If starting a new process is ok, you could have a look at WGET. (And even if not, the sources are available; you can look there to see how it's been implemented.)
Using POCO for this now. :-)