0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Quick Guide(Win32API)

Last updated at Posted at 2024-09-04

プロセス操作

プロセスの作成

CreateProcess関数を用いて新しいプロセスとプライマリスレッドを作成します。
なお、作成されるプロセスのウインドウに関する指定する情報はSTARTUPINFO構造体に設定します。
また、新しいプロセスは呼び出し元プロセスのセキュリティ コンテキストで実行されます。

Windowsはプロセスを作成するとスレッドが必ず1つ作成されます。

関数が成功すると、戻り値は0以外になります。関数が失敗した場合は、0を返します。
また、起動したプロセスに関する情報はPROCESS_INFORMATION構造体に格納されます。

CreateProcess関数
BOOL CreateProcessA(
    [in, optional]      LPCSTR                lpApplicationName,
    [in, out, optional] LPSTR                 lpCommandLine,
    [in, optional]      LPSECURITY_ATTRIBUTES lpProcessAttributes,
    [in, optional]      LPSECURITY_ATTRIBUTES lpThreadAttributes,
    [in]                BOOL                  bInheritHandles,
    [in]                DWORD                 dwCreationFlags,
    [in, optional]      LPVOID                lpEnvironment,
    [in, optional]      LPCSTR                lpCurrentDirectory,
    [in]                LPSTARTUPINFOA        lpStartupInfo,
    [out]               LPPROCESS_INFORMATION lpProcessInformation
);
STARTUPINFO構造体
typedef struct _STARTUPINFOA {
    DWORD  cb;
    LPSTR  lpReserved;
    LPSTR  lpDesktop;
    LPSTR  lpTitle;
    DWORD  dwX;
    DWORD  dwY;
    DWORD  dwXSize;
    DWORD  dwYSize;
    DWORD  dwXCountChars;
    DWORD  dwYCountChars;
    DWORD  dwFillAttribute;
    DWORD  dwFlags;
    WORD   wShowWindow;
    WORD   cbReserved2;
    LPBYTE lpReserved2;
    HANDLE hStdInput;
    HANDLE hStdOutput;
    HANDLE hStdError;
} STARTUPINFOA, *LPSTARTUPINFOA;
PROCESS_INFORMATION構造体
typedef struct _PROCESS_INFORMATION {
  HANDLE hProcess;
  HANDLE hThread;
  DWORD  dwProcessId;
  DWORD  dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;

なおプロセス作成後、プロセスへアクセス不要になった時点でハンドルをクローズする必要があります。

CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

サンプルプログラム

sample_CreateProcess.c
#include <windows.h>
#include <stdio.h>

int WINAPI main() {
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	// 構造体の確保したメモリ領域を0で埋める(=初期化)
	ZeroMemory(&si, sizeof(si));
	ZeroMemory(&pi, sizeof(pi));

	// STARTUPINFO構造体のサイズを設定する
	si.cb = sizeof(si);

	// 起動したいプロセス名
	char pName[] = "notepad.exe";

	/* PROCESS_INFORMATION
		CreateProcessで作成されたプロセスとそのプライマリスレッドに関する情報が保存される

		HANDLE hProcess		作成されたプロセスのハンドル
		HANDLE hThread		作成されたプライマリスレッドのハンドル
		DWORD dwProcessId	作成されたプロセスのID
		DWORD dwThreadId	作成されたプライマリスレッドのID
	*/

	/* CreateProcess
		CreateProcessの第1引数と第2引数で指定する差分
		 第1引数:フルパスで実行したいファイルを指定しなければならない
		 第2引数:キーワード(notepad, calc.exe)で実行することが可能
	*/

	if (CreateProcess(NULL, pName, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0) {
		printf("Failed to create process\n");
		return 1;
	}

	// 情報の表示
	printf("Process ID\t%d\n", pi.dwProcessId);
	printf("Thread ID\t%d\n", pi.dwThreadId);

	if (CloseHandle(pi.hProcess) == 0 || CloseHandle(pi.hThread) == 0) {
		printf("Failed to close handle\n");

		return 1;
	}

	return 0;
}

プロセスを開く

OpenProcess関数を用いて既に起動しているローカルのプロセスオブジェクトを開きます。
関数が成功した場合、戻り値は指定されたプロセスに対する開いているハンドルです。関数が失敗した場合は、返される値はNULLです。

OpenProcess関数
HANDLE OpenProcess(
    [in] DWORD dwDesiredAccess,
    [in] BOOL  bInheritHandle,
    [in] DWORD dwProcessId
);

プロセスの強制終了

TerminalteProcess関数を用いて、指定したプロセスとそのすべてのスレッドを強制終了します。なお、ハンドルはPROCESS_TERMINATEのアクセス権限を有している必要があります。
関数が成功すると、戻り値は0以外になります。関数が失敗した場合は、0を返します。

TerminateProcess関数
BOOL TerminateProcess(
    [in] HANDLE hProcess,
    [in] UINT   uExitCode
);

サンプルプログラム

プログラムの流れとしては以下のとおりである。なお、プログラム起動時には第1引数として終了させたいプロセスのPIDを指定する。

# 1756はユーザー権限で動作しているnotepad.exe
>example_TerminateProcess.exe 1756
Exit Code:259
Success to terminate process
  1. 終了させるプロセスのハンドルを取得する
  2. GetExitCodeProcess関数を使用して終了コードを取得する
  3. TerminateProcess関数で終了する
sample_TerminateProcess.c
#include <windows.h>
#include <stdio.h>

int WINAPI main(int argc, char* argv[]) {
	DWORD pid = atoi(argv[1]);


	// 1.対象プロセスのハンドルを取得
	//	GetExitCodeProcess関数を実行するためには、アクセス権限「PROCESS_QUERY_LIMITED_INFORMATION」を有していなければならない
	//	TerminateProcess関数を実行するためには、アクセス権限「PROCESS_TERMINATE」を有していなければならない
	HANDLE hProcess = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);

	if (hProcess == NULL) {
		printf("OpenProcess failed\n");
		return 1;
	}

	// 2.終了コードの取得
	DWORD ExitCode = 0;
	if (GetExitCodeProcess(hProcess, &ExitCode) == 0) {
		printf("GetExitCodeProcess failed\n");

		CloseHandle(hProcess);
		return 1;
	}
	printf("Exit Code:%d\n", ExitCode);

	// 3.プロセスの強制終了
	if (TerminateProcess(hProcess, ExitCode) == 0) {
		printf("TerminateProcess failed\n");

		CloseHandle(hProcess);
		return 1;
	}
	else {
		WaitForSingleObject(hProcess, INFINITE);
		printf("Success to terminate process\n");
	}

	CloseHandle(hProcess);
	return 0;
}

ソケット通信

アーキテクチャ

WS2_32.DLLを通じてWindows Sockets 2 APIを活用し、プロトコルスタックと通信する。

Windows ソケット 2のアーキテクチャ

winsockの使い方

基本的にはwinsock2.hws2tcpip.hをインクルードして利用する。また、#pragma comment(lib, "Ws2_32.lib")も宣言してスタティックリンクを行う。

ws2tcpip.hは接続先を指定する際に利用するInetPton関数を利用するために必要なヘッダーファイルである。

Ws2tcpip.h ヘッダー ファイルには、TCP/IP 用の WinSock 2 Protocol-Specific Annex ドキュメントで導入された定義が含まれています。これには、IP アドレスの取得に使用される新しい関数と構造体が含まれています。

また、Windows.hはロードする必要はありません。

Winsock2.h ヘッダー ファイルには内部的に Windows.h ヘッダー ファイルのコア要素が含まれているため、Winsock アプリケーションでは通常、Windows.h ヘッダー ファイルの#include行はありません。 Windows.h ヘッダー ファイルに#include行が必要な場合は、その前に #define WIN32_LEAN_AND_MEAN マクロを付ける必要があります。

#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "Ws2_32.lib")

サーバー モデル

一般的なモデルは以下の通り

  1. Winsock を初期化します。
  2. ソケットを作成します。
  3. ソケットをバインドします。
  4. クライアントのソケットでリッスンします。
  5. クライアントからの接続を受け入れます。
  6. データの受信と送信。
  7. 切断します。

Winsockの初期化

Windowsソケットの実装に関する情報が含まれるWSADATA構造体を準備したのち、WSAStartup関数を用いてプロセスがWinsock DLLを使用できるようにする。

WSAStartup関数の戻り値はint型であり、成功した場合は0を、失敗した場合はエラーコードを返す。

WSADATA wsaData;

int iResult;

// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
    printf("WSAStartup failed: %d\n", iResult);
    return 1;
}

