WinHTTP |
Microsoft Windows HTTP Services is a set of APIs to send requests using the HTTP protocol to other HTTP servers. WinHTTP provides a C/C++ application programming interface and a Component Object Model (COM). Microsoft Windows HTTP Services es un conjunto de APIs para enviar solicitudes usando el protocolo HTTP a otros servidores de HTTP. WinHTTP pueden usarse desde C/C++ o bien desde cualquier lenguaje que soporte Component Object Model (COM). |
Tip |
As WinINet was not designed for programs that will run as a Windows Service, Microsoft created WinHTTP to provide a platform to create Windows Services that use HTTP. Como WinINet no fue diseñada para programas que correrán como un Servicio de Windows, Microsoft creo WinHTTP para proporcionar una plataforma para crear Servicios de Windows que usen HTTP. |
Problem 1 |
Create a Wintempla Dialog Application called WinHTest to download the Google page using HTTPS. After creating the project, edit the stdafx.h file to uncomment WIN_WINHTTP, and thus, include the WinHTTP header file (winhttp.h), and link with the respective library for WinHTTP (winhttp.lib) as shown. Cree una Aplicación de diálogo de Wintempla llamada WinHTest para descargar la página de Google usando HTTPS. Después de crear el proyecto, edite el archivo stdafx.h para descomentar WIN_WINHTTP y así incluir el archivo de encabezado para WinHTTP (winhttp.h), y enlazar con la librería respectiva para WinHTTP (winhttp.lib) como se muestra. |
stdafx.h |
... //_________________________________________ WinHTTP #define WIN_WINHTTP // #define STRICT // #ifndef WIN_GDI_PLUS_ON #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #endif // #include "Wintempla.h" #include "WintemplaWin.h" |
WinHTest.h |
#pragma once //______________________________________ WinHTest.h #include "Resource.h" class WinHTest: public Win::Dialog { public: WinHTest() { } ~WinHTest() { } static DWORD ChooseAuthScheme(DWORD supportedSchemes); protected: ... }; |
WinHTest.cpp |
... void WinHTest::Window_Open(Win::Event& e) { const bool useSSL = true; HINTERNET hSession = ::WinHttpOpen(L"WinHTest", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (hSession == NULL) { Sys::DisplayLastError(hWnd, L"WinHTest::WinHttpOpen"); return; } //________________________________________________________________________________ Connect to twitter.com INTERNET_PORT port_number = useSSL ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT; HINTERNET hConnect = ::WinHttpConnect(hSession, L"www.google.com", port_number, 0); if (hConnect == NULL) { Sys::DisplayLastError(hWnd, L"WinHTest::WinHttpConnect"); if (hSession) ::WinHttpCloseHandle(hSession); return; } HINTERNET hRequest = ::WinHttpOpenRequest(hConnect, L"GET", L"/", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, useSSL ? WINHTTP_FLAG_SECURE : 0); if (hRequest == NULL) { Sys::DisplayLastError(hWnd, L"WinHTest::WinHttpOpenRequest"); if (hConnect) ::WinHttpCloseHandle(hConnect); if (hSession) ::WinHttpCloseHandle(hSession); return; } bool isDone = false; bool ok = false; DWORD proxyAuthScheme = 0; DWORD status_code = 0; DWORD buffer_len = sizeof(DWORD); DWORD supportedSchemes = 0; DWORD firstScheme = 0; DWORD selectedScheme = 0; DWORD target; DWORD last_status_code = 0; wchar_t text[256]; while (isDone == false) { // If a proxy authentication challenge was responded to, reset those credentials before each SendRequest, because the proxy // may require re-authentication after responding to a 401 or to a redirect. If you don't, you can get into a 407-401-407-401- loop. if (proxyAuthScheme != 0) { // hasResults = ::WinHttpSetCredentials(hRequest, WINHTTP_AUTH_TARGET_PROXY, proxyAuthScheme, proxyUsername, proxyPassword, NULL); } //______________________________________________________________ Send HTTP Request ok = (::WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) == TRUE); if (ok) ok = (::WinHttpReceiveResponse(hRequest, NULL) == TRUE); //______________________________________________________________ Resend the request in case of ERROR_WINHTTP_RESEND_REQUEST error. if (ok == false && GetLastError() == ERROR_WINHTTP_RESEND_REQUEST) continue; //______________________________________________________________Check the status code. if (ok) ok = (::WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status_code, &buffer_len, NULL) == TRUE); if (ok) { switch (status_code) { case 200: // OK! You can use WinHttpReadData to read the contents of the server's response. isDone = TRUE; break; case 401: // The server requires authentication. Obtain the supported and preferred schemes. ok = (::WinHttpQueryAuthSchemes(hRequest, &supportedSchemes, &firstScheme, &target) == TRUE); if (ok) { selectedScheme = ChooseAuthScheme(supportedSchemes); if (selectedScheme == 0) { isDone = TRUE; } else { //hasResults = WinHttpSetCredentials(hRequest, target, selectedScheme, username, password, NULL); } } if (last_status_code== 401) isDone = TRUE; // If the same credentials are requested twice, abort the request. break; case 407: // The proxy requires authentication. Obtain the supported and preferred schemes. ok = (::WinHttpQueryAuthSchemes(hRequest, &supportedSchemes, &firstScheme, &target) == TRUE); // Set the credentials before resending the request. if (ok) proxyAuthScheme = ChooseAuthScheme(supportedSchemes); if (last_status_code== 407) isDone = TRUE; // If the same credentials are requested twice, abort the request. break; default: // ERROR _snwprintf_s(text, 256, _TRUNCATE, L"Error with status code %d", status_code); tbxOutput.Text = text; isDone = TRUE; } } last_status_code = status_code; if (ok == false) isDone = TRUE; // If there are any errors, break out of the loop. } //___________________________________________________________ Read the response DWORD data_size = 0; char* buffer = NULL; wstring wbuffer; if (status_code == 200) { do { //_____________________________ Check for available data data_size = 0; if (::WinHttpQueryDataAvailable(hRequest, &data_size) == FALSE) { Sys::GetLastErrorInformation(text, 256); tbxOutput.Text += text; break; } if (data_size == 0) break; buffer = new char[data_size+1]; if (buffer == NULL) { tbxOutput.Text += L"ERROR: out of memory\r\n"; break; } //_______________________________ Read the Data ::ZeroMemory(buffer, data_size+1); if (::WinHttpReadData(hRequest, (LPVOID)buffer, data_size, &data_size) == TRUE) { Sys::Convert::StringToWstring(buffer, wbuffer); tbxOutput.Text += wbuffer; } else { Sys::GetLastErrorInformation(text, 256); tbxOutput.Text += text; } if (buffer != NULL) delete [] buffer; } while (data_size > 0); } if (hRequest) ::WinHttpCloseHandle(hRequest); if (hConnect) ::WinHttpCloseHandle(hConnect); if (hSession) ::WinHttpCloseHandle(hSession); } DWORD WinHTest::ChooseAuthScheme(DWORD supportedSchemes) { if (supportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE) return WINHTTP_AUTH_SCHEME_NEGOTIATE; else if (supportedSchemes & WINHTTP_AUTH_SCHEME_NTLM) return WINHTTP_AUTH_SCHEME_NTLM; else if (supportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT) return WINHTTP_AUTH_SCHEME_PASSPORT; else if (supportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST) return WINHTTP_AUTH_SCHEME_DIGEST; else return 0; } |
Problem 2 |
Create a Wintempla Dialog Application called MyNet to download the Google page using HTTPS using Wintempla classes for WinHTTP. Edit the stdafx.h file as shown to activate WinHTTP using Wintempla classes. Cree una Aplicación de diálogo de Wintempla llamada MyNet para descargar la página de Google usando HTTPS usando Wintempla classe para WinHTTP. Edite el archivo stdafx.h como se muestra para activar las classes de Wintempla para WinHTTP. |
stdafx.h |
... //_________________________________________ WinHTTP #define WIN_WINHTTP ... |
MyNet.h |
#pragma once //______________________________________ MyNet.h #include "Resource.h" class MyNet: public Win::Dialog { public: MyNet() { } ~MyNet() { } Web::HttpTransaction transaction; Web::HttpAssistant assistant; ... }; |
MyNet.cpp |
... void MyNet::Window_Open(Win::Event& e) { assistant.synchronous = true; assistant.user_agent = L"MyNet 1.0"; transaction.request.Method = L"GET"; transaction.request.serverName = L"www.google.com"; transaction.request.useHTTPS = false; transaction.request.resource = L"/"; Sys::Error error = assistant.SendRequest(transaction); if (error == true) { error.Display(hWnd, L"MyNet"); return; } wchar_t text[256]; _snwprintf_s(text, 256, _TRUNCATE, L"%s\r\n", Web::HttpResponse::GetCodeDescr(transaction.response.status_code)); tbxOutput.Text += text; tbxOutput.Text +=transaction.response.content_type; tbxOutput.Text += L"\r\n"; // wstring data; Sys::Convert::StringToWstring(transaction.response.data, data); tbxOutput.Text += data; } |
Asynchronous WinHTTP |
It allows processing several HTTP transactions simultaneously. The figure below shows how asynchronous WinHTTP works. In order for WinHTTP to work in this mode, it is necessary to register a status function. This function is typically known as a Status Callback. Basically, some WinHTTP functions will not block execution, instead they will send a notification (displayed in yellow in the figure) to report completion of the execution of the function. You may review the Web::HttpAssistant class to understand asynchronous WinHTTP. Este permite procesar varias transacciones de HTTP simultáneamente. La figura de abajo muestra como WinHTTP asíncrono funciona. A fin de que WinHTTP pueda operar en este modo, es necesario registrar una función de estado. Esta función es típicamente conocida como una Status Callback. Básicamente, algunas funciones de WinHTTP no bloquearan la ejecución, en su lugar ellas enviaran una notificación (mostrada en amarillo en la figura) para reportar que han terminado la ejecución de la función. Usted puede revisar la clase Web::HttpAssistant para entender WinHTTP asíncrono. |
Problem 3 |
Create a Wintempla Dialog Application called MyNetAsync to download several web pages using Asynchronous WinHTTP. Observe that the three Http transactions occur simultaneously. Observe also that the main class MyNetAsync implements the Web::IWinHTTP interface (EDIT the top part of the MyNetAsync.h file) in order to be able to receive the notifications from the HTTP transactions. Cree una Aplicación de diálogo de Wintempla llamada MyNetAsync para descargar varias páginas usando WinHTTP asíncrono. Observe que las tres transacciones de HTTP ocurren en forma simultánea. Observe también que la clase principal MyNetAsync implementa la interface Web::IWinHTTP (EDITE la parte superior del archivo MyNetAsync.h) a fin de poder recibir las notificaciones de las transacciones de HTTP. |
Step A |
Edit the stdafx.h file as shown to activate WinHTTP using Wintempla classes. Edite el archivo stdafx.h como se muestra para activar las classes de Wintempla para WinHTTP. |
stdafx.h |
... //_________________________________________ WinHTTP #define WIN_WINHTTP ... |
Step B |
Edit the GUI as shown and then edit the files: MyNetAsync.h and MyNetAsync.cpp. Be sure to implement the Web::IWinHTTP interface. Observe that each transaction requires an object that implements IWinHTTP, in this case we are using the same object for all the three transaction, however, you can create three different classes that implement IWinHTTP, one for each transaction. Edite la GUI como se muestra y entonces edite los archivos: MyNetAsync.h y MyNetAsync.cpp. Asegúrese de implementar la interface Web::IWinHTTP. Observe que cada transacción requiere un objeto que implemente IWinHTTP, en este caso nosotros estamos usando el mismo objeto para las tres transacciones, sin embargo, usted puede crear tres distintas clases que implementen IWinHTTP, una para cada transacción. |
MyNetAsync.h |
#pragma once //______________________________________ MyNetAsync.h #include "Resource.h" #define TRANSACION_A 0 #define TRANSACION_B 1 #define TRANSACION_C 2 class MyNetAsync: public Win::Dialog, public Web::IWinHTTP { public: MyNetAsync() { } ~MyNetAsync() { } Web::HttpTransaction transactionA; Web::HttpTransaction transactionB; Web::HttpTransaction transactionC; // Web::HttpAssistant assistant; //___________________________________________________________________________ Web::IWinHTTP // This function is called when an error is found. You must return from this function as quickly as possible! void OnWinHTTPError(Web::HttpTransaction& httpTransaction, const Sys::Error& error); // This function is called to notify that the HTTP transaction has completed. You must return from this function as quickly as possible! void OnWinHTTPComplete(Web::HttpTransaction& httpTransaction); protected: ... }; |
MyNetAsync.cpp |
... void MyNetAsync::Window_Open(Win::Event& e) { assistant.synchronous = false; Sys::Error error; //______________________________________________________ Transaction A transactionA.ID = TRANSACION_A; transactionA.request.Method = L"GET"; transactionA.request.serverName = L"www.google.com"; transactionA.request.useHTTPS = false; transactionA.request.resource = L"/"; transactionA.iwinhttp= this; //_______ Send A error = assistant.SendRequest(transactionA); if (error) error.Display(hWnd, L"MyNetAsync TransactionA"); //______________________________________________________ Transaction B transactionB.ID = TRANSACION_B; transactionB.request.Method = L"GET"; transactionB.request.serverName = L"www.yahoo.com"; transactionB.request.useHTTPS = false; transactionB.request.resource = L"/"; transactionB.iwinhttp= this; //_______ Send B error = assistant.SendRequest(transactionB); if (error) error.Display(hWnd, L"MyNetAsync TransactionB"); //______________________________________________________ Transaction C transactionC.ID = TRANSACION_C; transactionC.request.Method = L"GET"; transactionC.request.serverName = L"www.microsof.com"; transactionC.request.useHTTPS = false; transactionC.request.resource = L"/"; transactionC.iwinhttp= this; //_______ Send C error = assistant.SendRequest(transactionC); if (error) error.Display(hWnd, L"MyNetAsync TransactionC"); } void MyNetAsync::OnWinHTTPError(Web::HttpTransaction& httpTransaction, const Sys::Error& error) { wstring text; wstring data; error.GetInformation(text); Sys::Convert::StringToWstring(httpTransaction.response.data, data); if (httpTransaction.ID == TRANSACION_A) { tbxHeadA.Text = text; tbxDataA.Text = data; } else if (httpTransaction.ID == TRANSACION_B) { tbxHeadB.Text = text; tbxDataB.Text = data; } else if (httpTransaction.ID == TRANSACION_C) { tbxHeadC.Text = text; tbxDataC.Text = data; } } void MyNetAsync::OnWinHTTPComplete(Web::HttpTransaction& httpTransaction) { wstring data; if (httpTransaction.ID == TRANSACION_A) { tbxHeadA.Text = httpTransaction.response.header; Sys::Convert::StringToWstring(httpTransaction.response.data, data); tbxDataA.Text = data; //httpTransaction.response.data.SaveToFile(L"C:\\Users\\John\\water.jpg"); } else if (httpTransaction.ID == TRANSACION_B) { tbxHeadB.Text = httpTransaction.response.header; Sys::Convert::UTF8ToWstring(httpTransaction.response.data, data); tbxDataB.Text = data; } else if (httpTransaction.ID == TRANSACION_C) { tbxHeadC.Text = httpTransaction.response.header; Sys::Convert::UTF8ToWstring(httpTransaction.response.data, data); tbxDataC.Text = data; } } |