如何从连接的控制台执行命令

How to execute commands from an attached console

我正在编写一个 WinAPI GUI 程序,需要调用 ftp 和可能的其他控制台程序,同时让它们的控制台输出相应地执行 ie .在读取所有输出之前等待 ftp 完成执行是不行的。

我目前的方法是调用 CreateProcess() 创建一个 cmd.exe 进程,可能会隐藏丑陋的控制台 window、AttachConsole()让它成为我自己的,GetStdHandle() 获取输入和输出句柄,SetConsoleCursorPosition() 控制台缓冲区的末尾 ,以及 WriteConsole() 使用这样的命令如 ftp\ndir\n。然而,此命令已编写但未执行。但是,我可以手动使用相同的控制台(使用 CreateProcess()CREATE_NEW_CONSOLE 标志)键入 ftp 按回车键并执行它。

以前涉及的方法:

  1. 直接用 CreateProcess() 调用 ftp 并重定向 inputs/outputs。

    Couldn't get ftp output until the CreateProcess() process had already ended.

  2. 使用system()

    Was advised against its usage before getting any output.

我当前的精简代码:

// Next two structures might be a bit misleading, they were used for the 1. previous
// approach
PROCESS_INFORMATION piProcInfo;
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION)); 
STARTUPINFO siStartInfo;
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO); 
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES security;
security.nLength = sizeof(SECURITY_ATTRIBUTES); 
security.lpSecurityDescriptor = NULL;
security.bInheritHandle = FALSE;
CreateProcess( NULL, "cmd", &security, &security, FALSE, NORMAL_PRIORITY_CLASS |
 CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &piProcInfo); 
uint32_t pidConsole = piProcInfo.dwProcessId;
while ( ! AttachConsole(pidConsole) ){};
HANDLE myConsoleIn, myConsoleOut;
myConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
myConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
Sleep(100);
CONSOLE_SCREEN_BUFFER_INFO myConsoleCursorInformation = {};
GetConsoleScreenBufferInfo(myConsoleOut,&myConsoleCursorInformation);
SetConsoleCursorPosition(myConsoleOut,myConsoleCursorInformation.dwSize);
CHAR myConsoleBuffer[200]="dir\n";
DWORD myConsoleProcessed;
WriteConsole( myConsoleOut, myConsoleBuffer, 4, &myConsoleProcessed, NULL);

我怎样才能让写在控制台中的命令执行? 除了我尝试以尾随 \n [=39= 结束命令之外,还有其他方法吗? ]IE。使用 WriteConsole() 和 dir\n 或 ftp\n 参数。

我考虑过在键入所需命令后向相关进程发送按键。而创建的控制台不仅需要手动回车,还需要手动输入dirftp之类的命令.

请随时指出任何遗漏的信息!

How can I get a command written in the console to execute? Is there an alternative to my attempt of ending commands with a trailing \n ie. using WriteConsole() with a dir\n or ftp\n argument.

试试下面的代码看看是否有效:

STARTUPINFO si;
PROCESS_INFORMATION pi;

ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));

const wchar_t *cmdPath = L"C:\Windows\System32\cmd.exe";
wchar_t *cmdArgs = (wchar_t *)L"C:\Windows\System32\cmd.exe /k dir";

BOOL result = CreateProcess(cmdPath, cmdArgs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
DWORD errCode = GetLastError();
if (!result)
{
    std::cout << "Create Process failed: " << GetLastError() << std::endl;
}

/K Run Command and then return to the CMD prompt. This is useful for testing, to examine variables

如果需要,请使用 /C "Run Command and then terminate"。

更新: 使用管道与子进程(cmd.exe)通信的完整代码。

HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;

#define BUFSIZE 1024

void ErrorExit(LPCTSTR lpszFunction)
{
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError();

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf,
        0, NULL);

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
    StringCchPrintf((LPTSTR)lpDisplayBuf,
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"),
        lpszFunction, dw, lpMsgBuf);
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(1);
}

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

    for (;;)
    {
        DWORD bytesAvail = 0;
        if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &bytesAvail, NULL)) {
            std::cout << "Failed to call PeekNamedPipe" << std::endl;
        }
        if (bytesAvail) {
            DWORD n;
            BOOL success = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &n, NULL);
            if (!success || n == 0) {
            }
            bSuccess = WriteFile(hParentStdOut, chBuf,n, &dwWritten, NULL);
        }
        else
        {
            break;
        }
    }

}

void WriteToPipe(void)
{
    DWORD dwWritten;
    BOOL bSuccess = FALSE;
    CHAR buf[] = "dir\n";

    bSuccess = WriteFile(g_hChildStd_IN_Wr, buf, sizeof(buf)-1, &dwWritten, NULL);
}

int main()
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    SECURITY_ATTRIBUTES saAttr;

    printf("\n->Start of parent execution.\n");

    // Set the bInheritHandle flag so pipe handles are inherited. 

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

    // Create a pipe for the child process's STDOUT. 

    if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
        ErrorExit(TEXT("StdoutRd CreatePipe"));

    // Ensure the read handle to the pipe for STDOUT is not inherited.

    if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
        ErrorExit(TEXT("Stdout SetHandleInformation"));

    // Create a pipe for the child process's STDIN. 

    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
        ErrorExit(TEXT("Stdin CreatePipe"));

    // Ensure the write handle to the pipe for STDIN is not inherited. 

    if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
        ErrorExit(TEXT("Stdin SetHandleInformation"));

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));


    si.cb = sizeof(STARTUPINFO);
    si.hStdError = g_hChildStd_OUT_Wr;
    si.hStdOutput = g_hChildStd_OUT_Wr;
    si.hStdInput = g_hChildStd_IN_Rd;
    si.dwFlags |= STARTF_USESTDHANDLES;

    TCHAR cmdPath[] = TEXT("C:\Windows\System32\cmd.exe");

    BOOL result = CreateProcess(cmdPath, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
    DWORD errCode = GetLastError();
    if (!result)
    {
        std::cout << "Create Process failed: " << GetLastError() << std::endl;
    }

    for (;;)
    {
        ReadFromPipe();

        WriteToPipe();
    }
}