ソケットの作成

  1. 構造体を準備し名前解決を行う

    • ZeroMemory関数を実行してaddrinfo構造体のインスタンス化により確保された領域をすべて0で埋める
      • これを実行しないとgetaddrinfo関数はエラーを起こしてしまうため絶対に実施すること
    • getaddrinfo関数の第1引数には名前解決したいホスト名を、第2引数にはサービス名もしくはポート番号を格納する
    struct addrinfo *result = NULL, hints;
    
    ZeroMemory(&hints, sizeof (hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;
    
    // Resolve the local address and port to be used by the server
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    
  2. リッスン用ソケットオブジェクトの作成
    ソケット作成と統合してもよい

    SOCKET ListenSocket = INVALID_SOCKET;
    
  3. ソケットの作成

    // Create a SOCKET for the server to listen for client connections
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    

サーバーのソケットの作成

ソケットのバインド

サーバーの場合は作成したソケットをネットワークアドレス(IPアドレスとポート)にバインドする必要がある。また、バインド後はgetaddrinfo関数によって返された情報は不要になるためfreeaddrinfo関数でメモリ領域を解放する。

// Setup the TCP listening socket
iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
    printf("bind failed with error: %d\n", WSAGetLastError());
    freeaddrinfo(result);
    closesocket(ListenSocket);
    WSACleanup();
    return 1;
}

ソケットでのリッスン

  • Listen関数を用いてソケットをリッスン状態にする。
  • Listen関数の第2引数は保留中の接続のキューの最大長を示している。SOMAXCONNを指定すると最大の妥当な値に設定してくれる。
if ( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR ) {
    printf( "Listen failed with error: %ld\n", WSAGetLastError() );
    closesocket(ListenSocket);
    WSACleanup();
    return 1;
}

listen関数

接続の受け入れ

通常はパフォーマンス向上のためマルチスレッドで行う。また、複数の接続を受け付けるためにはループを用いて接続要求をチェックする。

  1. クライアントからの接続受け入れ用ソケットの作成(手順2に統合してもよい)
    SOCKET ClientSocket;
    
  2. 接続を受信したら許可する(シングルスレッド)
    ClientSocket = INVALID_SOCKET;
    
    // Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    

サーバーでのデータの受信と送信

  • recv関数でデータを受信しデータを第2引数で指定したchar型のバッファーへ格納する。
    • エラーが発生していなければ戻り値は受信したバイト数を返す
    • 接続が正常に閉じられている場合は戻り値は0となる
  • send関数で第2引数で指定したchar型バッファーに格納されているデータを送信する。

下記コードは受信したデータをそのまま送り返すものである

// Since the maximum value of MTU is 1500 bytes
#define DEFAULT_BUFLEN 1500

char recvbuf[DEFAULT_BUFLEN];
int iResult, iSendResult;
int recvbuflen = DEFAULT_BUFLEN;

// Receive until the peer shuts down the connection
do {

    iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
    if (iResult > 0) {
        printf("Bytes received: %d\n", iResult);

        // Echo the buffer back to the sender
        iSendResult = send(ClientSocket, recvbuf, iResult, 0);
        if (iSendResult == SOCKET_ERROR) {
            printf("send failed: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }
        printf("Bytes sent: %d\n", iSendResult);
    } else if (iResult == 0)
        printf("Connection closing...\n");
    else {
        printf("recv failed: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }

} while (iResult > 0);

また、バッファをリセットしないと受信データが前回より少ない場合は前回のデータが残ってしまうため毎回リセットするのが推奨される。

memset(recvbuf, '\0', sizeof(recvbuf));

サーバーの切断

  • shutdown関数を用いてクライアントと接続されているソケットの送信側を終了させる
  • 送信側を終了すると引き続きデータを受信できるようになるため、ソケットの受信側も終了させる
// shutdown the send half of the connection since no more data will be sent
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
    printf("shutdown failed: %d\n", WSAGetLastError());
    closesocket(ClientSocket);
    WSACleanup();
    return 1;
}

// cleanup
closesocket(ClientSocket);
WSACleanup();

return 0;

関連記事

クライアントモデル

  1. Winsock を初期化します。
  2. ソケットを作成します。
  3. サーバーに接続します。
  4. データの送受信。
  5. 切断します。

Winsockの初期化

サーバー モデルの「Winsockの初期化」と同じであるため記載省略。詳細はサーバー モデルを参照すること。

ソケットの作成

  1. 構造体を準備し名前解決を行う

    • ZeroMemory関数を実行してaddrinfo構造体のインスタンス化により確保された領域をすべて0で埋める
      • これを実行しないとgetaddrinfo関数はエラーを起こしてしまうため絶対に実施すること
    • getaddrinfo関数の第1引数には名前解決したいホスト名を、第2引数にはサービス名もしくはポート番号を格納する
    struct addrinfo *result = NULL, *ptr = NULL, hints;
    
    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family   = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    
    // Resolve the server address and port
    iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }
    
  2. リッスン用ソケットオブジェクトの作成
    ソケット作成と統合してもよい

    SOCKET ConnectSocket = INVALID_SOCKET;
    
  3. ソケットの作成

    // Attempt to connect to the first address returned by
    // the call to getaddrinfo
    ptr=result;
    
    // Create a SOCKET for connecting to server
    ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
    

ソケットへの接続

  • connect関数を用いて、作成されたソケットとgetaddrinfo関数にてパラメーターが設定されたsockaddr構造体を渡してサーバーと接続する。
  • getaddrinfo関数によって返された情報は不要になるためfreeaddrinfo関数でメモリ領域を解放する。
// Connect to server.
iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
    closesocket(ConnectSocket);
    ConnectSocket = INVALID_SOCKET;
}

// Should really try the next address returned by getaddrinfo
// if the connect call failed
// But for this simple example we just free the resources
// returned by getaddrinfo and print an error message

freeaddrinfo(result);

if (ConnectSocket == INVALID_SOCKET) {
    printf("Unable to connect to server!\n");
    WSACleanup();
    return 1;
}

なお、別のアプリケーションがソケットを経由してやり取りを行う場合はWSAconnectを用いること。
socket関数のみで行おうとする場合、非常に実装が難しく改行の扱いなどもこちら側で実装する必要があるためである。

WSAConnect 関数は、別のソケット アプリケーションへの接続を確立し、接続データを交換し、指定された FLOWSPEC 構造体に基づいて必要なサービス品質を指定します。

*WSAConnect関数

クライアントでのデータの送受信

  • recv関数でデータを受信しデータを第2引数で指定したchar型のバッファーへ格納する。
    • エラーが発生していなければ戻り値は受信したバイト数を返す
    • 接続が正常に閉じられている場合は戻り値は0となる
  • send関数で第2引数で指定したchar型バッファーに格納されているデータを送信する。
#define DEFAULT_BUFLEN 512

int recvbuflen = DEFAULT_BUFLEN;

const char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];

int iResult;

// Send an initial buffer
iResult = send(ConnectSocket, sendbuf, (int) strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
    printf("send failed: %d\n", WSAGetLastError());
    closesocket(ConnectSocket);
    WSACleanup();
    return 1;
}

printf("Bytes Sent: %ld\n", iResult);

// shutdown the connection for sending since no more data will be sent
// the client can still use the ConnectSocket for receiving data
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
    printf("shutdown failed: %d\n", WSAGetLastError());
    closesocket(ConnectSocket);
    WSACleanup();
    return 1;
}

