.NET WebClient.DownloadData(url) alternative in C++/WinAPI? - c++

How can I get contents of a file online with C++?

There are a number of ways you can do this.
WinInet
First off Windows has a built-in API allowing you to make HTTP requests, which is reasonably simple to use. I use this simple wrapper class to download files using it:
/**
* Simple wrapper around the WinInet library.
*/
class Inet
{
public:
explicit Inet() : m_hInet(NULL), m_hConnection(NULL)
{
m_hInet = ::InternetOpen(
"My User Agent",
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
/*INTERNET_FLAG_ASYNC*/0);
}
~Inet()
{
Close();
if (m_hInet)
{
::InternetCloseHandle(m_hInet);
m_hInet = NULL;
}
}
/**
* Attempt to open a URL for reading.
* #return false if we don't have a valid internet connection, the url is null, or we fail to open the url, true otherwise.
*/
bool Open(LPCTSTR url)
{
if (m_hInet == NULL)
{
return false;
}
if (url == NULL)
{
return false;
}
m_hConnection = ::InternetOpenUrl(
m_hInet,
url,
NULL /*headers*/,
0 /*headers length*/,
INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI,
reinterpret_cast<DWORD_PTR>(this));
return m_hConnection != NULL;
}
/**
* Read from a connection opened with Open.
* #return true if we read data.
*/
bool ReadFile(LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD dwRead)
{
ASSERT(m_hConnection != NULL);
return ::InternetReadFile(m_hConnection, lpBuffer, dwNumberOfBytesToRead, dwRead) != 0;
}
/**
* Close any open connection.
*/
void Close()
{
if (m_hConnection != NULL)
{
::InternetCloseHandle(m_hConnection);
m_hConnection = NULL;
}
}
private:
HINTERNET m_hInet;
HINTERNET m_hConnection;
};
Usage of this is very simple:
Inet inet;
if (inet.Open(url))
{
BYTE buffer[UPDATE_BUFFER_SIZE];
DWORD dwRead;
while (inet.ReadFile(&buffer[0], UPDATE_BUFFER_SIZE, &dwRead))
{
// TODO: Do Something With buffer here
if (dwRead == 0)
{
break;
}
}
}
LibCurl
If you'd rather avoid Windows-specific APIs then you could do a lot worse than use the libcurl library to get files using a variety of protocols, including HTTP. There is a good sample showing how to retrieve a URL directly into memory (avoiding downloading to disk): getinmemory sample.

Use the following functions.
WinHttpConnect
WinHttpOpenRequest
WinHttpSendRequest
WinHttpReceiveResponse
WinHttpQueryDataAvailable
WinHttpReadData

Related

Sending Data Structures over Named Pipes

