重定向子进程的输入和输出不起作用

Redirecting Input and Output of child process not working

本文关键字:输出 不起作用 输入 子进程 重定向      更新时间:2023-10-16

我已经编写了两个c++应用程序,一个是控制台,另一个是gui。我希望gui应用程序启动控制台应用程序,并在其上插入输入,重定向输出和错误,并在文本框中显示它们。

按下运行按钮后,我的代码在gui应用程序中不起作用,也没有显示输出,而且gui应用程序似乎冻结了!

这是我的代码:

[控制台部分]:

#include <iostream>
using namespace std;
int main()
{
int pass;
//cout << "welcome message from cpp app ..." << endl;
cout << "please enter pass => ";
cin >> pass;
if (pass==12345)
    cout << "ok!" << endl;
else
    cout << "oops!" << endl;
//system("pause");
return 0;
}

[gui部分]:

#include <windows.h>
#include <CommCtrl.h>
#define TEXTBOX1 100
#define BUTTON1 101
HWND htbx = nullptr;
HANDLE hChildStd_IN_Rd = nullptr;
HANDLE hChildStd_IN_Wr = nullptr;
HANDLE hChildStd_OUT_Rd = nullptr;
HANDLE hChildStd_OUT_Wr = nullptr;
int MessageLoop();
void ShowError()
{
    LPVOID lpMsgBuf;
    DWORD dw = GetLastError();
    DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS;
    FormatMessage(flags, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), LPWSTR(&lpMsgBuf), 0, nullptr);
    MessageBox(nullptr, LPCWSTR(lpMsgBuf), L"error", MB_OK);
    LocalFree(lpMsgBuf);
}
void WriteToPipe(LPCWSTR Commands2Write) 
{
    DWORD dwWritten;
    bool bSuccess = false;
    bSuccess = WriteFile(hChildStd_IN_Wr, Commands2Write, sizeof(Commands2Write), &dwWritten, nullptr);
    if (!bSuccess)
        ShowError();
    if (!CloseHandle(hChildStd_IN_Wr))
        ShowError();
}
void ReadFromPipe()
{
    DWORD dwRead;
    LPVOID lpDataBuf[4096];
    if (hChildStd_OUT_Rd==INVALID_HANDLE_VALUE)
    {
        MessageBox(0, L"INVALID HANDLE", L"error", MB_OK);
    }
    //bool bSuccess = false;
    /*bSuccess = ReadFile(hChildStd_OUT_Rd, lpDataBuf, 4096, &dwRead, nullptr);
    if (!bSuccess)
        ShowError();*/
    while (ReadFile(hChildStd_OUT_Rd, lpDataBuf, sizeof(lpDataBuf), &dwRead, nullptr))
    {
       SendMessage(htbx, WM_SETTEXT, 0, LPARAM(LPCWSTR(lpDataBuf)));
      // MessageBox(nullptr, LPCWSTR(lpDataBuf), L"Result", MB_OK);
    }
    LocalFree(lpDataBuf);
}
void Run()
{
    bool bSuccess = false;
    PROCESS_INFORMATION PI;
    ZeroMemory(&PI, sizeof(PI));
    STARTUPINFO SI;
    ZeroMemory(&SI, sizeof(SI));
    SI.cb = sizeof(SI);
    SI.dwFlags =  STARTF_USESTDHANDLES;
    //SI.hStdError = hChildStd_OUT_Wr;
    //SI.hStdOutput = hChildStd_OUT_Wr;
    //SI.hStdInput = hChildStd_IN_Rd;
    SI.hStdError = hChildStd_OUT_Rd;
    SI.hStdOutput = hChildStd_OUT_Rd;
    SI.hStdInput = hChildStd_IN_Wr;
    SECURITY_ATTRIBUTES SA;
    SA.nLength = sizeof(SECURITY_ATTRIBUTES);
    SA.bInheritHandle = TRUE;
    SA.lpSecurityDescriptor = nullptr;
    if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &SA, 0))
        ShowError();
    if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
        ShowError();
    if (!CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &SA, 0))
        ShowError();
    if (!SetHandleInformation(hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
        ShowError();
    bSuccess = CreateProcess(L"C:\Windows\system32\cmd.exe",
        L" /c C:\testa.exe", nullptr, nullptr, TRUE, 0, nullptr, nullptr, &SI, &PI);
    if (!bSuccess){ ShowError(); }
            else{CloseHandle(PI.hProcess);CloseHandle(PI.hThread);}
        WriteToPipe(L"12345");
        ReadFromPipe();
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch (Msg)
    {
    case WM_CREATE:
    {
        CreateWindowEx(0, WC_BUTTON, L"Run", WS_VISIBLE | WS_CHILD, 420, 100, 75, 35, hWnd, HMENU(BUTTON1), nullptr, nullptr);
        htbx=CreateWindowEx(0, WC_EDIT, L"", WS_VISIBLE | WS_CHILD | WS_BORDER| ES_MULTILINE, 10, 10, 400, 300, hWnd, HMENU(TEXTBOX1), nullptr, nullptr);
    }
    break;
    case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
            case BUTTON1:
                Run();
                break;
            default:
                break;
            }
        }
        break;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreviewInstance, LPSTR lpcmdline, int ncmdshow)
{
    WNDCLASSEX wndexcls;
    wndexcls.lpszClassName = L"win";
    wndexcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndexcls.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wndexcls.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndexcls.hbrBackground = (HBRUSH)(COLOR_3DSHADOW + 1);
    wndexcls.lpszMenuName = NULL;
    wndexcls.style = NULL;
    wndexcls.hInstance = hInstance;
    wndexcls.cbSize = sizeof(WNDCLASSEX);
    wndexcls.cbClsExtra = 0;
    wndexcls.cbWndExtra = 0;
    wndexcls.lpfnWndProc = WndProc;
    RegisterClassEx(&wndexcls);
    HWND Win_Handle = CreateWindowEx(0, L"win", L"TestApp", WS_OVERLAPPEDWINDOW, 100, 100, 640, 380, 0, 0, hInstance, 0);
    ShowWindow(Win_Handle, SW_SHOWDEFAULT);
    UpdateWindow(Win_Handle);
    int exitcode;
    exitcode = MessageLoop();
    return exitcode;
}
int MessageLoop()
{
    MSG wnd_msg;
    while (GetMessage(&wnd_msg, NULL, 0, 0)>0)
    {
        TranslateMessage(&wnd_msg);
        DispatchMessage(&wnd_msg);
    }
    return (int)wnd_msg.wParam;
}

