【问题标题】:C++ Executing CMD CommandsC++ 执行 CMD 命令
【发布时间】:2012-07-19 15:51:19
【问题描述】:

我在这里遇到了一个严重的问题。我需要在不显示控制台窗口的情况下通过 C++ 执行 CMD 命令行。因此我不能使用system(cmd),因为会显示窗口。

我试过winExec(cmd, SW_HIDE),但这也不起作用。 CreateProcess 是我尝试过的另一个。但是,这是用于运行程序或批处理文件的。

我最终尝试了ShellExecute

ShellExecute( NULL, "open",
    "cmd.exe",
    "ipconfig > myfile.txt",
    "c:\projects\b",
    SW_SHOWNORMAL
);

任何人都可以看到上面的代码有什么问题吗?我一直使用SW_SHOWNORMAL,直到我知道这可行。

我真的需要一些帮助。什么都没有曝光,我已经尝试了很长时间。任何人都可以提供的任何建议都会很棒:)

【问题讨论】:

  • 你检查返回码了吗?
  • 我知道你已经得到了答案,但通常最好说一下它是如何不起作用的。
  • 为什么不调用 WMI_ 函数并将结果写入文件。没有窗口,只有您需要的数据。

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


【解决方案1】:

将输出重定向到您自己的管道是一种更简洁的解决方案,因为它避免了创建输出文件,但这工作正常:

ShellExecute(0, "open", "cmd.exe", "/C ipconfig > out.txt", 0, SW_HIDE);

您看不到 cmd 窗口,输出按预期重定向。

您的代码可能失败(除了/C 事物),因为您将路径指定为"c:\projects\b" 而不是"c:\\projects\\b"