I found this StackOverflow question:
Create Named Pipe C++ Windows
And created this class:
#pragma once
#define PIPE_FD TEXT("\\\\.\\pipe\\somepipe")
#define BUFFER_SIZE 1024
// CPipe Class
class CPipe
{
private:
//
// Variables
//
HANDLE hPipe;
char buffer[BUFFER_SIZE];
DWORD dwRead;
DWORD dwWritten;
public:
bool CreatePipe()
{
hPipe = CreateNamedPipe(PIPE_FD, PIPE_ACCESS_DUPLEX | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, PIPE_WAIT, 1, BUFFER_SIZE * 16, BUFFER_SIZE * 16, NMPWAIT_USE_DEFAULT_WAIT, NULL);
return (hPipe == NULL) ? false : true;
}
bool CreatePipeFile()
{
hPipe = CreateFile(PIPE_FD, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
return (hPipe == NULL) ? false : true;
}
void Destroy()
{
DisconnectNamedPipe(hPipe);
}
bool IsPipe()
{
return (hPipe == NULL) ? false : true;
}
bool IsConnected()
{
return (ConnectNamedPipe(hPipe, NULL) != FALSE) ? true : false;
}
void Read()
{
while (ReadFile(hPipe, buffer, sizeof(buffer), &dwRead, NULL) != FALSE)
{
/* do something with data in buffer */
printf("%s", buffer);
}
}
void Write()
{
WriteFile(hPipe, "Hello Pipe\n", 12, &dwWritten, NULL);
CloseHandle(hPipe);
}
};
extern CPipe gPipe;
Main Process:
gPipe.CreatePipe();
while (gPipe.IsPipe())
{
if (gPipe.IsConnected())
{
gPipe.Read();
}
gPipe.Destroy();
}
Remote:
gPipe.CreatePipeFile();
if (gPipe.IsPipe())
{
gPipe.Write();
}
Which works great. I can send "Hello Pipe\n" between two applications. However, I am trying to modify it to send data structures rather than strings.
For example, this structure:
struct Test_t
{
int x;
int y;
float J[3];
bool Yes;
};
That way, the client can send the structure over the pipe, and the server can read the structure off the pipe and update local server structs accordingly.
I have tried:
reinterpret_cast<char*>(&test);
But I haven't been able to get it to work. Any ideas?
Any help is appreciated.
The reinterpret cast should probably look like this:
reinterpret_cast<void*>(&test);
You also need to make sure you get the amount of data to transfer right (sizeof is your friend here).
Note that sending structs like that can get risky.
The objects sent need to be plain old data objects or they will not serialize properly. See What are POD types in C++? for more information about plain old data. Pointer members, virtual functions etc. will not work.
If the two processes talking to each other have different ideas about structure packing things will come out wrong on the other side. As long as you have full control over the build environment this should be fine, but when things starts breaking you can get some really nasty bugs to fix.
Unless you have extreme performance considerations you should look into some more organized way of serializing the data you send over the pipe such as google protocol buffers or boost::serialize that will make sure the wire format is a little bit less fragile. protocol buffers (and many other technologies) have the advantage of not being c++ specific so you can talk between processes created in different programming languages.

How to detect when the Internet connection was lost - WinINet - C++

My ++ program connects to my FTP server and does some stuff which takes about 10 minutes. I want that when the connection is lost, the program waits until the connection is back then resumes. Here's the code I made (incomplete, please read comments):
#include <WinINet.h> //and other headers
HINTERNET hIntSession;
HINTERNET hFtpSession;
using namespace std;
void ResetConnection() {
if (hIntSession != NULL) InternetCloseHandle(hIntSession); //Do I need to add this line?
do {
if ((hIntSession = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0)) == NULL) continue;
hFtpSession = InternetConnect(hIntSession, "x.example.com", 21, "user", "pass", INTERNET_SERVICE_FTP, 0, 0);
if (hFtpSession == NULL) {
InternetCloseHandle(hIntSession); continue;
}
} while (FALSE);
}
int main() {
ResetConnection();
/*
FTP stuff
if(is not connected) ResetConnection();
FTP stuff
if(is not connected) ResetConnection();
and so on ...
*/
InternetCloseHandle(hConnect);
}
Also, I don't want to use extra memory, so maybe I should add more of "InternetCloseHandle()" function.
Please do not ask me to install a third party library.

Sending http request repeatedly, more fast (async mode)