// Receive data until the server closes the connection
do {
    iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
    if (iResult > 0)
        printf("Bytes received: %d\n", iResult);
    else if (iResult == 0)
        printf("Connection closed\n");
    else
        printf("recv failed: %d\n", WSAGetLastError());
} while (iResult > 0);

また、バッファをリセットしないと受信データが前回より少ない場合は前回のデータが残ってしまうため毎回リセットするのが推奨される。

memset(recvbuf, '\0', sizeof(recvbuf));

クライアントの切断

  • shutdown関数を用いてサーバーと接続されているソケットの送信側を終了させる
  • 送信側を終了後、ソケットの受信側も終了させる
// shutdown the send half of the connection since no more data will be sent
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
    printf("shutdown failed: %d\n", WSAGetLastError());
    closesocket(ConnectSocket);
    WSACleanup();
    return 1;
}

// cleanup
closesocket(ConnectSocket);
WSACleanup();

return 0;

HTTP Server API(Version 2.0)

HTTP Server API を使用すると、アプリケーションは URL に送信された HTTP 要求を受信し、HTTP 応答を送信できます。
また、複数のアプリケーションを 1 つのシステムに共存させ、同じ TCP ポート (HTTP の場合はポート 80、HTTPS の場合はポート 443 など) を共有し、URL 名前空間のさまざまな部分に対応できます。

