I am using _popen to start a process to run a command and gather the output
This is my c++ code:
bool exec(string &cmd, string &result)
{
result = "";
FILE* pipe = _popen(cmd.c_str(), "rt");
if (!pipe)
return(false);
char buffer[128];
while(!feof(pipe))
{
if(fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
_pclose(pipe);
return(true);
}
Is there any way of doing this without a console window opening (as it currently does at the _popen statement)?
On Windows, CreateProcess with a STARTUPINFO structure that has dwFlags to include STARTF_USESSHOWWINDOW. Then setting STARTUPINFO.dwFlags to SW_HIDE will cause the console window to be hidden when triggered. Example code (which may be poorly formatted, and contains a mix of C++ and WinAPI):
#include <windows.h>
#include <iostream>
#include <string>
using std::cout;
using std::endl;
void printError(DWORD);
int main()
{
STARTUPINFOA si = {0};
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
BOOL result = ::CreateProcessA("c:/windows/system32/notepad.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if(result == 0) {
DWORD error = ::GetLastError();
printError(error);
std::string dummy;
std::getline(std::cin, dummy);
return error;
}
LPDWORD retval = new DWORD[1];
::GetExitCodeProcess(pi.hProcess, retval);
cout << "Retval: " << retval[0] << endl;
delete[] retval;
cout << "Press enter to continue..." << endl;
std::string dummy;
std::getline(std::cin, dummy);
return 0;
}
void printError(DWORD error) {
LPTSTR lpMsgBuf = nullptr;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
cout << reinterpret_cast<char*>(lpMsgBuf) << endl;
LocalFree(lpMsgBuf);
}
As far as I know, you can't1: you are starting a console application (cmd.exe, that will run the specified command), and Windows always creates a console window when starting a console application.
although, you can hide the window after the process started, or even create it hidden if you pass the appropriate flags to CreateProcess; problem is, _popen do not pass these flags, so you have to use the Win32 APIs instead of _popen to create your pipe.
[Final Edit]
a similar SO question merges everything said above and gets you your output
C++ popen command without console
[Edited again]
erk. sorry I got excited about spawning processes. I reread your q. and apart from the extra window you're actually trying to get the processes's stdout/stderr. I'd just like to add that for that purpose, all my suggestions are sadly irrelevant. but I'll leave them here for reference.
[Edited]
For no good specific reason (except that "open" works for both windows and macs), I use ShellExecute for spawning processes rather than CreateProcess. I'll research that later..but here is my StartProcess function.
Hidden or Minimized seem to produce the same result. the cmd window does come into being but it is minimized and doesn't ever pop up on the desktop which might be your primary goal.
#if defined(PLATFORM_WIN32)
#include <Windows.h>
#include <shellapi.h>
#elif defined(PLATFORM_OSX)
#include <sys/param.h>
#endif
namespace LGSysUtils
{
// -----------------------------------------------------------------------
// pWindow : {Optional} - can be NULL
// pOperation : "edit", "explore", "find", "open", "print"
// pFile : url, local file to execute
// pParameters : {Optional} - can be NULL otherwise a string of args to pass to pFile
// pDirectory : {Optional} - set cwd for process
// type : kProcessWinNormal, kProcessWinMinimized, kProcessWinMaximized, kProcessHidden
//
bool StartProcess(void* pWindow, const char* pOperation, const char* pFile, const char* pParameters, const char* pDirectory, LGSysUtils::eProcessWin type)
{
bool rc = false;
#if defined(PLATFORM_WIN32)
int showCmd;
switch(type)
{
case kProcessWinMaximized:
showCmd = SW_SHOWMAXIMIZED;
break;
case kProcessWinMinimized:
showCmd = SW_SHOWMINIMIZED;
break;
case kProcessHidden:
showCmd = SW_HIDE;
break;
case kProcessWinNormal:
default:
showCmd = SW_NORMAL;
}
int shellRC = (int)ShellExecute(reinterpret_cast<HWND>(pWindow), pOperation,pFile,pParameters,pDirectory,showCmd);
//Returns a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise.
if( shellRC > 32 )
{
rc = true;
}
#elif defined(PLATFORM_OSX)
char cmd[1024];
sprintf(cmd, "%s %s", pOperation, pFile);
int sysrc = system( cmd );
dbPrintf("sysrc = %d", sysrc);
rc = true;
#endif
return rc;
}
}
[and previously mentioned]
If you are in control of the source code for the application that is launched, you could try adding this to the top of your main.cpp (or whatever you have named it)
// make this process windowless/aka no console window
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
You could also feed those options to the linker directly. The above is easier to play with for different build configurations imho.
Related
I am trying to create a program in which you can execute commands. The output of these commands should be displayed in a GUI. For this I use QT (because I want to get familiar with WinAPI I don't use QProcess). In the current program it is already possible to redirect the output of a command with a handle. Now my question, how is it possible to interrupt the ReadFile if the command expects a user input.
As an example, I want to run the command yarn run from C++.
This returns as output that this command does not exist and asks which command I want to execute instead. At the moment the command aborts there (comparable with CTRL+C) and returns error No command specified. At this point, however, a user input should be possible.
Expected outcome of the program:
The output I get instead:
As you can see in picture 1 yarn asks the user for input. In image 2 there is no question at all. This behaviour is for example possible if you press CTRL+C if the question input shows up.
So how is it possible to make a user input in the gui (for now it would be enough to redirect the value of a variable into the input) and redirect it back to the process. The process should wait until it gets the input.
Command.h
#ifndef COMMAND_H
#define COMMAND_H
#include <string>
#include <cstdlib>
#include <cstdio>
#include <io.h>
#include <fcntl.h>
#include <stdexcept>
#include <windows.h>
#include <iostream>
#define BUFSIZE 256
class Project;
class Command
{
private:
int exitStatus;
const Project * project;
std::string cmd;
HANDLE g_hChildStd_IN_Rd = nullptr;
HANDLE g_hChildStd_IN_Wr = nullptr;
HANDLE g_hChildStd_OUT_Rd = nullptr;
HANDLE g_hChildStd_OUT_Wr = nullptr;
HANDLE g_hInputFile = nullptr;
void setupWindowsPipes();
void createWindowsError(const std::string &errorText);
void readFromPipe();
public:
Command() = delete;
explicit Command(std::string cmd, const Project *project);
void exec();
};
#endif // COMMAND_H
Command.cpp (the entry point which is called by the gui is exec())
#include "command.h"
#include "project.h"
Command::Command(std::string cmd, const Project *project) : exitStatus(0), project(project), cmd(std::move(cmd)) {}
void Command::createWindowsError(const std::string &errorText) {
DWORD code = GetLastError();
LPSTR lpMsgBuf;
if(code == 0) return;
auto size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &lpMsgBuf,
0, NULL );
std::string msg(lpMsgBuf, size);
LocalFree(lpMsgBuf);
throw std::runtime_error(errorText + "()" + std::to_string(code) + ": " + msg);
}
void Command::setupWindowsPipes(){
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = true;
saAttr.lpSecurityDescriptor = nullptr;
if(!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
createWindowsError("StdOutRd CreatePipe");
if(!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
createWindowsError("StdOut SetHandleInformation");
if(!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
createWindowsError("StdInRd CreatePipe");
if(!SetHandleInformation(g_hChildStd_IN_Rd, HANDLE_FLAG_INHERIT, 0))
createWindowsError("StdIn SetHandleInformation");
}
void Command::readFromPipe() {
DWORD dwRead;
char chBuf[BUFSIZE];
bool bSuccess = false;
for (;;)
{
dwRead = 0;
for(int i = 0;i<BUFSIZE;++i) {
chBuf[i] = '\0';
}
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead <= 0 ) break;
std::cout << chBuf;
}
std::cout << std::endl;
}
void Command::exec() {
std::cout << "CMD to run: " << this->cmd << std::endl;
this->setupWindowsPipes();
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&pi, sizeof(pi));
char* dir = nullptr;
if(this->project != nullptr) {
auto n = this->project->getLocalUrl().size() + 1;
auto nString = this->project->getLocalUrl().replace("/", "\\");
dir = new char[n];
std::strncpy(dir, nString.toStdString().c_str(), n);
}
std::string cmdString = "cmd /c ";
cmdString.append(this->cmd);
char cmdCopy[cmdString.size() + 1];
cmdString.copy(cmdCopy, cmdString.size());
cmdCopy[cmdString.size() + 1] = '\0';
bool rc = CreateProcessA( nullptr,
cmdCopy,
nullptr,
nullptr,
true,
CREATE_NO_WINDOW,
nullptr,
dir,
&si,
&pi);
delete []dir;
if(!rc)
createWindowsError("Failed to create process");
std::cout << "PID: " << pi.dwProcessId << std::endl;
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_IN_Rd);
readFromPipe();
std::cout << "fin reading pipe" << std::endl;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
It sounds like you have an XY problem, luckily you described X so we can address it.
The issue is not your failure to call WriteFile to store the response into the redirected input pipe. If the program were trying to read input, it would wait.
The issue is that the program is not requesting input at all. It has detected that interactive input is not possible, because it detects a pipe and assumes that a pipe is not interactive. So it doesn't perform the prompt or try to read from standard input at all. You can't provide an answer to a question that the program didn't ask!
(To confirm this is the behavior of the yarn program you are spawning, you can launch it from cmd.exe using a pipe to provide the input. cmd.exe has well-tested buffering logic for redirected input and output handles and you can be sure that any suspected deadlock in your code doesn't affect cmd.exe)
On Unix-like systems, this is solved by redirecting to a pseudo-tty (ptty) special file instead of a pipe special file, which causes the isatty() function to return true.
On Windows, this used to be effectively impossible, as the console API, implemented at kernel level, was permanently associated to the console subsystem csrss.exe which only exchanged data with the official Console Host process (owner of console windows).
Now however, Windows API supports pseudo-consoles. You can find a complete introduction on the Microsoft Dev Blog
Windows Command-Line: Introducing the Windows Pseudo Console (ConPTY)
The important function you need (in case that link breaks) is CreatePseudoConsole supported starting with Windows 10 version 1809 (October 2018 update).
When you use CreatePseudoConsole to promote the pipes and then supply this console to CreateProcess (instead of attaching pipes to your subprocess standard I/O streams), the subprocess will detect an interactive console, can use console API functions such as AttachConsole, can open the special filenames CONIN$ etc. And the data comes to you (and from you) instead of being linked to a console window.
There's also a complete sample on GitHub.
That same blog post also discusses the workaround used by "Terminal" and "remote shell" type software prior to the addition of CreatePseudoConsole in Windows 10, namely setting up the subprocess with a detached console, hiding the associated console window, and screen-scraping the console screen buffer.
I'm creating a Windows service, which cannot have an associated console. Therefore I want to redirect stdout and stderr to a (the same) file. Here is what I discovered so far:
Redirecting cout and cerr in C++ can be done by changing the buffers, but this does not affect C I/O like puts or Windows I/O handles.
Hence we can use freopen to reopen stdout or stderr as a file like here, but we cannot specify the same file twice.
To still use the same file for both we can redirect stderr to stdout using dup2 like here.
So far so good, and when we run this code with /SUBSYSTEM:CONSOLE (project properties → Linker → System) everything works fine:
#include <Windows.h>
#include <io.h>
#include <fcntl.h>
#include <cstdio>
#include <iostream>
void doit()
{
FILE *stream;
if (_wfreopen_s(&stream, L"log.log", L"w", stdout)) __debugbreak();
// Also works as service when uncommenting this line: if (_wfreopen_s(&stream, L"log2.log", L"w", stderr)) __debugbreak();
if (_dup2(_fileno(stdout), _fileno(stderr)))
{
const auto err /*EBADF if service; hover over in debugger*/ = errno;
__debugbreak();
}
// Seemingly can be left out for console applications
if (!SetStdHandle(STD_OUTPUT_HANDLE, reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stdout))))) __debugbreak();
if (!SetStdHandle(STD_ERROR_HANDLE, reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stderr))))) __debugbreak();
if (_setmode(_fileno(stdout), _O_WTEXT) == -1) __debugbreak();
if (_setmode(_fileno(stderr), _O_WTEXT) == -1) __debugbreak();
std::wcout << L"1☺a" << std::endl;
std::wcerr << L"1☺b" << std::endl;
_putws(L"2☺a");
fflush(stdout);
fputws(L"2☺b\n", stderr);
fflush(stderr);
const std::wstring a3(L"3☺a\n"), b3(L"3☺b\n");
if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), a3.c_str(), a3.size() * sizeof(wchar_t), nullptr, nullptr))
__debugbreak();
if (!WriteFile(GetStdHandle(STD_ERROR_HANDLE), b3.c_str(), b3.size() * sizeof(wchar_t), nullptr, nullptr))
__debugbreak();
}
int main() { doit(); }
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) { return doit(), 0; }
This nicely writes the following text to log.log:
1☺a
1☺b
2☺a
2☺b
3☺a
3☺b
(Of course we want emoji, so we need some sort of unicode. In this case we use wide characters, which means we need to use setmode or else everything will mess up. You may also need to save the cpp file in an encoding that MSVC understands, e.g. UTF-8 with signature.)
But now back to the original problem: doing this as a service without console, or, equivalent but easier to debug, a GUI app (/SUBSYSTEM:WINDOWS).
The problem is that in this case dup2 fails because fileno(stderr) is not a valid file descriptor, because the app initially has no associated streams. As mentioned here, fileno(stderr) == -2 in this case.
Note that when we first open stderr as another file using freopen, everything works fine, but we created a dummy empty file.
So now my question is: what is the best way to redirect both stdout and stderr to the same file in an application which initially has no streams?
Just to recap: the problem is that when stdout or stderr is not associated with an output stream, fileno returns -2, so we cannot pass it to dup2.
(I do not want to change the code used for the actual printing, because that might mean that some output produced by external functions will not be redirected.)
Here's an example of a program that creates a file for writing and then uses CreateProcess and setting stdout and stderr for the process to the HANDLE of the created file. This example just starts itself with a dummy argument to make it write a lot of things to stdout and stderr that will be written to output.txt.
// RedirectStd.cpp
#include <iostream>
#include <string_view>
#include <vector>
#include <Windows.h>
struct SecAttrs_t : public SECURITY_ATTRIBUTES {
SecAttrs_t() : SECURITY_ATTRIBUTES{ 0 } {
nLength = sizeof(SECURITY_ATTRIBUTES);
bInheritHandle = TRUE;
}
operator SECURITY_ATTRIBUTES* () { return this; }
};
struct StartupInfo_t : public STARTUPINFO {
StartupInfo_t(HANDLE output) : STARTUPINFO{ 0 } {
cb = sizeof(STARTUPINFO);
dwFlags = STARTF_USESTDHANDLES;
hStdOutput = output;
hStdError = output;
}
operator STARTUPINFO* () { return this; }
};
int cppmain(const std::string_view program, std::vector<std::string_view> args) {
if (args.size() == 0) {
// no arguments, create a file and start a new process
SecAttrs_t sa;
HANDLE hFile = CreateFile(L"output.txt",
GENERIC_WRITE,
FILE_SHARE_READ,
sa, // lpSecurityAttributes
CREATE_ALWAYS, // dwCreationDisposition
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
NULL // dwFlagsAndAttributesparameter
);
if (hFile == INVALID_HANDLE_VALUE) return 1;
StartupInfo_t su(hFile); // set output handles to hFile
PROCESS_INFORMATION pi;
std::wstring commandline = L"RedirectStd.exe dummy";
BOOL bCreated = CreateProcess(
NULL,
commandline.data(),
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
TRUE, // bInheritHandles
0, // dwCreationFlags
NULL, // lpEnvironment
NULL, // lpCurrentDirectory
su, // lpStartupInfo
&pi
);
if (bCreated == 0) return 2;
CloseHandle(pi.hThread); // no need for this
WaitForSingleObject(pi.hProcess, INFINITE); // wait for the process to finish
CloseHandle(pi.hProcess);
CloseHandle(hFile);
}
else {
// called with an argument, output stuff to stdout and stderr
for (int i = 0; i < 1024; ++i) {
std::cout << "stdout\n";
std::cerr << "stderr\n";
}
}
return 0;
}
int main(int argc, char* argv[]) {
return cppmain(argv[0], { argv + 1, argv + argc });
}
I found a solution that works and does not create a temporary file log2.log. Instead of this file, we can open NUL (Windows's /dev/null), so the code becomes:
FILE *stream;
if (_wfreopen_s(&stream, L"log.log", L"w", stdout)) __debugbreak();
if (freopen_s(&stream, "NUL", "w", stderr)) __debugbreak();
if (_dup2(_fileno(stdout), _fileno(stderr))) __debugbreak();
if (!SetStdHandle(STD_OUTPUT_HANDLE, reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stdout))))) __debugbreak();
if (!SetStdHandle(STD_ERROR_HANDLE, reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stderr))))) __debugbreak();
if (_setmode(_fileno(stdout), _O_WTEXT) == -1) __debugbreak();
if (_setmode(_fileno(stderr), _O_WTEXT) == -1) __debugbreak();
This makes sure that _fileno(stderr) is not -2 anymore so we can use dup2.
There might be a more elegant solution (not sure), but this works and does not create a dummy empty file (also not one named NUL).
My program is designed to be a secure Sticky Keys hack. If sticky keys is invoked from logon, it will ask for a username and password to a local account. If they are correct, an instance of cmd.exe will be invoked as that user to avoid damage. When I double-click the sethc program from explorer,
It runs successfully.
When I run the same program with pressing shift five times,
It fails with error 5 Access Denied.
I can verify that sethc is run under winlogon when shift is pressed five times.
The whole file is:
// cmd.cpp : Defines the entry point for the console application.
//
#include <Windows.h>
#include <Lmcons.h>
#include <iostream>
#include <ctype.h>
#include <string>
#include <stdio.h>
#define winstring LPWSTR
#define stcas(x) static_cast<x>
#define INFO_BUFFER_SIZE 260
using namespace std;
void ReportError(LPCWSTR pszFunction, DWORD dwError = GetLastError())
{
wprintf(L"%s failed w/err 0x%08lx\n", pszFunction, dwError);
system("pause");
exit(dwError);
}
int main()
{
LPTSTR szCmdline[] = {"cmd"};
STARTUPINFOW si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
TCHAR un[UNLEN+1];
DWORD size = UNLEN + 1;
GetUserName(un, &size);
string una(un);
bool sys = !una.compare("SYSTEM");
if(!sys) {
system("cls");
if(!CreateProcessW(L"C:\\Windows\\System32\\cmd.exe", NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi)) ReportError(L"normal Create process");
return 0;
}
wchar_t szUserName[INFO_BUFFER_SIZE] = {};
wchar_t szPassword[INFO_BUFFER_SIZE] = {};
wchar_t *pc = NULL;
HANDLE hToken = NULL;
HANDLE aToken = NULL;
BOOL dup = FALSE;
BOOL logon = FALSE;
printf("Enter the username: ");
fgetws(szUserName, ARRAYSIZE(szUserName), stdin);
pc = wcschr(szUserName, '\n');
if (pc != NULL) *pc = '\0'; // Remove the trailing L'\n'
cout << endl;
printf("Enter the password: ");
fgetws(szPassword, ARRAYSIZE(szPassword), stdin);
pc = wcschr(szPassword, '\n');
if (pc != NULL) *pc = '\0'; // Remove the trailing L'\n'
if(!LogonUserW(szUserName, NULL, szPassword, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken)) ReportError(L"Logon");
else logon = TRUE;
if(!DuplicateTokenEx(hToken, TOKEN_DUPLICATE | TOKEN_IMPERSONATE, NULL, SecurityImpersonation, TokenPrimary, &aToken)) ReportError(L"Impersonate");
else dup = TRUE;
if(!CreateProcessAsUserW(aToken,L"C:\\Windows\\System32\\cmd.exe", NULL, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, L"C:\\Windows\\System32\\", &si, &pi)){
ReportError(L"Create Process");
}
SecureZeroMemory(szPassword, sizeof(szPassword));
}
I would like to know why sethc is not allowed CreateProcess at all if it is derived from winlogon. I am running Windows 7. The weird thing is that system(command) works fine. I use system("pause"); once.
While it is difficult to say what your question actually is, be aware that there is a specific privilege called PROCESS_CREATE_PROCESS.
From MSDN:
PROCESS_CREATE_PROCESS (0x0080) Required to create a process.
A process lacking this privilege can't spawn child processes. system("pause"); does not create a child process, because pause is a built-in shell command, but I understand why you might have got that impression.
So check if your process has that privilege. Use process explorer or simply a WINAPI function, there's bound to be one.
If you want to know why Microsoft developers decided not to give that privilege to sethc, you'd have to ask them.
The code, sory it is abit too long but I've managed it to shorten it only to such size, the key issue is (I think) with this strange for loop at the end. No, I don't know why the loop header is empty, microsoft want's it that way.
The problem is that the code waits to eternity for yet more data from child app.
The page with full algorighm: http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx
(Yes, I know it's a mess, but it is self sustained mess at least.)
#include <iostream>
#include <stdio.h>
#include <windows.h>
using namespace std;
#define BUFSIZE 4096
int main() {
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0);
// Ensure the read handle to the pipe for STDOUT is not inherited.
SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0);
// Create a pipe for the child process's STDIN.
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0);
// Ensure the write handle to the pipe for STDIN is not inherited.
SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0);
// Create the child process.
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
char szCmdline[]="cmd /c dir";
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bCreateSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bCreateSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bWriteSuccess = FALSE;
BOOL bReadSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;) {
bReadSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bReadSuccess || dwRead == 0 ) break;
bReadSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
if (! bReadSuccess ) break;
}
printf("\n->End of parent execution.\n");
return 0;
}
From the looks of things, you've forgotten to close the parent's handles to the write-end of the pipes you're passing to the child process. Since there's still a valid write handle to the pipe, the system can't detect that writing to the pipe is no longer possible, and you'll wait infinitely for the child to finish.
If you only need to capture the child's standard output, _popen may be a lot easier way to do it.
Edit: Okay, some ancient code to spawn a child process with all three of its standard streams directed to pipes that connect to the parent. This is a lot longer than it should be for such a simple task, but such is life with the Windows API. To be fair, it probably could be shorter, but it's 20 years old (or so). Neither the API nor the way I wrote code then is quite what it is now (though some might not consider my newer code any improvement).
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <ctype.h>
#include <io.h>
#include <fcntl.h>
#include <stdlib.h>
#include "spawn.h"
static void system_error(char const *name) {
// A function to retrieve, format, and print out a message from the
// last error. The `name' that's passed should be in the form of a
// present tense noun (phrase) such as "opening file".
//
char *ptr = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
0,
GetLastError(),
0,
(char *)&ptr,
1024,
NULL);
fprintf(stderr, "%s\n", ptr);
LocalFree(ptr);
}
static void InitializeInheritableSA(SECURITY_ATTRIBUTES *sa) {
sa->nLength = sizeof *sa;
sa->bInheritHandle = TRUE;
sa->lpSecurityDescriptor = NULL;
}
static HANDLE OpenInheritableFile(char const *name) {
SECURITY_ATTRIBUTES sa;
HANDLE retval;
InitializeInheritableSA(&sa);
retval = CreateFile(
name,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (INVALID_HANDLE_VALUE == retval) {
char buffer[100];
sprintf(buffer, "opening file %s", name);
system_error(buffer);
return retval;
}
}
static HANDLE CreateInheritableFile(char const *name, int mode) {
SECURITY_ATTRIBUTES sa;
HANDLE retval;
DWORD FSmode = mode ? OPEN_ALWAYS : CREATE_NEW;
InitializeInheritableSA(&sa);
retval = CreateFile(
name,
GENERIC_WRITE,
FILE_SHARE_READ,
&sa,
FSmode,
FILE_ATTRIBUTE_NORMAL,
0);
if (INVALID_HANDLE_VALUE == retval) {
char buffer[100];
sprintf(buffer, "creating file %s", name);
system_error(buffer);
return retval;
}
if ( mode == APPEND )
SetFilePointer(retval, 0, 0, FILE_END);
}
enum inheritance { inherit_read = 1, inherit_write = 2 };
static BOOL CreateInheritablePipe(HANDLE *read, HANDLE *write, int inheritance) {
SECURITY_ATTRIBUTES sa;
InitializeInheritableSA(&sa);
if ( !CreatePipe(read, write, &sa, 0)) {
system_error("Creating pipe");
return FALSE;
}
if (!inheritance & inherit_read)
DuplicateHandle(
GetCurrentProcess(),
*read,
GetCurrentProcess(),
NULL,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
if (!inheritance & inherit_write)
DuplicateHandle(
GetCurrentProcess(),
*write,
GetCurrentProcess(),
NULL,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
return TRUE;
}
static BOOL find_image(char const *name, char *buffer) {
// Try to find an image file named by the user.
// First search for the exact file name in the current
// directory. If that's found, look for same base name
// with ".com", ".exe" and ".bat" appended, in that order.
// If we can't find it in the current directory, repeat
// the entire process on directories specified in the
// PATH environment variable.
//
#define elements(array) (sizeof(array)/sizeof(array[0]))
static char *extensions[] = {".com", ".exe", ".bat", ".cmd"};
int i;
char temp[FILENAME_MAX];
if (-1 != access(name, 0)) {
strcpy(buffer, name);
return TRUE;
}
for (i=0; i<elements(extensions); i++) {
strcpy(temp, name);
strcat(temp, extensions[i]);
if ( -1 != access(temp, 0)) {
strcpy(buffer, temp);
return TRUE;
}
}
_searchenv(name, "PATH", buffer);
if ( buffer[0] != '\0')
return TRUE;
for ( i=0; i<elements(extensions); i++) {
strcpy(temp, name);
strcat(temp, extensions[i]);
_searchenv(temp, "PATH", buffer);
if ( buffer[0] != '\0')
return TRUE;
}
return FALSE;
}
static HANDLE DetachProcess(char const *name, HANDLE const *streams) {
STARTUPINFO s;
PROCESS_INFORMATION p;
char buffer[FILENAME_MAX];
memset(&s, 0, sizeof s);
s.cb = sizeof(s);
s.dwFlags = STARTF_USESTDHANDLES;
s.hStdInput = streams[0];
s.hStdOutput = streams[1];
s.hStdError = streams[2];
if ( !find_image(name, buffer)) {
system_error("Finding Image file");
return INVALID_HANDLE_VALUE;
}
// Since we've redirected the standard input, output and error handles
// of the child process, we create it without a console of its own.
// (That's the `DETACHED_PROCESS' part of the call.) Other
// possibilities include passing 0 so the child inherits our console,
// or passing CREATE_NEW_CONSOLE so the child gets a console of its
// own.
//
if (!CreateProcess(
NULL,
buffer, NULL, NULL,
TRUE,
DETACHED_PROCESS,
NULL, NULL,
&s,
&p))
{
system_error("Spawning program");
return INVALID_HANDLE_VALUE;
}
// Since we don't need the handle to the child's thread, close it to
// save some resources.
CloseHandle(p.hThread);
return p.hProcess;
}
static HANDLE StartStreamHandler(ThrdProc proc, HANDLE stream) {
DWORD ignore;
return CreateThread(
NULL,
0,
proc,
(void *)stream,
0,
&ignore);
}
HANDLE CreateDetachedProcess(char const *name, stream_info *streams) {
// This Creates a detached process.
// First parameter: name of process to start.
// Second parameter: names of files to redirect the standard input, output and error
// streams of the child to (in that order.) Any file name that is NULL will be
// redirected to an anonymous pipe connected to the parent.
// Third Parameter: handles of the anonymous pipe(s) for the standard input, output
// and/or error streams of the new child process.
//
// Return value: a handle to the newly created process.
//
HANDLE child_handles[3];
HANDLE process;
int i;
// First handle the child's standard input. This is separate from the
// standard output and standard error because it's going the opposite
// direction. Basically, we create either a handle to a file the child
// will use, or else a pipe so the child can communicate with us.
//
if ( streams[0].filename != NULL ) {
streams[0].handle = NULL;
child_handles[0] = OpenInheritableFile(streams[0].filename);
}
else
CreateInheritablePipe(child_handles, &(streams[0].handle), inherit_read);
// Now handle the child's standard output and standard error streams. These
// are separate from the code above simply because they go in the opposite
// direction.
//
for ( i=1; i<3; i++)
if ( streams[i].filename != NULL) {
streams[i].handle = NULL;
child_handles[i] = CreateInheritableFile(streams[i].filename, APPEND);
}
else
CreateInheritablePipe(&(streams[i].handle), child_handles+i, inherit_write);
// Now that we've set up the pipes and/or files the child's going to use,
// we're ready to actually start up the child process:
process = DetachProcess(name, child_handles);
if (INVALID_HANDLE_VALUE == process)
return process;
// Now that we've started the child, we close our handles to its ends of the pipes.
// If one or more of these happens to a handle to a file instead, it doesn't really
// need to be closed, but it doesn't hurt either. However, with the child's standard
// output and standard error streams, it's CRUCIAL to close our handles if either is a
// handle to a pipe. The system detects the end of data on a pipe when ALL handles to
// the write end of the pipe are closed -- if we still have an open handle to the
// write end of one of these pipes, we won't be able to detect when the child is done
// writing to the pipe.
//
for ( i=0; i<3; i++) {
CloseHandle(child_handles[i]);
if ( streams[i].handler )
streams[i].handle =
StartStreamHandler(streams[i].handler, streams[i].handle);
}
return process;
}
#ifdef TEST
#define buf_size 256
unsigned long __stdcall handle_error(void *pipe) {
// The control (and only) function for a thread handling the standard
// error from the child process. We'll handle it by displaying a
// message box each time we receive data on the standard error stream.
//
char buffer[buf_size];
HANDLE child_error_rd = (HANDLE)pipe;
unsigned bytes;
while (ERROR_BROKEN_PIPE != GetLastError() &&
ReadFile(child_error_rd, buffer, 256, &bytes, NULL))
{
buffer[bytes+1] = '\0';
MessageBox(NULL, buffer, "Error", MB_OK);
}
return 0;
}
unsigned long __stdcall handle_output(void *pipe) {
// A similar thread function to handle standard output from the child
// process. Nothing special is done with the output - it's simply
// displayed in our console. However, just for fun it opens a C high-
// level FILE * for the handle, and uses fgets to read it. As
// expected, fgets detects the broken pipe as the end of the file.
//
char buffer[buf_size];
int handle;
FILE *file;
handle = _open_osfhandle((long)pipe, _O_RDONLY | _O_BINARY);
file = _fdopen(handle, "r");
if ( NULL == file )
return 1;
while ( fgets(buffer, buf_size, file))
printf("%s", buffer);
return 0;
}
int main(int argc, char **argv) {
stream_info streams[3];
HANDLE handles[3];
int i;
if ( argc < 3 ) {
fputs("Usage: spawn prog datafile"
"\nwhich will spawn `prog' with its standard input set to"
"\nread from `datafile'. Then `prog's standard output"
"\nwill be captured and printed. If `prog' writes to its"
"\nstandard error, that output will be displayed in a"
"\nMessageBox.\n",
stderr);
return 1;
}
memset(streams, 0, sizeof(streams));
streams[0].filename = argv[2];
streams[1].handler = handle_output;
streams[2].handler = handle_error;
handles[0] = CreateDetachedProcess(argv[1], streams);
handles[1] = streams[1].handle;
handles[2] = streams[2].handle;
WaitForMultipleObjects(3, handles, TRUE, INFINITE);
for ( i=0; i<3; i++)
CloseHandle(handles[i]);
return 0;
}
#endif
How do I get the process name from a PID using C++ in Windows?
I guess the OpenProcess function should help, given that your process possesses the necessary rights. Once you obtain a handle to the process, you can use the GetModuleFileNameEx function to obtain full path (path to the .exe file) of the process.
#include "stdafx.h"
#include "windows.h"
#include "tchar.h"
#include "stdio.h"
#include "psapi.h"
// Important: Must include psapi.lib in additional dependencies section
// In VS2005... Project > Project Properties > Configuration Properties > Linker > Input > Additional Dependencies
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE Handle = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE,
8036 /* This is the PID, you can find one from windows task manager */
);
if (Handle)
{
TCHAR Buffer[MAX_PATH];
if (GetModuleFileNameEx(Handle, 0, Buffer, MAX_PATH))
{
// At this point, buffer contains the full path to the executable
}
else
{
// You better call GetLastError() here
}
CloseHandle(Handle);
}
return 0;
}
You can obtain the process name by using the WIN32 API GetModuleBaseName after having the process handle. You can get the process handle by using OpenProcess.
To get the executable name you can also use GetProcessImageFileName.
All the above methods require psapi.dll to be loaded (Read the remarks section) and iterating through process snapshot is an option one should not even consider for getting a name of the executable file from an efficiency standpoint.
The best approach, even according to MSDN recommendation, is to use QueryFullProcessImageName.
std::string ProcessIdToName(DWORD processId)
{
std::string ret;
HANDLE handle = OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION,
FALSE,
processId /* This is the PID, you can find one from windows task manager */
);
if (handle)
{
DWORD buffSize = 1024;
CHAR buffer[1024];
if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize))
{
ret = buffer;
}
else
{
printf("Error GetModuleBaseNameA : %lu", GetLastError());
}
CloseHandle(handle);
}
else
{
printf("Error OpenProcess : %lu", GetLastError());
}
return ret;
}
If you are trying to get the executable image name of a given process, take a look at GetModuleFileName.
Check out the enumprocess functions in the tool help library:
http://msdn.microsoft.com/en-us/library/ms682629(v=vs.85).aspx
Good example # http://msdn.microsoft.com/en-us/library/ms682623(v=vs.85).aspx
Try this function :
std::wstring GetProcName(DWORD aPid)
{
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE)
{
std::wcout << "can't get a process snapshot ";
return 0;
}
for(BOOL bok =Process32First(processesSnapshot, &processInfo);bok; bok = Process32Next(processesSnapshot, &processInfo))
{
if( aPid == processInfo.th32ProcessID)
{
std::wcout << "found running process: " << processInfo.szExeFile;
CloseHandle(processesSnapshot);
return processInfo.szExeFile;
}
}
std::wcout << "no process with given pid" << aPid;
CloseHandle(processesSnapshot);
return std::wstring();
}