I need to send one request to server more quickly and repeatedly.
So I implemented code like this.
BOOL CTestDlg::OnInitDialog()
{
...
m_hInstance = InternetOpen(L"asynchttp", INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,INTERNET_FLAG_ASYNC);
if(m_hInstance == NULL)
{
ErrorLog(L"InternetOpen Failed");
return TRUE;
}
if (InternetSetStatusCallback(m_hInstance,(INTERNET_STATUS_CALLBACK)&Callback) == INTERNET_INVALID_STATUS_CALLBACK)
{
ErrorLog(L"Set Callback Function Failed");
return TRUE;
}
m_hConnect = InternetConnect(m_hInstance, L"192.168.2.116", 8080,NULL,NULL,INTERNET_SERVICE_HTTP,INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_KEEP_CONNECTION,1);
if(m_hConnect == NULL)
{
if(DWORD dwError = GetLastError() != ERROR_IO_PENDING)
{
ErrorLog(L"Fail to Connect Server");
return TRUE;
}
WaitForSingleObject(hConnectedEvent, INFINITE);
}
CreateThread(0 , 0 , (LPTHREAD_START_ROUTINE)SendThread , 0 , 0 , 0);
return TRUE;
}
void __stdcall Callback(HINTERNET hInternet,DWORD dwContext,DWORD dwInternetStatus,LPVOID lpStatusInfo,DWORD dwStatusInfoLen)
{
switch(dwContext)
{
case 1:
{
SetEvent(hConnectedEvent);
}
break;
}
}
DWORD SendThread(LPVOID lpParam)
{
TCHAR *szAceptType[] = {_T("*/*") , NULL};
CString szContentType = _T("Content-Type: application/x-www-form-urlencoded\r\n");
char szPostData[MAX_PATH];
sprintf(szPostData , "num=1234&str=3240");
HINTERNET hRequest = HttpOpenRequest(m_hConnect, L"POST", L"/TestWeb/index.jsp", HTTP_VERSION , NULL , (LPCTSTR*)szAceptType , 0, 2);
while(1)
{
try
{
if (!HttpSendRequest(hRequest, szContentType, (DWORD)szContentType.GetLength(), szPostData,(DWORD)strlen(szPostData)))
{
if (DWORD dwError = GetLastError() != ERROR_IO_PENDING)
ErrorLog(L"SendRequest: Error = %d" , dwError);
}
}
catch (CException* e)
{
UNREFERENCED_PARAMETER(e);
}
Sleep(100);
}
return 0;
}
When execute or debug program, I saw this "SendRequest; Error = 1" log frequently.
And Server does not record anymore to database , after one or two request data recorded
It seems like HttpSendRequest API doesn't work correctly after error occured.
I aim to send one request to server more fast and more correctly, without loss.
Please teach me what is wrong problem.
Or if you got another best way, then please tell me.
Thanks.
Error code 1 is "Invalid Parameter".
You appear to be mixing ASCII (char and sprintf) and Unicode strings in your sample above. Have you tried with all Unicode strings (making szPostData a WCHAR array)? From MSDN: "There two versions of HttpSendRequest —HttpSendRequestA (used with ANSI builds) and HttpSendRequestW (used with Unicode builds)". It goes on to talk about when ERROR_INVALID_PARAMETER is returned. http://msdn.microsoft.com/en-us/library/windows/desktop/aa384247(v=vs.85).aspx
Also, I haven't used MFC in a long time, but I remember that you should call e->Delete() in your exception handler (unless you re-throw). The CException::Delete method will delete any thrown exception created on the heap (and do nothing otherwise).

FtpGetFile WinINEt never returns

