我想写一个程序来执行cmd或powershell命令。为了实现这一点,我创建了一个进程,并使用pipes作为输入/输出。我的代码工作正常,但在命令(例如:“ipconfig”)执行后,我的程序不会停止,因为ReadFile阻止了它。
我尝试使用ShellExecute,但我不知道如何重定向输出,所以我更喜欢使用CashProcess和我现有的代码的解决方案。
我的代码:
#include "execute_cmdpws.h"
#include <stdio.h>
bool create_pipe(PHANDLE stdInWrite, PHANDLE stdInRead, PHANDLE stdOutWrite, PHANDLE stdOutRead){
bool status = false;
int iResult;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
iResult = CreatePipe(stdInRead, stdInWrite, &sa, 0);
if (!iResult){
CloseHandle(*stdInRead);
CloseHandle(*stdInWrite);
goto end;
}
iResult = CreatePipe(stdOutRead, stdOutWrite, &sa, 0);
if (!iResult) {
CloseHandle(*stdInRead);
CloseHandle(*stdInWrite);
CloseHandle(*stdOutRead);
CloseHandle(*stdOutWrite);
goto end;
}
status = true;
end:
return status;
}
bool execute_command(char* shelltype, char* command, PHANDLE stdInWrite, PHANDLE stdInRead, PHANDLE stdOutWrite, PHANDLE stdOutRead){
bool status = false;
int iResult;
int buffer_size = 1024;
char buffer[buffer_size];
DWORD dwBytesRead;
STARTUPINFO sinfo;
memset(&sinfo, 0, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
sinfo.dwFlags = (STARTF_USESTDHANDLES);
sinfo.hStdInput = *stdInRead;
sinfo.hStdOutput = sinfo.hStdError = *stdOutWrite;
PROCESS_INFORMATION pinfo;
iResult = CreateProcessA(NULL, shelltype, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo);
if (!iResult){
goto end;
}
// Wait for the process to start
WaitForInputIdle(pinfo.hProcess, INFINITE);
if (!iResult){
goto end;
}
DWORD dwBytesWritten = 0;
iResult = WriteFile(*stdInWrite, command, strlen(command), &dwBytesWritten, NULL);
if (!iResult){
goto end;
}
// Close the write end of the pipes in the parent process, as they are not needed
CloseHandle(*stdInWrite);
// Read the output in a loop until there is no more data
while (true) {
iResult = ReadFile(*stdOutRead, buffer, buffer_size - 1, &dwBytesRead, NULL);
if (!iResult || dwBytesRead == 0) {
break; // No more data or error reading
}
buffer[dwBytesRead] = '\0'; // Null-terminate the buffer
printf("Output: %s", buffer);
memset(buffer, 0, buffer_size);
}
if (!iResult){
goto end;
}
CloseHandle(*stdOutRead);
status = true;
end:
TerminateProcess(pinfo.hProcess, 0);
CloseHandle(pinfo.hProcess);
CloseHandle(pinfo.hThread);
// Close the read end of the pipes in the parent process
CloseHandle(*stdInRead);
CloseHandle(*stdOutWrite);
return status;
}
int main(){
HANDLE
stdInWrite,
stdInRead,
stdOutWrite,
stdOutRead;
bool res;
res = create_pipe(&stdInWrite, &stdInRead, &stdOutWrite, &stdOutRead);
if (!res) return 1;
res = execute_command("cmd.exe", "ipconfig\n", &stdInWrite, &stdInRead, &stdOutWrite, &stdOutRead);
if (!res) return 2;
return 0;
}
字符串
这是我得到的输出的最后一行(它似乎需要输入,但我不能输入任何东西):
输出:C:\path\to\my\folder>
有没有解决这个问题的方法或者其他方法来执行cmd / powershell命令并重定向输出?
编辑:
我现在使用这个代码,它的工作,但它不是理想的。如果有人有其他的想法,我仍然在寻找解决方案。
// Read the output in a loop until there is no more data
DWORD dwTotalBytesAvail;
DWORD dwBytesRead;
DWORD timeout = 1000;
DWORD start_time = GetTickCount();
while (true){
iResult = PeekNamedPipe(stdOutRead, NULL, 0, NULL, &dwTotalBytesAvail, NULL);
if (!iResult){
return 3;
}
if (dwTotalBytesAvail > 0){
iResult = ReadFile(stdOutRead, buffer, buffer_size - 1, &dwBytesRead, NULL);
if (!iResult || dwBytesRead == 0){
break; // No more data or error reading
}
buffer[dwBytesRead] = '\0'; // Null-terminate the buffer
printf("Output: %s", buffer);
memset(buffer, 0, buffer_size);
start_time = GetTickCount();
} else {
DWORD elapsed_time = GetTickCount() - start_time;
if (elapsed_time >= timeout){
break;
}
Sleep(100);
}
}
型
1条答案
按热度按时间smdncfj31#
程序不会停止,因为
ReadFile
阻止了它。但是为什么
ReadFile
必须返回?如果我们使用异步句柄-它总是返回。但是如果同步句柄- API调用可以等待无限长的输入。如果我们使用管道句柄- I/O操作将在链接管道末端的last句柄关闭的情况下完成。如果子进程中的链接管道-当进程退出时-所有句柄都关闭,如果没有其他句柄-这将是last句柄,
ReadFile
返回(错误将是STATUS_PIPE_BROKEN
- * 管道操作失败,因为管道的另一端已经关闭 )。但是如果我们忘记关闭self进程中链接的管道端-最后一个句柄将不会关闭,ReadFile
永远不会返回。其他致命错误-启动 cmd.exe .什么意义启动 cmd.exe 当你需要启动 ipconfig.exe?启动它直接没有任何cmd.
当我们用继承的句柄启动进程时,总是需要使用
PROC_THREAD_ATTRIBUTE_HANDLE_LIST
属性,以精确控制什么将被继承下一个-对于什么是需要2管对?!真正单个管道对总是足够的,管道可以同时用于读和写,而不需要额外的管道对,但是这里存在一个问题-如果使用同步文件-所有的操作都是顺序的,直到前面没有完成.这可能导致死锁.只是因为这经常使用2个单独的管道对-读和写,为了避免可能的死锁.但如果我们使用异步管道(如何最小化从自己的一边,另一端可以同步)我们从来没有得到死锁,可以使用单管道对
在许多情况下,像 ipconfig.exe 我们只需要读输出。我们不需要写。所以可能(并且必须)在这里只使用单个管道对,即使在完全同步管道的情况下(两端都是同步的)
因此, ipconfig.exe * 的最小示例
字符串
这里我们只使用单管道对进行读取(两端是同步的)
如果我们需要更通用的代码,在需要写命令的地方,在同步管道的情况下,代码可以是下一个:
型
最后,以异步管道为例(实际上我们的端是异步的,客户端是同步的)
对于使用异步I/O,我们几乎总是需要一些lib/类来进行带句柄和I/O的 Package 操作
对于当前的演示-非常小的代码:
型
uObject
是抽象的。需要为具体对象实现OnIoComplete
。对于管道(这种简单的情况),我们可以做下一个实现:型
然后我们需要自己实现
CreatePipe
API。标准API创建完全同步的管道对。但是我们需要异步。型
现在我们可以使用下一个代码:
型