C++ 执行 CMD 命令

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11564594/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:16:57  来源:igfitidea点击:

C++ Executing CMD Commands

c++windowswinapicmdwindows-console

提问by JP29

I'm having a serious problem here. I need to execute a CMD command line via C++ without the console window displaying. Therefore I cannot use system(cmd), since the window will display.

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

I have tried winExec(cmd, SW_HIDE), but this does not work either. CreateProcessis another one I tried. However, this is for running programs or batch files.

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

I have ended up trying ShellExecute:

我最终尝试ShellExecute

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

Can anyone see anything wrong with the above code? I have used SW_SHOWNORMALuntil I know this works.

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

I really need some help with this. Nothing has come to light, and I have been trying for quite a while. Any advice anyone could give would be great :)

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

采纳答案by arx

Redirecting the output to your own pipe is a tidier solution because it avoids creating the output file, but this works fine:

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

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

You don't see the cmd window and the output is redirected as expected.

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

Your code is probably failing (apart from the /Cthing) because you specify the path as "c:\projects\b"rather than "c:\\projects\\b".

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

回答by Cédric Fran?oys

Here is my implementation of a DosExec function that allows to (silently) execute any DOS command and retrieve the generated output as a unicode string.

这是我对 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] = '
#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;
}
'; 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; }

回答by Felice Pollano

You should use CreateProcesson cmd.exewith the /Cparameter to tunnel the ipconfig command. The > does not work per se on the command line. You have to redirect programmatically the stdout.

您应该使用带有参数的CreateProcesson来隧道 ipconfig 命令。> 本身在命令行上不起作用。您必须以编程方式重定向 stdoutcmd.exe/C

回答by particlereddy

I have a similar program [windows7 and 10 tested] on github

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

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

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

This is server program which

这是服务器程序

  1. listens on "Local Area Connection" named interface in windows for UDP port (5555) and receives udp packet.
  2. received udp packet content is executed on cmd.exe [please not cmd.exe is NOT closed after running the command and the output string [the output of the executed command] is fedback to the client program over same udp port].
  3. In other words, command received in udp packet -> parsed udp packet -> executed on cmd.exe -> output sent back on same port to client program
  1. 侦听 Windows 中名为“本地连接”的接口以获取 UDP 端口 (5555) 并接收 udp 数据包。
  2. 接收到的udp包内容在cmd.exe上执行【请不要在运行命令后cmd.exe不关闭,输出字符串【执行命令的输出】通过相同的udp端口反馈给客户端程序】。
  3. 换句话说,在 udp 数据包中接收到的命令 -> 解析 udp 数据包 -> 在 cmd.exe 上执行 -> 在同一端口上发送回客户端程序的输出

This does not show "console window" No need for someone to execute manually command on cmd.exe remwin.exe can be running in background and its a thin server program

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

回答by kakyo

To add to @Cédric Fran?oys answer, I fixed a few things in his code for a Windows build:

为了补充@Cédric Fran?oys 的回答,我在他的 Windows 版本代码中修复了一些内容:

Missing function definition:

缺少函数定义:

To make the code compile, add the following function definition:

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

strcpy_s(output, sizeof(output), "");

strcat_s(output, RESULT_SIZE, buffer);

Unsafe CRT string function calls:

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

To make the code compile, replace strcpyand strcatwith the following calls

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

buffer[bytesRead] = '
 string command = "start /B cmd /c " + myCommand;
 system(command.c_str());
';

Remove redundant null-termination:

删除多余的空终止:

Remove in the do-while loop:

在 do-while 循环中删除:

##代码##

because strcat_stakes care of that.

因为strcat_s会照顾到这一点。

回答by Oyoqui

You could use

你可以用

##代码##

Hopefully this works for you

希望这对你有用