I'm experiencing a curious problem (very strange, let me say hehe). During a FTP download of an EXE file (24 MB), if the connection is ever interrupted, it appears that the function FtpGetFile of the WinINEt library has a bug and it never returns. This causes that future file transfers fail (the connection is already opened).
Apparently, I found a workaround by increasing the timeout of the server transfers but I do not like it. I didn't found a similar problem by googling (maybe I introduced the wrong keywords).
I read some forums on the internet and it seems that everyone does not recommend using the FtpGetFile because it is buggy.
This appears in a network scenario that has a big lag (and not always) but in good conditions it disappears (downloads take place correctly and FtpGetFile returns always).
Here is how I use the function:
if( FtpGetFile(m_hFtpSession, strSourcePath.c_str(), strTargetPath.c_str(), 0, 0, FTP_TRANSFER_TYPE_BINARY, 0)==TRUE)
Can anyone confirm that? Should I refactor my code and look for an update?
Thank you
I found a way to download files without using FtpGetFile. I hope this code can help someone:
bool RetrieveFile(const string& strSource, const string& strTarget) {
/* The handle for the transfer */
HINTERNET hTransfer = NULL;
/*
* Set default error
*/
DWORD error = ERROR_SUCCESS;
if( !isConnected ) {
debug("%s(): ERROR not connected\n", __FUNCTION__);
return false;
}
/* Initiate access to a remote FTP connection */
hTransfer = FtpOpenFile(hFtpSession, strSource.c_str(), GENERIC_READ,
FTP_TRANSFER_TYPE_BINARY, 0);
if(hTransfer) {
std::ofstream myostream(strTarget.c_str(), std::ios::binary);
if ( myostream.is_open() ) {
static const DWORD SIZE = 1024;
BYTE data[SIZE];
DWORD size = 0;
do {
BOOL result = InternetReadFile(hTransfer, data, SIZE, &size);
if ( result == FALSE ) {
error = GetLastError();
Debug("InternetReadFile(): %lu\n", error);
}
myostream.write((const char*)data, size);
}
while ((error == ERROR_SUCCESS) && (size > 0));
// Close the stream
myostream.close();
}
else {
Debug("Could not open '%s'.\n", strTarget.c_str());
error = ERROR_FILE_NOT_FOUND; // Not necessarily not found, but it is to describe a file error which is different from ERROR_SUCCESS
}
// Close
const BOOL result = InternetCloseHandle(hTransfer);
if ( result == FALSE ) {
const DWORD error = GetLastError();
debug("InternetClose(): %lu\n", error);
}
/* Check error status of the process */
return (error == ERROR_SUCCESS);
}
DWORD dwInetError;
DWORD dwExtLength = 1000;
TCHAR *szExtErrMsg = NULL;
TCHAR errmsg[1000];
szExtErrMsg = errmsg;
int returned = InternetGetLastResponseInfo( &dwInetError, szExtErrMsg, &dwExtLength );
debug("dwInetError: %d Returned: %d\n", dwInetError, returned);
debug("Buffer: %s\n", szExtErrMsg);
debug("%s() : ERROR to get '%s' file (errorCode=%d)\n", __FUNCTION__, strSource.c_str(), GetLastError());
return false;
}

How do I detect a disabled Network Interface Connection from a Windows application?

