プロセス操作
プロセスの作成
CreateProcess
関数を用いて新しいプロセスとプライマリスレッドを作成します。
なお、作成されるプロセスのウインドウに関する指定する情報はSTARTUPINFO
構造体に設定します。
また、新しいプロセスは呼び出し元プロセスのセキュリティ コンテキストで実行されます。
Windowsはプロセスを作成するとスレッドが必ず1つ作成されます。
関数が成功すると、戻り値は0以外になります。関数が失敗した場合は、0を返します。
また、起動したプロセスに関する情報はPROCESS_INFORMATION
構造体に格納されます。
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
);
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;
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
なおプロセス作成後、プロセスへアクセス不要になった時点でハンドルをクローズする必要があります。
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
サンプルプログラム
#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です。
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);
プロセスの強制終了
TerminalteProcess
関数を用いて、指定したプロセスとそのすべてのスレッドを強制終了
します。なお、ハンドルはPROCESS_TERMINATE
のアクセス権限を有している必要があります。
関数が成功すると、戻り値は0以外になります。関数が失敗した場合は、0を返します。
BOOL TerminateProcess(
[in] HANDLE hProcess,
[in] UINT uExitCode
);
サンプルプログラム
プログラムの流れとしては以下のとおりである。なお、プログラム起動時には第1引数として終了させたいプロセスのPIDを指定する。
# 1756はユーザー権限で動作しているnotepad.exe
>example_TerminateProcess.exe 1756
Exit Code:259
Success to terminate process
- 終了させるプロセスのハンドルを取得する
- GetExitCodeProcess関数を使用して終了コードを取得する
- TerminateProcess関数で終了する
#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
を活用し、プロトコルスタックと通信する。
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 マクロを付ける必要があります。
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
サーバー モデル
一般的なモデルは以下の通り
- Winsock を初期化します。
- ソケットを作成します。
- ソケットをバインドします。
- クライアントのソケットでリッスンします。
- クライアントからの接続を受け入れます。
- データの受信と送信。
- 切断します。
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;
}
ソケットの作成
-
構造体を準備し名前解決を行う
-
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; }
-
-
リッスン用ソケットオブジェクトの作成
ソケット作成と統合してもよいSOCKET ListenSocket = INVALID_SOCKET;
-
ソケットの作成
// 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;
}
接続の受け入れ
通常はパフォーマンス向上のためマルチスレッドで行う。また、複数の接続を受け付けるためにはループを用いて接続要求をチェックする。
- クライアントからの接続受け入れ用ソケットの作成(手順2に統合してもよい)
SOCKET ClientSocket;
- 接続を受信したら許可する(シングルスレッド)
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;
関連記事
クライアントモデル
- Winsock を初期化します。
- ソケットを作成します。
- サーバーに接続します。
- データの送受信。
- 切断します。
Winsockの初期化
サーバー モデルの「Winsockの初期化」と同じであるため記載省略。詳細はサーバー モデルを参照すること。
ソケットの作成
-
構造体を準備し名前解決を行う
-
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; }
-
-
リッスン用ソケットオブジェクトの作成
ソケット作成と統合してもよいSOCKET ConnectSocket = INVALID_SOCKET;
-
ソケットの作成
// 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 構造体に基づいて必要なサービス品質を指定します。
クライアントでのデータの送受信
-
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.h
やWs2_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では以下の順に従い処理が必要となる
- サーバーのセッションを作成する(
HttpCreateServerSession
関数) - 作成されたサーバーセッションの下にURLグループを作成する(
HttpCreateUrlGroup
関数) - 指定したURLをURLグループに追加する(
HttpAddUrlToUrlGroup
関数) - 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つの手順がある。
- 要求の受信(
HttpReceiveHttpRequest
関数) - 要求の処理(
response
構造体の操作) - 応答の送信(
HttpSendHttpResponse
関数) - 処理できない要求の取り消し
なお、継続的に要求を受け付ける場合は、下記コードの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);