WinHTTPについて
WinHTTPはWindows用のHTTP通信を行うためのコンポーネントです。
本記事ではWin32APIからWinHTTPを使うサンプルを記載しています。
動作環境
WinHTTPの対応OSについては以下を参照してください。
WinHTTP Versions
Windows 2000はService Pack 3以上が必要です。
Windows XPはService Pack 1 (SP1)以上が必要です。
Windows XPでTLS 1.1とTLS 1.2を使うためには別途対応が必要です。
例えば以下の方法
TLS 1.2 · sayurin/ffftp Wiki · GitHub
ただし、この対応をしてもTLS 1.2は正常に動作しない様子。
Windows 7でTLS 1.1とTLS 1.2を使うには以下の対応が必要です。
技術情報 3140245 WinHTTP が Windows での既定のセキュリティで保護されたプロトコルとして TLS 1.1 および TLS 1.2 を有効にする更新プログラム
WinHTTPの使い方
WinHTTPを使うには「winhttp.lib」をリンクして「winhttp.h」をインクルードする必要があります。
WinHTTPはUNICODE用のAPIのみで、マルチバイト用のAPIはありません。
以下はGETリクエストの最低限のサンプルです。
ヘッダーと本文を受信してコンソールに出力します。
#include <windows.h>
#include <stdio.h>
#include <winhttp.h>
#pragma comment (lib, "winhttp.lib")
int main()
{
WCHAR *url = L"https://xxx/";
HINTERNET hSession, hConnect, hRequest;
URL_COMPONENTS urlComponents = { 0 };
WCHAR szHostName[256], szUrlPath[2048];
WCHAR *header;
DWORD ret = 0;
DWORD dwSize;
DWORD dwStatusCode;
// WinHTTPの初期化
hSession = WinHttpOpen(L"UserAgent/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession == NULL) {
return -1;
}
// URL解析
urlComponents.dwStructSize = sizeof(URL_COMPONENTS);
urlComponents.lpszHostName = szHostName;
urlComponents.dwHostNameLength = sizeof(szHostName) / sizeof(WCHAR);
urlComponents.lpszUrlPath = szUrlPath;
urlComponents.dwUrlPathLength = sizeof(szUrlPath) / sizeof(WCHAR);
if (!WinHttpCrackUrl(url, wcslen(url), 0, &urlComponents)) {
WinHttpCloseHandle(hSession);
return -1;
}
// HTTPの開始
hConnect = WinHttpConnect(hSession, szHostName, urlComponents.nPort, 0);
if (hConnect == NULL) {
WinHttpCloseHandle(hSession);
return -1;
}
hRequest = WinHttpOpenRequest(hConnect, L"GET", szUrlPath,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
(INTERNET_SCHEME_HTTPS == urlComponents.nScheme) ? WINHTTP_FLAG_SECURE : 0);
if (hRequest == NULL) {
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0,
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0) == FALSE) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (WinHttpReceiveResponse(hRequest, NULL) == FALSE) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// ヘッダー取得
dwSize = 0;
if (WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
WINHTTP_NO_OUTPUT_BUFFER, &dwSize,
WINHTTP_NO_HEADER_INDEX) == FALSE) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
}
header = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, dwSize);
if (header == NULL) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
header, &dwSize, WINHTTP_NO_HEADER_INDEX) == FALSE) {
HeapFree(GetProcessHeap(), 0, header);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
wprintf(L"%s", header);
HeapFree(GetProcessHeap(), 0, header);
// レスポンスコードの確認
dwSize = sizeof(DWORD);
WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX, &dwStatusCode, &dwSize,
WINHTTP_NO_HEADER_INDEX);
if (dwStatusCode != HTTP_STATUS_OK) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// 本文の受信
while (1) {
DWORD dwSize = 0;
if (WinHttpQueryDataAvailable(hRequest, &dwSize) == FALSE) {
ret = -1;
break;
}
if (dwSize == 0) {
break;
}
BYTE *buf = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, (dwSize + 1) * sizeof(BYTE));
if (buf == NULL) {
ret = -1;
break;
}
if (WinHttpReadData(hRequest, buf, dwSize, NULL) == FALSE) {
HeapFree(GetProcessHeap(), 0, buf);
ret = -1;
break;
}
buf[dwSize] = '\0';
printf("%s", buf);
HeapFree(GetProcessHeap(), 0, buf);
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return ret;
}
ヘッダーの追加
リクエストにヘッダー項目を追加する場合はWinHttpSendRequest()に指定します。
複数のヘッダーを追加するには改行(\r\n)で区切ります。
長さは -1L を指定すると第2引数の L'\0' までが長さとなります。
if (WinHttpSendRequest(hRequest,
L"Accept: */*\r\nPragma: no-cache\r\nCache-Control: no-cache\r\n",
-1L,
WINHTTP_NO_REQUEST_DATA, 0,
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0) == FALSE) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
エラーの取得
WinHTTPのエラーはGetLastError()で取得でき、FormatMessage()でwinhttp.dllからエラーメッセージを取得できます。
DWORD err;
TCHAR *msg = NULL;
// エラーコードの取得
err = GetLastError();
// エラーメッセージの取得
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle(TEXT("winhttp.dll")), err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msg, 0, NULL);
_tprintf(TEXT("Error: %s\n"), msg);
// エラーメッセージの解放
LocalFree(msg);
ヘッダー追加とエラーメッセージ出力を追加したソースは以下になります。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <winhttp.h>
#pragma comment (lib, "winhttp.lib")
static void PrintHttpError(const TCHAR *func)
{
DWORD err;
TCHAR *msg = NULL;
// エラーコードの取得
err = GetLastError();
// エラーメッセージの取得
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle(TEXT("winhttp.dll")), err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msg, 0, NULL);
if (msg != NULL) {
_tprintf(TEXT("Error: %s: (%u) %s\n"), func, err, msg);
LocalFree(msg);
} else {
_tprintf(TEXT("Error: %s: (%u)\n"), func, err);
}
}
int main()
{
WCHAR *url = L"https://xxx/";
HINTERNET hSession, hConnect, hRequest;
URL_COMPONENTS urlComponents = { 0 };
WCHAR szHostName[256], szUrlPath[2048];
WCHAR *header;
DWORD ret = 0;
DWORD dwSize;
DWORD dwStatusCode;
// WinHTTPの初期化
hSession = WinHttpOpen(L"UserAgent/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession == NULL) {
PrintHttpError("WinHttpOpen");
return -1;
}
// URL解析
urlComponents.dwStructSize = sizeof(URL_COMPONENTS);
urlComponents.lpszHostName = szHostName;
urlComponents.dwHostNameLength = sizeof(szHostName) / sizeof(WCHAR);
urlComponents.lpszUrlPath = szUrlPath;
urlComponents.dwUrlPathLength = sizeof(szUrlPath) / sizeof(WCHAR);
if (!WinHttpCrackUrl(url, wcslen(url), 0, &urlComponents)) {
PrintHttpError(TEXT("WinHttpCrackUrl"));
WinHttpCloseHandle(hSession);
return -1;
}
// HTTPの開始
hConnect = WinHttpConnect(hSession, szHostName, urlComponents.nPort, 0);
if (hConnect == NULL) {
PrintHttpError(TEXT("WinHttpConnect"));
WinHttpCloseHandle(hSession);
return -1;
}
hRequest = WinHttpOpenRequest(hConnect, L"GET", szUrlPath,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
(INTERNET_SCHEME_HTTPS == urlComponents.nScheme) ? WINHTTP_FLAG_SECURE : 0);
if (hRequest == NULL) {
PrintHttpError(TEXT("WinHttpOpenRequest"));
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (WinHttpSendRequest(hRequest,
L"Accept: */*\r\nPragma: no-cache\r\nCache-Control: no-cache\r\n", -1L,
WINHTTP_NO_REQUEST_DATA, 0, WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0) == FALSE) {
PrintHttpError(TEXT("WinHttpSendRequest"));
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (WinHttpReceiveResponse(hRequest, NULL) == FALSE) {
PrintHttpError(TEXT("WinHttpReceiveResponse"));
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// ヘッダー取得
dwSize = 0;
if (WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
WINHTTP_NO_OUTPUT_BUFFER, &dwSize,
WINHTTP_NO_HEADER_INDEX) == FALSE) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
PrintHttpError(TEXT("WinHttpQueryHeaders(Size)"));
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
}
header = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, dwSize);
if (header == NULL) {
PrintHttpError(TEXT("WinHttpQueryHeaders(HeapAlloc)"));
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
header, &dwSize, WINHTTP_NO_HEADER_INDEX) == FALSE) {
PrintHttpError(TEXT("WinHttpQueryHeaders"));
HeapFree(GetProcessHeap(), 0, header);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
wprintf(L"%s", header);
HeapFree(GetProcessHeap(), 0, header);
// レスポンスコードの確認
dwSize = sizeof(DWORD);
WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX, &dwStatusCode, &dwSize,
WINHTTP_NO_HEADER_INDEX);
if (dwStatusCode != HTTP_STATUS_OK) {
_tprintf(TEXT("Error: WinHttpQueryHeaders: %d\n"), dwStatusCode);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// 本文の受信
while (1) {
DWORD dwSize = 0;
if (WinHttpQueryDataAvailable(hRequest, &dwSize) == FALSE) {
PrintHttpError(TEXT("WinHttpQueryDataAvailable"));
ret = -1;
break;
}
if (dwSize == 0) {
break;
}
BYTE *buf = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, (dwSize + 1) * sizeof(BYTE));
if (buf == NULL) {
PrintHttpError(TEXT("WinHttpReadData(HeapAlloc)"));
ret = -1;
break;
}
if (WinHttpReadData(hRequest, buf, dwSize, NULL) == FALSE) {
PrintHttpError(TEXT("WinHttpReadData"));
HeapFree(GetProcessHeap(), 0, buf);
ret = -1;
break;
}
buf[dwSize] = '\0';
printf("%s", buf);
HeapFree(GetProcessHeap(), 0, buf);
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return ret;
}
POSTリクエストについて
POSTリクエストを送信するにはWinHttpOpenRequest()の第2引数に「POST」を指定し、WinHttpSendRequest()でPOSTデータを送信します。
以下はPOSTリクエストを送信するサンプルです。
#include <windows.h>
#include <stdio.h>
#include <winhttp.h>
#pragma comment (lib, "winhttp.lib")
int main()
{
WCHAR *url = L"https://xxx/";
HINTERNET hSession, hConnect, hRequest;
URL_COMPONENTS urlComponents = { 0 };
WCHAR szHostName[256], szUrlPath[2048];
WCHAR *header;
DWORD ret = 0;
DWORD dwSize;
DWORD dwStatusCode;
// WinHTTPの初期化
hSession = WinHttpOpen(L"UserAgent/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession == NULL) {
return -1;
}
// URL解析
urlComponents.dwStructSize = sizeof(URL_COMPONENTS);
urlComponents.lpszHostName = szHostName;
urlComponents.dwHostNameLength = sizeof(szHostName) / sizeof(WCHAR);
urlComponents.lpszUrlPath = szUrlPath;
urlComponents.dwUrlPathLength = sizeof(szUrlPath) / sizeof(WCHAR);
if (!WinHttpCrackUrl(url, wcslen(url), 0, &urlComponents)) {
WinHttpCloseHandle(hSession);
return -1;
}
// HTTPの開始
hConnect = WinHttpConnect(hSession, szHostName, urlComponents.nPort, 0);
if (hConnect == NULL) {
WinHttpCloseHandle(hSession);
return -1;
}
hRequest = WinHttpOpenRequest(hConnect, L"POST", szUrlPath,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
(INTERNET_SCHEME_HTTPS == urlComponents.nScheme) ? WINHTTP_FLAG_SECURE : 0);
if (hRequest == NULL) {
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// POSTデータの送信
char *sendData = "id=1&name=aaa";
if (WinHttpSendRequest(hRequest, L"Content-Type: application/x-www-form-urlencoded\r\n", -1,
sendData, strlen(sendData), strlen(sendData), 0) == FALSE) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (WinHttpReceiveResponse(hRequest, NULL) == FALSE) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// ヘッダー取得
dwSize = 0;
if (WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
WINHTTP_NO_OUTPUT_BUFFER, &dwSize,
WINHTTP_NO_HEADER_INDEX) == FALSE) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
}
header = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, dwSize);
if (header == NULL) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
header, &dwSize, WINHTTP_NO_HEADER_INDEX) == FALSE) {
HeapFree(GetProcessHeap(), 0, header);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
wprintf(L"%s", header);
HeapFree(GetProcessHeap(), 0, header);
// レスポンスコードの確認
dwSize = sizeof(DWORD);
WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX, &dwStatusCode, &dwSize,
WINHTTP_NO_HEADER_INDEX);
if (dwStatusCode != HTTP_STATUS_OK) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// 本文の受信
while (1) {
DWORD dwSize = 0;
if (WinHttpQueryDataAvailable(hRequest, &dwSize) == FALSE) {
ret = -1;
break;
}
if (dwSize == 0) {
break;
}
BYTE *buf = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, (dwSize + 1) * sizeof(BYTE));
if (buf == NULL) {
ret = -1;
break;
}
if (WinHttpReadData(hRequest, buf, dwSize, NULL) == FALSE) {
HeapFree(GetProcessHeap(), 0, buf);
ret = -1;
break;
}
buf[dwSize] = '\0';
printf("%s", buf);
HeapFree(GetProcessHeap(), 0, buf);
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return ret;
}
REST APIなどでJSONデータを送信する場合は以下のように設定します。
// POSTデータの送信
char *sendData = "{\"id\": 1, \"name\": \"aaa\"}";
if (WinHttpSendRequest(hRequest, L"Content-Type: application/json\r\n", -1,
sendData, strlen(sendData), strlen(sendData), 0) == FALSE) {
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}