Quick Guide（Win32API）

# プロセス操作

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

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


```c++: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
);
```

```c++: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;
```

```C:PROCESS_INFORMATION構造体
typedef struct _PROCESS_INFORMATION {
  HANDLE hProcess;
  HANDLE hThread;
  DWORD  dwProcessId;
  DWORD  dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
```

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

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

> * [CreateProcess関数](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa)
> * [プロセスとスレッドの概要](https://learn.microsoft.com/ja-jp/windows/win32/procthread/about-processes-and-threads)
> * [PROCESS_INFORMATION 構造体](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_information)
> * [プロセスを作成する](https://learn.microsoft.com/ja-jp/windows/win32/procthread/creating-processes)

### サンプルプログラム
```c++: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です。

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

> * [OpenProcess関数](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess)

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

```c++:TerminateProcess関数
BOOL TerminateProcess(
    [in] HANDLE hProcess,
    [in] UINT   uExitCode
);
```

> * [TerminateProcess関数](https://learn.microsoft.com/ja-jp/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess)

### サンプルプログラム
プログラムの流れとしては以下のとおりである。なお、プログラム起動時には第1引数として終了させたいプロセスのPIDを指定する。
```shell
# 1756はユーザー権限で動作しているnotepad.exe
>example_TerminateProcess.exe 1756
Exit Code：259
Success to terminate process
```

1. 終了させるプロセスのハンドルを取得する
2. GetExitCodeProcess関数を使用して終了コードを取得する
3. TerminateProcess関数で終了する

```c++: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のアーキテクチャ](https://learn.microsoft.com/ja-jp/windows/win32/winsock/windows-sockets-2-architecture-2) 

## winsockの使い方
基本的には`winsock2.h`と`ws2tcpip.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 マクロを付ける必要があります。

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

#pragma comment(lib, "Ws2_32.lib")
```
> * [Winsockの概要](https://learn.microsoft.com/ja-jp/windows/win32/winsock/getting-started-with-winsock)
> * [基本的な Winsock アプリケーションの作成](https://learn.microsoft.com/ja-jp/windows/win32/winsock/creating-a-basic-winsock-application)

## サーバー モデル
一般的なモデルは以下の通り
1. Winsock を初期化します。
1. ソケットを作成します。
1. ソケットをバインドします。
1. クライアントのソケットでリッスンします。
1. クライアントからの接続を受け入れます。
1. データの受信と送信。
1. 切断します。

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

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

```c++
WSADATA wsaData;

int iResult;

// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
    printf("WSAStartup failed: %d\n", iResult);
    return 1;
}
```
> * [Winsockの初期化](https://learn.microsoft.com/ja-jp/windows/win32/winsock/initializing-winsock)
> * [WSADATA 構造体](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock/ns-winsock-wsadata)
> * [WSAStartup関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock/nf-winsock-wsastartup)


### ソケットの作成
1. 構造体を準備し名前解決を行う  
    * `ZeroMemory`関数を実行してaddrinfo構造体のインスタンス化により確保された領域をすべて0で埋める
        * **これを実行しないと`getaddrinfo`関数はエラーを起こしてしまうため絶対に実施すること**
    * `getaddrinfo`関数の第1引数には名前解決したいホスト名を、第2引数にはサービス名もしくはポート番号を格納する
    ```c++
    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;
    }
    ```
    > * [addrinfo構造体](https://learn.microsoft.com/ja-jp/windows/win32/api/ws2def/ns-ws2def-addrinfoa)
    > * [getaddrinfo関数](https://learn.microsoft.com/ja-jp/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo)

2. リッスン用ソケットオブジェクトの作成  
    ソケット作成と統合してもよい
    ```c
    SOCKET ListenSocket = INVALID_SOCKET;
    ```
3. ソケットの作成
    ```c++
    // Create a SOCKET for the server to listen for client connections
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    ```
    > * [socket関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-socket)

> [サーバーのソケットの作成](https://learn.microsoft.com/ja-jp/windows/win32/winsock/creating-a-socket-for-the-server)

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

```c++
// 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;
}
```
> * [ソケットのバインド](https://learn.microsoft.com/ja-jp/windows/win32/winsock/binding-a-socket)
> * [freeaddrinfo関数](https://learn.microsoft.com/ja-jp/windows/win32/api/ws2tcpip/nf-ws2tcpip-freeaddrinfo)

### ソケットでのリッスン
* Listen関数を用いてソケットをリッスン状態にする。
* Listen関数の第2引数は保留中の接続のキューの最大長を示している。`SOMAXCONN`を指定すると最大の妥当な値に設定してくれる。

```c++
if ( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR ) {
    printf( "Listen failed with error: %ld\n", WSAGetLastError() );
    closesocket(ListenSocket);
    WSACleanup();
    return 1;
}
```
> [listen関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-listen)


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

1. クライアントからの接続受け入れ用ソケットの作成（手順2に統合してもよい）
    ```c
    SOCKET ClientSocket;
    ```
2. 接続を受信したら許可する（シングルスレッド）
    ```c++
    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;
    }
    ```
> * [接続の受け入れ](https://learn.microsoft.com/ja-jp/windows/win32/winsock/accepting-a-connection)


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

下記コードは受信したデータをそのまま送り返すものである
```c++
// 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);
```

また、バッファをリセットしないと受信データが前回より少ない場合は前回のデータが残ってしまうため毎回リセットするのが推奨される。
```c
memset(recvbuf, '\0', sizeof(recvbuf));
```

> * [サーバーでのデータの受信と送信](https://learn.microsoft.com/ja-jp/windows/win32/winsock/receiving-and-sending-data-on-the-server)
> * [recv関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock/nf-winsock-recv)
> * [send関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-send)
> * [memset関数](https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/reference/memset-wmemset?view=msvc-170)
> * [NULLと'\0'の違い](http://www.kis-lab.com/serikashiki/C/C01.html)

### サーバーの切断
* `shutdown`関数を用いてクライアントと接続されている**ソケットの送信側**を終了させる
* 送信側を終了すると引き続きデータを受信できるようになるため、ソケットの受信側も終了させる

```c++
// 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;
```

> * [サーバーの切断](https://learn.microsoft.com/ja-jp/windows/win32/winsock/disconnecting-the-server)
> * [shutdown関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock/nf-winsock-shutdown)

### 関連記事
* [Windows SocketsでTCP通信とUDP通信を実装したプログラムをリバーシングしてみる](https://kashiwaba-yuki.com/windows-windbg-010-socket)

## クライアントモデル
1. Winsock を初期化します。
1. ソケットを作成します。
1. サーバーに接続します。
1. データの送受信。
1. 切断します。

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

### ソケットの作成
1. 構造体を準備し名前解決を行う  
    * `ZeroMemory`関数を実行してaddrinfo構造体のインスタンス化により確保された領域をすべて0で埋める
        * **これを実行しないと`getaddrinfo`関数はエラーを起こしてしまうため絶対に実施すること**
    * `getaddrinfo`関数の第1引数には名前解決したいホスト名を、第2引数にはサービス名もしくはポート番号を格納する
    ```c++
    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;
    }
    ```
    > * [addrinfo構造体](https://learn.microsoft.com/ja-jp/windows/win32/api/ws2def/ns-ws2def-addrinfoa)
    > * [getaddrinfo関数](https://learn.microsoft.com/ja-jp/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo)

2. リッスン用ソケットオブジェクトの作成  
    ソケット作成と統合してもよい
    ```c++
    SOCKET ConnectSocket = INVALID_SOCKET;
    ```
3. ソケットの作成
    ```c++
    // 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);
    ```
    > * [socket関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-socket)

> * [クライアントのソケットの作成](https://learn.microsoft.com/ja-jp/windows/win32/winsock/creating-a-socket-for-the-client)

### ソケットへの接続
* `connect`関数を用いて、作成されたソケットと`getaddrinfo`関数にてパラメーターが設定された`sockaddr`構造体を渡してサーバーと接続する。
* getaddrinfo関数によって返された情報は不要になるため`freeaddrinfo`関数でメモリ領域を解放する。

```c++
// 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;
}
```

> * [ソケットへの接続](https://learn.microsoft.com/ja-jp/windows/win32/winsock/connecting-to-a-socket)
> * [connect関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-connect)

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

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

> *[WSAConnect関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-wsaconnect)

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

```c++
#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);
```

また、バッファをリセットしないと受信データが前回より少ない場合は前回のデータが残ってしまうため毎回リセットするのが推奨される。
```c
memset(recvbuf, '\0', sizeof(recvbuf));
```

> * [クライアントでのデータの送受信](https://learn.microsoft.com/ja-jp/windows/win32/winsock/sending-and-receiving-data-on-the-client)
> * [recv関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock/nf-winsock-recv)
> * [send関数](https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-send)
> * [memset関数](https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/reference/memset-wmemset?view=msvc-170)
> * [NULLと'\0'の違い](http://www.kis-lab.com/serikashiki/C/C01.html)

### クライアントの切断
* `shutdown`関数を用いてサーバーと接続されている**ソケットの送信側**を終了させる
* 送信側を終了後、ソケットの受信側も終了させる
```c++
// 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;
```
> * [クライアントの切断](https://learn.microsoft.com/ja-jp/windows/win32/winsock/disconnecting-the-client)


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

> * [HTTP Server API](https://learn.microsoft.com/ja-jp/windows/win32/http/http-api-start-page)
> * [HTTP Server APIについて](https://learn.microsoft.com/ja-jp/windows/win32/http/about-http-server-api)

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

以下のように宣言する。
```c++
#include <http.h>
#include <winsock2.h>
#include <stdlib.h> //mallocで利用
#pragma comment(lib, "httpapi.lib")
#pragma comment(lib, "Ws2_32.lib")
```

正式な宣言はこちら。こちらも動作する。
```c++
#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|

```c++
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);
}
```

> * [HTTP サービスを初期化する](https://learn.microsoft.com/ja-jp/windows/win32/http/http-server-tasks#initialize-the-http-service)
> * [HttpInitialize 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpinitialize)
> * [HttpTerminate 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpterminate)
> * [HttpCreateRequestQueue 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpcreaterequestqueue)
> * [HttpCloseRequestQueue 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpcloserequestqueue)

## リッスンするURLを登録する
HTTP Server API version 2.0では以下の順に従い処理が必要となる
1. サーバーのセッションを作成する（`HttpCreateServerSession`関数）
2. 作成されたサーバーセッションの下にURLグループを作成する（`HttpCreateUrlGroup`関数）
3. 指定したURLをURLグループに追加する（`HttpAddUrlToUrlGroup`関数）
4. URLグループと要求キューを関連付ける（`HttpSetUrlGroupProperty`関数）

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

|作成用 関数名|終了用 関数名|
|---|---|
|HttpCreateServerSession|HttpCloseServerSession|
|HttpCreateUrlGroup|HttpCloseUrlGroup|
|HttpAddUrlToUrlGroup|（終了ではなく除外用）<br>HttpRemoveUrlFromUrlGroup|
|HttpSetUrlGroupProperty|（なし）|

```c++
// 以下が定義されている前提
// -> 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);
}
```

> * [リッスンする URL を登録する](https://learn.microsoft.com/ja-jp/windows/win32/http/http-server-tasks#register-the-urls-to-listen-on)
> * [HttpCreateServerSession 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpcreateserversession)
> * [HttpCloseServerSession 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpcloseserversession)
> * [HttpCreateUrlGroup 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpcreateurlgroup)
> * [HttpCloseUrlGroup 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpcloseurlgroup)
> * [HttpAddUrlToUrlGroup 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpaddurltourlgroup)
> * [HttpRemoveUrlFromUrlGroup 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpremoveurlfromurlgroup)
> * [HttpSetUrlGroupProperty 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpseturlgroupproperty)
> * [HTTP_BINDING_INFO構造体](https://learn.microsoft.com/ja-jp/windows/win32/api/http/ns-http-http_binding_info)

## 要求を処理する
要求の処理には4つの手順がある。
1. 要求の受信（`HttpReceiveHttpRequest`関数）
2. 要求の処理（`response`構造体の操作）
3. 応答の送信（`HttpSendHttpResponse`関数）
4. 処理できない要求の取り消し

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

```c++
// 以下が定義されている前提
// -> 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){
// エラーハンドリング
//}
```

> * [要求の処理](https://learn.microsoft.com/ja-jp/windows/win32/http/processing-requests)
> * [HttpReceiveHttpRequest 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpreceivehttprequest)
> * [response 構造体](https://learn.microsoft.com/ja-jp/windows/win32/http/http-response)
> * [HttpSendHttpResponse 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpsendhttpresponse)

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

```c++
free(request);
HttpCloseUrlGroup(urlGroupId);
HttpCloseRequestQueue(hRequestQueue);
HttpCloseServerSession(serverSessionId);
HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
```

> * [HttpCloseUrlGroup 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpcloseurlgroup)
> * [HttpCloseRequestQueue 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpcloserequestqueue)
> * [HttpCloseServerSession 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpcloseserversession)
> * [HttpTerminate 関数](https://learn.microsoft.com/ja-jp/windows/win32/api/http/nf-http-httpterminate)