更新:

我根据Barmak Shemirani的回答更新了代码,并使用了ANSI字符集。但是writetopipe部分不起作用,在readfrompipe部分,如果在CreateProcessA中使用"/c dir"作为第二个参数,而不是"/c c:\consoleap.exe",则它只显示输出的第一行,而不是总输出。为什么会发生这种情况?

[更新代码]:

#include <windows.h>
#include <CommCtrl.h>
#define TEXTBOX1 100
#define BUTTON1 101
HWND htbx = nullptr;
HANDLE out_read = NULL;
HANDLE out_write = NULL;
HANDLE in_read = NULL;
HANDLE in_write = NULL;
int MessageLoop();
void ShowError()
{
    LPVOID lpMsgBuf;
    DWORD dw = GetLastError();
    DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS;
    FormatMessageA(flags, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), LPSTR(&lpMsgBuf), 0, nullptr);
    MessageBoxA(nullptr, LPCSTR(lpMsgBuf), "error", MB_OK);
    LocalFree(lpMsgBuf);
}
void WriteToPipe() 
{
    DWORD writecount;
    char bufw[1024]; 
    //memset(bufw, 12345, 5);
    strncpy_s(bufw, "12345", sizeof(bufw));
    if (WriteFile(in_write, bufw, sizeof(bufw), &writecount, NULL))
    {
        if (!writecount)
        {
            ShowError();
        }
    }
    if (!CloseHandle(in_write))
        ShowError();
    LocalFree(bufw);
}
void ReadFromPipe()
{
    DWORD readCount;
    char bufr[1024];
    memset(bufr, 0, sizeof(bufr));
    if (ReadFile(out_read, bufr, sizeof(bufr), &readCount, NULL))
    {
        if (readCount)
        {
            SendMessageA(htbx, EM_SETSEL, WPARAM(-1), LPARAM(-1));
            SendMessageA(htbx, EM_REPLACESEL, 0, LPARAM(bufr));
        }
    }
    if (!CloseHandle(out_read))
        ShowError();
    LocalFree(bufr);
}
DWORD WINAPI run(LPVOID)
{
    PROCESS_INFORMATION pi{};
    STARTUPINFOA si{ sizeof STARTUPINFO };
    si.dwFlags = STARTF_USESTDHANDLES;
    { //set handles
        SECURITY_ATTRIBUTES secAttr;
        secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
        secAttr.bInheritHandle = TRUE;
        secAttr.lpSecurityDescriptor = nullptr;
        if (!CreatePipe(&out_read, &out_write, &secAttr, 0)) ShowError();
        if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) ShowError();
        if (!CreatePipe(&in_read, &in_write, &secAttr, 0)) ShowError();
        if (!SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0)) ShowError();
        si.hStdOutput = out_write;
        si.hStdError = out_write;
        si.hStdInput = in_read;
        si.dwFlags |= STARTF_USESTDHANDLES;
    }
    if (!CreateProcessA("C:\Windows\system32\cmd.exe",
        "/c C:\consoleapp.exe", 0, 0, TRUE, 0, 0, 0, &si, &pi))
        ShowError();
    while (pi.hProcess && WaitForSingleObject(pi.hProcess, 25) == WAIT_TIMEOUT)
    {
        if (out_read == INVALID_HANDLE_VALUE)
            ShowError();
        if (in_write == INVALID_HANDLE_VALUE)
            ShowError();
        WriteToPipe();
        ReadFromPipe();
    }
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch (Msg)
    {
    case WM_CREATE:
    {
        CreateWindowExA(0, WC_BUTTONA, "Run", WS_VISIBLE | WS_CHILD, 420, 100, 75, 35, hWnd, HMENU(BUTTON1), nullptr, nullptr);
        htbx=CreateWindowExA(0, WC_EDITA, "", WS_VISIBLE | WS_CHILD | WS_BORDER| ES_MULTILINE, 10, 10, 400, 300, hWnd, HMENU(TEXTBOX1), nullptr, nullptr);
    }
    break;
    case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
            case BUTTON1:
                //Run();
                CreateThread(NULL, 0, run, 0, 0, NULL);
                break;
            default:
                break;
            }
        }
        break;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreviewInstance, LPSTR lpcmdline, int ncmdshow)
{
    WNDCLASSEXA wndexcls;
    wndexcls.lpszClassName = "win";
    wndexcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndexcls.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wndexcls.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndexcls.hbrBackground = (HBRUSH)(COLOR_3DSHADOW + 1);
    wndexcls.lpszMenuName = NULL;
    wndexcls.style = NULL;
    wndexcls.hInstance = hInstance;
    wndexcls.cbSize = sizeof(WNDCLASSEX);
    wndexcls.cbClsExtra = 0;
    wndexcls.cbWndExtra = 0;
    wndexcls.lpfnWndProc = WndProc;
    RegisterClassExA(&wndexcls);
    HWND Win_Handle = CreateWindowExA(0, "win", "TestApp", WS_OVERLAPPEDWINDOW, 100, 100, 640, 380, 0, 0, hInstance, 0);
    ShowWindow(Win_Handle, SW_SHOWDEFAULT);
    UpdateWindow(Win_Handle);
    int exitcode;
    exitcode = MessageLoop();
    return exitcode;
}
int MessageLoop()
{
    MSG wnd_msg;
    while (GetMessage(&wnd_msg, NULL, 0, 0)>0)
    {
        TranslateMessage(&wnd_msg);
        DispatchMessage(&wnd_msg);
    }
    return (int)wnd_msg.wParam;
}

