見出し画像

#156 Anonymous Pipes

 Windowsには、プロセス間でやりとりをするための仕組みが用意されていて、パイプと呼ばれています。

パイプには、匿名パイプと名前付きパイプの2種類があり、用途に応じて使い分けます。

匿名パイプ
親子関係のあるプロセス間などで使われる、1方向のパイプ

名前付きパイプ
パイプサーバーと複数のパイプクライアントでの通信に使われる1方向または双方向のパイプ


匿名パイプ

 匿名パイプを使って、プロセス間通信をしてみたいと思います。
親プロセスから「cmd.exe」の子プロセスを生成して、子プロセスの標準入力・標準出力を親プロセスと連携させます。

コード


#include <Windows.h>
#include <iostream>


void ReadFromPipe(HANDLE StdOutRead)
{
    DWORD dwRead, dwWritten;
    CHAR chBuf[1024];
    BOOL bSuccess = FALSE;
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    for (;;)
    {
        bSuccess = ReadFile(StdOutRead, chBuf, 1024, &dwRead, NULL);
        if (!bSuccess || dwRead == 0) break;

        bSuccess = WriteFile(hParentStdOut, chBuf,
            dwRead, &dwWritten, NULL);
        if (!bSuccess) break;
    }
}

void WriteToPipe(HANDLE StdInWrite)
{
    DWORD dwRead, dwWritten;
    CHAR chBuf[1024];
    BOOL bSuccess = FALSE;
    HANDLE hParentStdIn = GetStdHandle(STD_INPUT_HANDLE);
    for (;;)
    {
        bSuccess = ReadFile(hParentStdIn, chBuf, 1024, &dwRead, NULL);
        if (!bSuccess || dwRead == 0) break;

        bSuccess = WriteFile(StdInWrite, chBuf, dwRead, &dwWritten, NULL);
        if (!bSuccess) break;
    }

    if (!CloseHandle(StdInWrite)) {
        printf("[!] CloseHandle failed with error: %d\n", GetLastError());
    }
}

int main()
{
    HANDLE StdInRead = NULL;
    HANDLE StdInWrite = NULL;
    HANDLE StdOutRead = NULL;
    HANDLE StdOutWrite = NULL;
    HANDLE hProcess = NULL;
    PROCESS_INFORMATION pi = { 0 };
    STARTUPINFO si = { 0 };
    SECURITY_ATTRIBUTES	sa = { 0 };
    wchar_t lpCmd[] = L"cmd.exe";

    RtlZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    RtlZeroMemory(&si, sizeof(STARTUPINFO));
    RtlZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));

    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;

    if (!CreatePipe(&StdInRead, &StdInWrite, &sa, 0)) {
        printf("[!] CreatePipe(1) failed with error: %d\n", GetLastError());
        return FALSE;
    }

    if (!CreatePipe(&StdOutRead, &StdOutWrite, &sa, 0)) {
        printf("[!] CreatePipe(2) failed with error: %d\n", GetLastError());
        return FALSE;
    }
    if (!SetHandleInformation(StdInWrite, HANDLE_FLAG_INHERIT, 0)) {
        printf("[!] SetHandleInformation(1) failed with error: %d\n", GetLastError());
        return -1;
    }
    if (!SetHandleInformation(StdOutRead, HANDLE_FLAG_INHERIT, 0)) {
        printf("[!] SetHandleInformation(2) failed with error: %d\n", GetLastError());
        return -1;
    }

    
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags |= (STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES);
    si.wShowWindow = SW_HIDE;
    si.hStdInput = StdInRead;
    si.hStdOutput = si.hStdError = StdOutWrite;

    if (!CreateProcessW(NULL, lpCmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
        return -1;
    }

    hProcess = pi.hProcess;
    CloseHandle(StdInRead);
    CloseHandle(StdOutWrite);

    HANDLE hOutThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ReadFromPipe, StdOutRead, NULL, NULL);
    HANDLE hInThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)WriteToPipe, StdInWrite, NULL, NULL);
    WaitForMultipleObjects(1, &hOutThread, TRUE, INFINITE);
    WaitForMultipleObjects(1, &hInThread, TRUE, INFINITE);
}

Visual Studioでビルドして実行すると、コマンドプロンプトが立ち上がります。コンソールの標準入力・標準出力を子プロセスの「cmd.exe」と接続しているため、普通の「cmd.exe」と同じように扱うことができます。

まあ、これだけではそんなに役に立つものではありませんが…
アイデア次第でおもしろいことができそうです。


EOF

いいなと思ったら応援しよう!