I would like to know when a interface has been disabled.
If I go into the windows manager and disable one of the 2 enabled connections, GetIfTable() only returns status about 1 interface, it no longer sees the disconnected one.
(Returns 1 table)
How can I get something to return that the disabled interface still exists but is currently disabled?
Thanks.
http://msdn.microsoft.com/en-us/library/aa365943%28VS.85%29.aspx
I think you would just need to read the registry.
For example, this is a snippet found on the web of what things should look like:
[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{1E6AF554-25FF-40FC-9CEE-EB899472C5A3}\Connection]
"PnpInstanceID"="PCI\\VEN_14E4&DEV_1696&SUBSYS_12BC103C&REV_03\\4&3A321F38&0&10F0"
"MediaSubType"=dword:00000001
"Name"="Lan Name"
"ShowIcon"=dword:00000000
"IpCheckingEnabled"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{1E6AF554-25FF-40FC-9CEE-EB899472C5A3}\Connection]
"PnpInstanceID"="PCI\\VEN_14E4&DEV_1696&SUBSYS_12BC103C&REV_03\\4&3A321F38&0&10F0"
"MediaSubType"=dword:00000001
"Name"="Lan Name"
"ShowIcon"=dword:00000000
"IpCheckingEnabled"=dword:00000001
How about using the interfaces from netcon.h as illustrated in this example? The code in that example enables and disables the interface programmatically, but I've made some modifications so that you could query the status instead:
#include <netcon.h>
// wszName is the name of the connection as appears in Network Connections folder
// set bEnable to true to enable and to false to disable
bool GetConnectionStatus(LPCWSTR wszName, bool *status)
{
bool result = false;
if (!status)
return false;
typedef void (__stdcall * LPNcFreeNetconProperties)(NETCON_PROPERTIES* pProps);
HMODULE hmod = LoadLibrary("netshell.dll");
if (!hmod)
return false;
LPNcFreeNetconProperties NcFreeNetconProperties =
(LPNcFreeNetconProperties)GetProcAddress(hmod, "NcFreeNetconProperties");
if (!NcFreeNetconProperties )
return false;
INetConnectionManager * pMan = 0;
HRESULT hres = CoCreateInstance(CLSID_ConnectionManager,
0,
CLSCTX_ALL,
__uuidof(INetConnectionManager),
(void**)&pMan);
if (SUCCEEDED(hres))
{
IEnumNetConnection * pEnum = 0;
hres = pMan->EnumConnections(NCME_DEFAULT, &pEnum);
if (SUCCEEDED(hres))
{
INetConnection * pCon = 0;
ULONG count;
while (pEnum->Next(1, &pCon, &count) == S_OK && !done)
{
NETCON_PROPERTIES * pProps = 0;
hres = pCon->GetProperties(&pProps);
if (SUCCEEDED(hres))
{
if (wcscmp(pProps->pszwName,wszName) == 0)
{
*status = pProps->Status == NCS_CONNECTED;
}
NcFreeNetconProperties(pProps);
}
pCon->Release();
}
pEnum->Release();
}
pMan->Release();
}
FreeLibrary(hmod);
return result;
}
Another option is use the Win32_NetworkAdapter WMI Class , check the NetConnectionStatus and NetEnabled properties.
The IP_ADAPTER_ADDRESSES structure hold an OperStatus member.
See MSDN documentation
I think it can be used to detect disabled NICs. I didn't try.
Here is a test code:
ULONG nFlags= 0;
if (WINVER>=0x0600) // flag supported in Vista and later
nFlags= 0x0100; // GAA_FLAG_INCLUDE_ALL_INTERFACES
// during system initialization, GetAdaptersAddresses may return ERROR_BUFFER_OVERFLOW and supply nLen,
// but in a subsequent call it may return ERROR_BUFFER_OVERFLOW and supply greater nLen !
ULONG nLen= sizeof (IP_ADAPTER_ADDRESSES);
BYTE* pBuf= NULL;
DWORD nErr= 0 ;
do
{
delete[] pBuf;
pBuf= new BYTE[nLen];
nErr= ::GetAdaptersAddresses(AF_INET, nFlags, NULL, (IP_ADAPTER_ADDRESSES*&)pBuf, &nLen);
}
while (ERROR_BUFFER_OVERFLOW == nErr);
if (NO_ERROR != nErr)
{
delete[] pBuf;
TCHAR czErr[300]= _T("GetAdaptersAddresses failed. ");
REPORT(REP_ERROR, _T("GetAdapterInfo"), GetSysErrStr(nErr, czErr, 300));
return false;
}
const IP_ADAPTER_ADDRESSES* pAdaptersAddresses= (IP_ADAPTER_ADDRESSES*&)pBuf;
while (pAdaptersAddresses) // for each adapter
{
TCHAR czAdapterName [500]; str_cpy(czAdapterName , 500, pAdaptersAddresses->AdapterName );
TCHAR czDesc [500]; str_cpy(czDesc , 500, pAdaptersAddresses->Description );
TCHAR czFriendlyName[500]; str_cpy(czFriendlyName, 500, pAdaptersAddresses->FriendlyName);
const IF_OPER_STATUS& Stat= pAdaptersAddresses->OperStatus; // 1:up, 2:down...
...
pAdaptersAddresses= pAdaptersAddresses->Next;
}
According to this CodeGuru forum message, you can query WMI for this information (A C# code is provided there).
To query WMI using C++, see these two links:
MSDN
CodeProject
command-line:
wmic NIC where(ConfigManagerErrorCode=22)get Description,Index,NetConnectionID,PNPDeviceID
Output:
Description Index NetConnectionID PNPDeviceID
Broadcom 802.11g Network Adapter 8 WiFi PCI\VEN_14E4&DEV_4320&SUBSYS_041814E4&REV_03\4&31B6CD7&0&00F0
1394 Net Adapter 13 1394 V1394\NIC1394\1B9E0F31E8C00
TAP-Win32 Adapter V9 14 Steganos Internet Anonym 2012 VPN Adapter ROOT\NET\0000
VirtualBox Host-Only Ethernet Adapter 24 VirtualBox Host-Only Network ROOT\NET\0001