如何向CreateProcess创建的PowerShell进程写入可执行命令
问题:通过C++创建PowerShell进程并执行传入命令
现有代码通过CreateProcess启动PowerShell窗口,尝试用cout输出命令并模拟回车,但PowerShell仅将内容显示为普通文本,不执行命令。原代码如下:
#include <cstdio> #include <windows.h> #include <tlhelp32.h> #include <string> #include <iostream> using namespace std; int main(int argc, char *argv[]) { STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); char myCommand[] = "cmd /c powershell.exe -NoExit"; CreateProcess(NULL, // No module name (use command line) myCommand, // Command line NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi); // Pointer to PROCESS_INFORMATION structure Sleep(5000); string strMytestString("ls"); cout << strMytestString.c_str(); Sleep(2000); // ENTER key down keybd_event(VK_RETURN, 0x9C, 0, 0); // ENTER key up keybd_event(VK_RETURN, 0x9C, KEYEVENTF_KEYUP, 0); return 0; }
原代码问题在于:cout是向当前程序的控制台输出,而非PowerShell窗口;keybd_event是全局模拟按键,但PowerShell窗口未获得焦点,导致按键未生效到目标窗口。
以下是三种可行的解决方案:
方法一:直接通过PowerShell命令行参数执行命令
这是最简单的方式,启动PowerShell时直接通过-Command参数指定要执行的命令,结合-NoExit保持窗口不关闭。
修改CreateProcess的命令行参数:
char myCommand[] = "powershell.exe -NoExit -Command \"ls\"";
无需模拟输入,启动后PowerShell会直接执行ls命令并保留窗口。
方法二:激活PowerShell窗口后模拟输入
先找到PowerShell窗口句柄,将其置为前台,再用SendInput(替代过时的keybd_event)模拟字符输入和回车。
修改后的代码示例:
#include <cstdio> #include <windows.h> #include <string> #include <iostream> using namespace std; int main(int argc, char *argv[]) { STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); // 直接启动PowerShell,无需通过cmd中转 char myCommand[] = "powershell.exe -NoExit"; CreateProcess(NULL, myCommand, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); Sleep(2000); // 等待窗口创建完成 // 查找PowerShell窗口(标题可能因版本不同变化,需根据实际调整) HWND psWindow = FindWindow(NULL, L"Windows PowerShell"); if (psWindow == NULL) { cout << "无法找到PowerShell窗口" << endl; return 1; } // 将窗口置为前台,确保输入生效 SetForegroundWindow(psWindow); Sleep(500); // 等待窗口激活 // 准备要发送的输入内容:"ls" + 回车 string cmd = "ls"; INPUT input[2 * cmd.size() + 2]; ZeroMemory(input, sizeof(input)); int idx = 0; // 逐个发送字符的按下和释放事件 for (char c : cmd) { input[idx].type = INPUT_KEYBOARD; input[idx].ki.wVk = 0; input[idx].ki.wScan = c; input[idx].ki.dwFlags = KEYEVENTF_UNICODE; idx++; input[idx].type = INPUT_KEYBOARD; input[idx].ki.wVk = 0; input[idx].ki.wScan = c; input[idx].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; idx++; } // 发送回车按下事件 input[idx].type = INPUT_KEYBOARD; input[idx].ki.wVk = VK_RETURN; idx++; // 发送回车释放事件 input[idx].type = INPUT_KEYBOARD; input[idx].ki.wVk = VK_RETURN; input[idx].ki.dwFlags = KEYEVENTF_KEYUP; idx++; // 执行输入 SendInput(idx, input, sizeof(INPUT)); return 0; }
方法三:通过标准输入管道写入命令
创建进程时重定向标准输入,通过管道向PowerShell发送命令,无需依赖窗口界面,可靠性更高。
代码示例:
#include <cstdio> #include <windows.h> #include <string> #include <iostream> using namespace std; int main(int argc, char *argv[]) { STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE hStdinRead, hStdinWrite; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.dwFlags |= STARTF_USESTDHANDLES; // 创建管道,用于向子进程发送标准输入 SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (!CreatePipe(&hStdinRead, &hStdinWrite, &saAttr, 0)) { cout << "创建管道失败" << endl; return 1; } // 设置子进程的标准输入为管道读端 si.hStdInput = hStdinRead; // 启动PowerShell进程 char myCommand[] = "powershell.exe -NoExit"; if (!CreateProcess(NULL, myCommand, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { cout << "创建进程失败" << endl; CloseHandle(hStdinRead); CloseHandle(hStdinWrite); return 1; } // 关闭父进程不需要的管道读端 CloseHandle(hStdinRead); Sleep(2000); // 等待进程初始化完成 // 向管道写入命令和换行(PowerShell需要换行触发执行) string cmd = "ls\n"; DWORD bytesWritten; WriteFile(hStdinWrite, cmd.c_str(), cmd.size(), &bytesWritten, NULL); // 关闭管道写端,否则PowerShell可能不会立即处理输入 CloseHandle(hStdinWrite); // 可选:等待进程结束 // WaitForSingleObject(pi.hProcess, INFINITE); // 清理句柄 CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return 0; }
三种方法对比
- 方法一:实现最简单,适合执行已知的单次命令,但无法后续动态输入。
- 方法二:适合模拟用户交互场景,但依赖窗口标题和焦点,环境变化时可能失效。
- 方法三:最可靠,不依赖GUI界面,适合后台执行或需要多次输入的场景。
内容的提问来源于stack exchange,提问作者p3ppi
相关产品推荐
相关产品推荐