使用するライブラリやヘッダ

http.hヘッダーファイルをインクルードするとともに、httpapi.libをアプリケーションにリンクする必要がある。
また、winsock2.hWs2_32.libも必要なようなので注意が必要である。

以下のように宣言する。

#include <http.h>
#include <winsock2.h>
#include <stdlib.h> //mallocで利用
#pragma comment(lib, "httpapi.lib")
#pragma comment(lib, "Ws2_32.lib")

正式な宣言はこちら。こちらも動作する。

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <http.h>
#include <stdio.h>
#include <stdlib.h> //mallocで利用

#pragma comment(lib, "httpapi.lib")

HTTP Server タスク

通常、HTTP サーバー タスクは特定の順序で実行されます。
つまり、1 つのタスクを完了し、次のタスクが開始する前に出力を取得する必要があります。

  • HTTP サービスを初期化する
  • リッスンする URL を登録する
  • ルーチンを呼び出して要求を受信する
  • 要求を受信する
  • HTTP 要求を処理する
  • HTTP 応答を送信する
  • HTTP POST 応答を送信する
  • HTTP Server API のクリーンアップ

HTTP サービスを初期化する

HTTP サービスは HttpInitialize 関数を使用して初期化する。その次に要求キューを作成する。
なお、それぞれ開発者側で上記終了処理を実装する必要がある。

