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

时间:2019-01-10 02:38:25

标签: c++ winapi c++17 windows-console

我正在编码一个WinAPI GUI程序,该程序需要调用 ftp 以及可能的其他控制台程序,同时使它们的控制台输出相应地起作用。等待ftp完成执行,然后再读取其所有输出。

我当前的方法是调用import java.util.Arrays; import java.util.Enumeration; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; public class Test { public static void main(String[] args) { DefaultMutableTreeNode root = new DefaultMutableTreeNode("~"); DefaultTreeModel model = new DefaultTreeModel(root); DefaultMutableTreeNode docs = new DefaultMutableTreeNode("Documents"); docs.add(new DefaultMutableTreeNode("text1.txt")); docs.add(new DefaultMutableTreeNode("text2.txt")); root.add(docs); DefaultMutableTreeNode pics = new DefaultMutableTreeNode("Pictures"); pics.add(new DefaultMutableTreeNode("text1.txt")); root.add(pics); describe(model); DefaultMutableTreeNode newFolder = new DefaultMutableTreeNode("folder1"); newFolder.add(new DefaultMutableTreeNode("text1.txt")); insertNodes(model, findNode(model, "~/Pictures"), newFolder); describe(model); } protected static DefaultMutableTreeNode findNode(DefaultTreeModel model, String path) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) model.getRoot(); String[] parts = path.split("/"); if (node.getUserObject().toString().equals(parts[0])) { return findNode(node, Arrays.copyOfRange(parts, 1, parts.length)); } return null; } protected static DefaultMutableTreeNode findNode(DefaultMutableTreeNode node, String[] path) { if (path.length == 0) { return node; } Enumeration<TreeNode> children = node.children(); while (children.hasMoreElements()) { DefaultMutableTreeNode child = (DefaultMutableTreeNode) children.nextElement(); if (child.getUserObject().toString().equals(path[0])) { return findNode(child, Arrays.copyOfRange(path, 1, path.length)); } } return null; } private static void insertNodes( DefaultTreeModel model, DefaultMutableTreeNode parent, DefaultMutableTreeNode newNode) { parent.add(newNode); model.nodesWereInserted(parent, new int[]{parent.getChildCount() - 1}); } protected static void describe(DefaultTreeModel model) { describe((DefaultMutableTreeNode) model.getRoot(), 0); } protected static void describe(DefaultMutableTreeNode node, int level) { StringBuilder padding = new StringBuilder(); for (int index = 0; index < level; index++) { padding.append(" "); } System.out.println(padding + "+ " + node); padding.append(" "); Enumeration<TreeNode> children = node.children(); while (children.hasMoreElements()) { DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) children.nextElement(); if (childNode.isLeaf()) { System.out.println(padding + "- " + childNode); } else { describe(childNode, level + 1); } } } } 创建一个 cmd.exe 进程,该进程可能隐藏丑陋的控制台窗口CreateProcess(),使其成为我自己的AttachConsole()获取输入和输出句柄,GetStdHandle()到达控制台缓冲区的末尾,SetConsoleCursorPosition()使用诸如 ftp \ n dir \ n 。但是,该命令已写入但未执行。但是,我可以手动使用同一控制台(将WriteConsole()CreateProcess()标志一起使用)键入 ftp ,然后按enter键并使其执行。

涉及的先前方法:

  1. 直接使用CREATE_NEW_CONSOLE调用 ftp 并重定向输入/输出。
      

    CreateProcess()进程结束之前,无法获得 ftp 输出。

  2. 使用CreateProcess()
      

    在获得任何输出之前,建议不要使用它。

我当前的精简代码:

system()

如何获取在控制台中编写的命令以执行?除了用结尾的// 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); ie结束命令之外,我还有其他选择吗? \n 和dir \ n或ftp \ n参数一起使用。

我考虑过在键入所需命令后将按键发送到相关进程。创建的控制台不仅需要手动按下Enter键,还需要具有 dir ftp 或任何手动键入的命令。

请随时指出任何缺少的信息!

1 个答案:

答案 0 :(得分:0)

  

如何获取在控制台中编写的命令以执行?有没有   替代我尝试以尾随\ n即结束命令的尝试。   使用带有dir \ n或ftp \ n参数的WriteConsole()。

尝试以下代码以查看其是否有效:

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运行命令,然后返回到CMD提示符。             这对于测试,检查变量很有用

如果要“运行命令然后终止”,请使用/ C。

更新用于使用管道与子进程(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();
    }
}