我不确定重定向控制台窗口的输入和输出是否有意义。如果这是为了练习,那么我展示了一个更简单的例子来重定向控制台的输出。

控制台程序似乎是ANSI,而Windows程序是UNICODE。这一部分让人困惑,你必须在某些地方使用ANSI API。

DWORD WINAPI run(LPVOID)
{
    HANDLE out_read = NULL;
    HANDLE out_write = NULL;
    PROCESS_INFORMATION pi{};
    STARTUPINFO si{ sizeof STARTUPINFO };
    si.dwFlags = STARTF_USESTDHANDLES;
    { //set handles
        SECURITY_ATTRIBUTES secAttr;
        secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
        secAttr.bInheritHandle = TRUE;
        secAttr.lpSecurityDescriptor = nullptr;
        if (!CreatePipe(&out_read, &out_write, &secAttr, 0)) error();
        if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) error();
        si.hStdOutput = out_write;
        si.dwFlags |= STARTF_USESTDHANDLES;
    }
    if (!CreateProcess(L"consoleProgram.exe", 0, 0, 0, TRUE, 0, 0, 0, &si, &pi))
        error();
    while (pi.hProcess && WaitForSingleObject(pi.hProcess, 25) == WAIT_TIMEOUT)
    {
        if (out_read == INVALID_HANDLE_VALUE)
            break;
        DWORD readCount;
        char buf[1024];
        memset(buf, 0, sizeof buf);
        if (ReadFile(out_read, buf, sizeof(buf), &readCount, NULL))
        {
            if (readCount)
            {
                SendMessageA(hedit, EM_SETSEL, WPARAM(-1), LPARAM(-1));
                SendMessageA(hedit, EM_REPLACESEL, 0, (LPARAM)buf);
            }
        }
    }
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return 0;
}
LRESULT CALLBACK WndProc(...)
{
    ...
    case WM_COMMAND:
        switch (LOWORD(wParam))
            case BUTTON1:
                CreateThread(NULL, 0, run, 0, 0, NULL);
            ...
}