作成用 関数名 終了用 関数名
HttpInitialize HttpTerminate
HttpCreateRequestQueue HttpCloseRequestQueue
ULONG result;

// HTTP Server APIドライバーを初期化する(version 2.0指定)
result = HttpInitialize(HTTPAPI_VERSION_2, HTTP_INITIALIZE_SERVER, NULL);
if(result != NO_ERROR){
    // エラーハンドリング
}

/* 新しい要求キューの作成
* HttpCreateRequestQueueの第2引数にPCWSTR型で文字列を指定すると
* その名前で他プロセスからアクセスできるようになる
*/
HANDLE hRequestQueue = NULL;
result = HttpCreateRequestQueue(HTTPAPI_VERSION_2, NULL, NULL, 0, &hRequestQueue);
if(result != NO_ERROR){
    // エラーハンドリング
    HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
}

リッスンするURLを登録する

HTTP Server API version 2.0では以下の順に従い処理が必要となる

  1. サーバーのセッションを作成する(HttpCreateServerSession関数)
  2. 作成されたサーバーセッションの下にURLグループを作成する(HttpCreateUrlGroup関数)
  3. 指定したURLをURLグループに追加する(HttpAddUrlToUrlGroup関数)
  4. URLグループと要求キューを関連付ける(HttpSetUrlGroupProperty関数)

なお、それぞれ開発者側で上記終了処理を実装する必要がある。

作成用 関数名 終了用 関数名
HttpCreateServerSession HttpCloseServerSession
HttpCreateUrlGroup HttpCloseUrlGroup
HttpAddUrlToUrlGroup (終了ではなく除外用)
HttpRemoveUrlFromUrlGroup
HttpSetUrlGroupProperty (なし)
// 以下が定義されている前提
// -> ULONG result;

// 1. サーバーのセッションを作成
HTTP_SERVER_SESSION_ID serverSessionId = 0;

result = HttpCreateServerSession(HTTPAPI_VERSION_2, &serverSessionId, 0);
if(result != NO_ERROR){
    // エラーハンドリング
    HttpCloseRequestQueue(hRequestQueue);
    HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
}

// 2. URLグループを作成
HTTP_URL_GROUP_ID urlGroupId = 0;

result = HttpCreateUrlGroup(serverSessionId, &urlGroupId, 0);
if(result != NO_ERROR){
    // エラーハンドリング
    HttpCloseRequestQueue(hRequestQueue);
    HttpCloseServerSession(serverSessionId);
    HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
}

// 3. URLグループに追加
PCWSTR URL = L"http://localhost:8080/";
result = HttpAddUrlToUrlGroup(urlGroupId, URL, 0, 0);

