LoginSignup
9
4

More than 1 year has passed since last update.

WinHTTPの使い方

Last updated at Posted at 2018-10-25

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;
	}
9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4