【讨论】:

    【解决方案2】:

    这是我实现的 DosExec 函数,它允许(静默)执行任何 DOS 命令并将生成的输出检索为 unicode 字符串。

    // Convert an OEM string (8-bit) to a UTF-16 string (16-bit) 
    #define OEMtoUNICODE(str)   CHARtoWCHAR(str, CP_OEMCP)
    
    /* Convert a single/multi-byte string to a UTF-16 string (16-bit).
     We take advantage of the MultiByteToWideChar function that allows to specify the charset of the input string.
    */
    LPWSTR CHARtoWCHAR(LPSTR str, UINT codePage) {
        size_t len = strlen(str) + 1;
        int size_needed = MultiByteToWideChar(codePage, 0, str, len, NULL, 0);
        LPWSTR wstr = (LPWSTR) LocalAlloc(LPTR, sizeof(WCHAR) * size_needed);
        MultiByteToWideChar(codePage, 0, str, len, wstr, size_needed);
        return wstr;
    }
    
    /* Execute a DOS command.
    
     If the function succeeds, the return value is a non-NULL pointer to the output of the invoked command. 
     Command will produce a 8-bit characters stream using OEM code-page.
    
     As charset depends on OS config (ex: CP437 [OEM-US/latin-US], CP850 [OEM 850/latin-1]),
     before being returned, output is converted to a wide-char string with function OEMtoUNICODE.
    
     Resulting buffer is allocated with LocalAlloc.
     It is the caller's responsibility to free the memory used by the argument list when it is no longer needed. 
     To free the memory, use a single call to LocalFree function.
    */
    LPWSTR DosExec(LPWSTR command){
        // Allocate 1Mo to store the output (final buffer will be sized to actual output)
        // If output exceeds that size, it will be truncated
        const SIZE_T RESULT_SIZE = sizeof(char)*1024*1024;
        char* output = (char*) LocalAlloc(LPTR, RESULT_SIZE);
    
        HANDLE readPipe, writePipe;
        SECURITY_ATTRIBUTES security;
        STARTUPINFOA        start;
        PROCESS_INFORMATION processInfo;
    
        security.nLength = sizeof(SECURITY_ATTRIBUTES);
        security.bInheritHandle = true;
        security.lpSecurityDescriptor = NULL;
    
        if ( CreatePipe(
                        &readPipe,  // address of variable for read handle
                        &writePipe, // address of variable for write handle
                        &security,  // pointer to security attributes
                        0           // number of bytes reserved for pipe
                        ) ){
    
    
            GetStartupInfoA(&start);
            start.hStdOutput  = writePipe;
            start.hStdError   = writePipe;
            start.hStdInput   = readPipe;
            start.dwFlags     = STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
            start.wShowWindow = SW_HIDE;
    
    // We have to start the DOS app the same way cmd.exe does (using the current Win32 ANSI code-page).
    // So, we use the "ANSI" version of createProcess, to be able to pass a LPSTR (single/multi-byte character string) 
    // instead of a LPWSTR (wide-character string) and we use the UNICODEtoANSI function to convert the given command 
            if (CreateProcessA(NULL,                    // pointer to name of executable module
                               UNICODEtoANSI(command),  // pointer to command line string
                               &security,               // pointer to process security attributes
                               &security,               // pointer to thread security attributes
                               TRUE,                    // handle inheritance flag
                               NORMAL_PRIORITY_CLASS,   // creation flags
                               NULL,                    // pointer to new environment block
                               NULL,                    // pointer to current directory name
                               &start,                  // pointer to STARTUPINFO
                               &processInfo             // pointer to PROCESS_INFORMATION
                             )){
    
                // wait for the child process to start
                for(UINT state = WAIT_TIMEOUT; state == WAIT_TIMEOUT; state = WaitForSingleObject(processInfo.hProcess, 100) );
    
                DWORD bytesRead = 0, count = 0;
                const int BUFF_SIZE = 1024;
                char* buffer = (char*) malloc(sizeof(char)*BUFF_SIZE+1);
                strcpy(output, "");
                do {                
                    DWORD dwAvail = 0;
                    if (!PeekNamedPipe(readPipe, NULL, 0, NULL, &dwAvail, NULL)) {
                        // error, the child process might have ended
                        break;
                    }
                    if (!dwAvail) {
                        // no data available in the pipe
                        break;
                    }
                    ReadFile(readPipe, buffer, BUFF_SIZE, &bytesRead, NULL);
                    buffer[bytesRead] = '\0';
                    if((count+bytesRead) > RESULT_SIZE) break;
                    strcat(output, buffer);
                    count += bytesRead;
                } while (bytesRead >= BUFF_SIZE);
                free(buffer);
            }
    
        }
    
        CloseHandle(processInfo.hThread);
        CloseHandle(processInfo.hProcess);
        CloseHandle(writePipe);
        CloseHandle(readPipe);
    
        // convert result buffer to a wide-character string
        LPWSTR result = OEMtoUNICODE(output);
        LocalFree(output);
        return result;
    }
    

    【讨论】:

    • godlike 我试图使用 ConsoleScreenBuffer 而不是管道,不知道是否有可能,谢谢
    • 我不明白你对WaitForSingleObject的使用,它似乎不需要,如果是的话,做整个循环的事情没有意义,你可以使用INFINITE等待它返回.此外,我很惊讶您使用 + 作为位标志并没有在您的脸上爆炸。请使用按位或运算符。
    【解决方案3】:

    我在github上有一个类似的程序[windows7和10测试]

    https://github.com/vlsireddy/remwin/tree/master/remwin

    这是服务器程序

    1. 侦听 Windows 中名为“本地连接”的 UDP 端口 (5555) 接口并接收 udp 数据包。
    2. 接收到的udp包内容在cmd.exe上执行[请不要cmd.exe在运行命令后没有关闭,输出字符串[执行命令的输出]通过同一个udp端口反馈给客户端程序]。
    3. 换句话说, 在 udp 数据包中接收到的命令 -> 解析的 udp 数据包 -> 在 cmd.exe 上执行 -> 在同一端口上发送回客户端程序的输出

    这不显示“控制台窗口” 不需要有人在 cmd.exe 上手动执行命令 remwin.exe 可以在后台运行,它是一个瘦服务器程序

    【讨论】:

    • 您好,看起来监管太多了,我放了一个有效的测试链接,它通过测试代码的具体答案回答具体问题,不理解您的删除审查建议。你运行代码 [MSVC],检查它的输出,测试它,如果不合适,删除它。
    • 阅读您的答案,在我看来您解决了问题。但是,Stack Overflow 不鼓励以外部链接形式提供解决方案的答案。
    • 一半以上的代码是从ms article 复制的,包括cmets,不提供信用。我想udp代码也是。必须得到那些 github 视图?
    【解决方案4】:

    您应该在cmd.exe 上使用CreateProcess/C 参数来隧道ipconfig 命令。 > 在命令行上本身不起作用。你必须redirect programmatically the stdout

    【讨论】:

    • 如果是cmd /c 命令行,那么> 重定向当然会起作用。
    【解决方案5】:

    为了补充@Cédric Françoys 的答案,我在他的 Windows 构建代码中修复了一些问题:

    缺少函数定义:

    要编译代码,添加以下函数定义:

    #define UNICODEtoANSI(str)   WCHARtoCHAR(str, CP_OEMCP)
    
    LPSTR WCHARtoCHAR(LPWSTR wstr, UINT codePage) {
        int len = (int)wcslen(wstr) + 1;    
        int size_needed = WideCharToMultiByte(codePage, 0, wstr, len, NULL, 0, NULL, NULL);
        LPSTR str = (LPSTR)LocalAlloc(LPTR, sizeof(CHAR) * size_needed);
        WideCharToMultiByte(codePage, 0, wstr, len, str, size_needed, NULL, NULL);
        return str;
    }
    

    不安全的 CRT 字符串函数调用:

    要编译代码,请将strcpystrcat 替换为以下调用

    strcpy_s(output, sizeof(output), "");
    
    strcat_s(output, RESULT_SIZE, buffer);
    

    删除多余的空终止:

    在do-while循环中移除:

    buffer[bytesRead] = '\0';
    

    因为strcat_s 负责。

    【讨论】:

      【解决方案6】:

      你可以使用

       string command = "start /B cmd /c " + myCommand;
       system(command.c_str());
      

      希望这对你有用

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-19
        • 2018-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-24
        • 2016-06-17
        相关资源
        最近更新 更多