17

我这里有一个严重的问题。我需要在不显示控制台窗口的情况下通过 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,直到我知道这是有效的。

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

4

6 回答 6

19

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

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

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

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

于 2012-07-19T17:33:36.710 回答
8

这是我对 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;
}
于 2016-03-11T08:28:05.780 回答
5

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

于 2012-07-19T15:59:20.527 回答
2

我在 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 可以在后台运行,它是一个瘦服务器程序

于 2017-10-30T00:28:05.853 回答
0

为了添加到@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照顾它。

于 2019-09-03T12:30:39.763 回答
0

你可以使用

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

希望这对你有用

于 2019-12-18T17:14:26.873 回答