if(result != NO_ERROR){
    // エラーハンドリング
    HttpCloseUrlGroup(urlGroupId);
    HttpCloseRequestQueue(hRequestQueue);
    HttpCloseServerSession(serverSessionId);
    HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
}

// 4. URLグループと要求キューを関連付ける
HTTP_BINDING_INFO bindingInfo;
bindingInfo.Flags.Present = 1;
bindingInfo.RequestQueueHandle = hRequestQueue;

result = HttpSetUrlGroupProperty(urlGroupId, HttpServerBindingProperty, &bindingInfo, sizeof(HTTP_BINDING_INFO));
if(result != NO_ERROR){
    // エラーハンドリング
    HttpCloseUrlGroup(urlGroupId);
    HttpCloseRequestQueue(hRequestQueue);
    HttpCloseServerSession(serverSessionId);
    HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
}

要求を処理する

要求の処理には4つの手順がある。

  1. 要求の受信(HttpReceiveHttpRequest関数)
  2. 要求の処理(response構造体の操作)
  3. 応答の送信(HttpSendHttpResponse関数)
  4. 処理できない要求の取り消し

なお、継続的に要求を受け付ける場合は、下記コードの1~4の部分をループさせること。

// 以下が定義されている前提
// -> ULONG result;

// 変数の初期化
HTTP_REQUEST *request = NULL;
HTTP_RESPONSE response;
DWORD bytesReceived, bytesSent;

// HTTP_REQUEST 構造体のバイトサイズ+2048バイト分のメモリを確保し
// HTTP_REQUEST型にキャストしたのちポインタ変数へ先頭のメモリアドレスを渡す
request = (HTTP_REQUEST*)malloc(sizeof(HTTP_REQUEST)+2048);
if (request == NULL) {
    // エラーハンドリング
    HttpCloseUrlGroup(urlGroupId);
    HttpCloseRequestQueue(hRequestQueue);
    HttpCloseServerSession(serverSessionId);
    HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
}

/* 1. 要求受信部分 */
// 先ほど確保した受信データ保存用メモリ領域をすべてゼロで埋める
RtlZeroMemory(request, sizeof(HTTP_REQUEST)+2048);

// 要求を受信する
result = HttpReceiveHttpRequest(hRequestQueue, HTTP_NULL_ID, 0, request, sizeof(HTTP_REQUEST) + 2048, &bytesReceived, NULL);
if(result != NO_ERROR){
    // エラーハンドリング
    HttpCloseUrlGroup(urlGroupId);
    HttpCloseRequestQueue(hRequestQueue);
    HttpCloseServerSession(serverSessionId);
    HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
}

/* 2. 要求の処理 */
// 応答データ保存用メモリ領域をすべてゼロで埋める
RtlZeroMemory(&response, sizeof(HTTP_RESPONSE));

// 応答データを組み立てる
char responseData[] = "Hello, World!";
response.StatusCode = 200;
response.pReason = "OK";
response.ReasonLength = 2;
response.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = "text/plain";
response.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = 10;
response.EntityChunkCount = 1;
response.pEntityChunks = (PHTTP_DATA_CHUNK)malloc(sizeof(HTTP_DATA_CHUNK));
response.pEntityChunks[0].DataChunkType = HttpDataChunkFromMemory;
response.pEntityChunks[0].FromMemory.pBuffer = responseData;
response.pEntityChunks[0].FromMemory.BufferLength = (ULONG)strlen(responseData);

/* 3. 応答の送信 */
result = HttpSendHttpResponse(hRequestQueue, request->RequestId, 0, &response, NULL, &bytesSent, NULL, 0, NULL, NULL);
if(result != NO_ERROR){
 // エラーハンドリング
}

// 次の処理を行えるように解放する
free(response.pEntityChunks);

/* 4. 要求の取り消し */
// 必要に応じて実装
//result = HttpCancelHttpRequest(hRequestQueue, request->RequestId, NULL);
//if(result != NO_ERROR){
// エラーハンドリング
//}

クリーンアップする

アプリケーションを動作させるために作成した各種オブジェクトを明示的に終了させる。

free(request);
HttpCloseUrlGroup(urlGroupId);
HttpCloseRequestQueue(hRequestQueue);
HttpCloseServerSession(serverSessionId);
HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?