Reputation: 106609
For the life of me I can't figure out why this is not working. Basically, I created the pipe with the inherit bit set to true, and created two child processes, and used the STARTUPINFO structure to set the input and output handles as one would need, but the pipe seems broken (the second process writes no output to the console, even though output is expected)
I know the problem does not lie in my test program (BitTwiddler.exe
) because I performed the same operation using CMD.exe, and everything works as expected.
Below is a minimal reproduction of what I have. What have I done incorrectly?
#include "windows.h"
int main()
{
PROCESS_INFORMATION piSource, piDest;
HANDLE hPipeIn, hPipeOut;
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
STARTUPINFOW suSource, suDest;
ZeroMemory(&suSource, sizeof(suSource));
ZeroMemory(&suDest, sizeof(suDest));
suSource.cb = suDest.cb = sizeof(STARTUPINFOW);
suSource.dwFlags = suDest.dwFlags = STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = TRUE;
if (CreatePipe(&hPipeIn, &hPipeOut, &sa, 0) == 0)
{
return GetLastError();
}
suSource.hStdInput = hIn;
suSource.hStdError = suSource.hStdOutput = hPipeIn;
suDest.hStdInput = hPipeOut;
suDest.hStdError = suDest.hStdOutput = hOut;
std::wstring cmdLineA(L"BitTwiddler 1"), cmdLineB(L"BitTwiddler 0");
cmdLineA.push_back(0); cmdLineB.push_back(0);
if (CreateProcessW(0, &cmdLineA[0], 0, 0, TRUE, 0, 0, 0, &suSource, &piSource) == 0)
{
return GetLastError();
}
CloseHandle(piSource.hThread);
if (CreateProcessW(0, &cmdLineB[0], 0, 0, TRUE, 0, 0, 0, &suDest, &piDest) == 0)
{
return GetLastError();
}
CloseHandle(piDest.hThread);
HANDLE hArray[2];
hArray[0] = piSource.hProcess;
hArray[1] = piDest.hProcess;
WaitForMultipleObjects(2, hArray, TRUE, INFINITE);
CloseHandle(hArray[0]);
CloseHandle(hArray[1]);
return 0;
}
(In case anyone's interested, BitTwiddler is:
#include <windows.h>
#include <sstream>
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
std::size_t opt = 0;
argc--; argv++;
if (argc == 0)
{
return 0;
}
else
{
std::istringstream converter(*argv);
converter >> opt;
}
switch(opt)
{
case 0:
{
std::wstring currentLine;
while(std::getline(std::wcin, currentLine))
{
std::wcout << "Got somepin: " << currentLine << std::endl;
}
}
break;
case 1:
for (;;)
{
std::wcout << L"Hello World!" << std::endl;
Sleep(1000);
}
break;
case 2:
return -1;
default:
std::wcout << "Unknown option.";
return 0;
}
return 0;
}
), but I really don't think that matters.
Upvotes: 3
Views: 2613
Reputation: 420
I upvoted your question as it is a very important one. Since I learnt Windows programming from FamTrinli's demos years ago, CreateProcess has been one of the harddest functions of the Win32 API. If not, the harddest.
CreateProcess has alot of arguments. Each argument requires careful reading of the documentation to properly use. Alot of arguments are complicated structures; such as SECURITY_ATTRIBUTES and STARTUPINFO. The names for the HANDLE(s) in STARTUPINFO structure can be confusing. As you were confused. The steps that are involved in using CreateProcess. whether or not you need to immediately close the handles(nb: you don't). And the actual communication between the processes can cause deadlocks.
After using CreateProcess for years I did an extensive research and testing of the API. I then designed a library that encapsulates all that knowledge. This library is apart of my Jav/win32 library. This library is similar to Java's Process class.
I am not able to post every last bit of source code here. If you are interested in the source code I can email it to you. Keeping Cpp a goto for programming.
Jav/win32/Process.h
#ifndef JAV_WIN32_PROCESS_HPP
#define JAV_WIN32_PROCESS_HPP
#include <Jav/File.h>
#include <Jav/string/cstring.h>
#include <Jav/error/error.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/container/flat_map.hpp>
#include <sstream>
#include <Jav/win32/win32.h>
namespace Jav { namespace win32 {
/** class Process makes handling win32 CreateProcess a bit easier.
Parent Process refers to the process that is calling CreateProcess.
Child Process refers to the process being created.
Note: CreateProcess uses the parent process's CurrentDirectory and
EnvironmentVariable when deducing relative filepaths used in AppName and CommandLine String.
Thus I have no idea the purpose of passing a current directory and environment to the CreateProcess.
Note: Rather than using Process::setCurrentDir and Process::addEnv and Process::addParentEnv; which
seems to make no sense at all as CreateProcess doesn't use these settings to deduce relative filepaths
passed to CreateProcess; instead use Process::addPathArg to pass absolute filepaths on the commandline.
Note: Using Process::addEnv or Process::addParentEnv will temporarily adjust parent process Environment,
and pass the adjusted Environment to the child process as well. This allows you to set Path variable
so that CreateProcess deduces relative paths. However using Process::addPath is probably better.
Note: Asynchronous read and write functions are not implemented.
Probably synchronous read and writes are good enough.
To properly read and write the child process, be sure to know when it makes read and write request.
If you sync read a process that never writes to stdout or stderror your program will deadlock.
If you write to a process that didn't read stdinput that makes no sense. You are just filling up the buffer.
Also ensure to read out data written by child process so that the buffer is not clogged up causing deadlock.
*/
class ProcessA
{
public:
enum { INVALID_EXIT_CODE = -1 };
enum { AUTO_KILL=1, WAIT=2, DEBUG=4, ADD_PARENT_ENV=8 };
struct Builder
{
struct ICompare { bool operator()(const std::string &l,const std::string &r)const; };
using EnvList = boost::container::flat_map<std::string,std::ostringstream,ICompare>;
std::ostringstream cmd;
EnvList env_list;
Jav::cstring app_name;
Jav::cstring current_dir;
STARTUPINFOA si = {sizeof(STARTUPINFOA)};
};
public:
ProcessA(const char *cmd=NULL,const char *current_dir=NULL,const char *app_name=NULL);
~ProcessA();
public:
bool launch();
int stop();
int wait(uint time=INFINITE);
void pause();
void resume();
bool isOpen();
bool isValid() { return info.hProcess; }
size_t getMsgSize();
size_t getErrorMsgSize();
size_t read(void*,size_t);
size_t readError(void*,size_t);
size_t write(const void*,size_t);
size_t write(const char*);
bool read_async(void*,size_t);
bool readError_async(void*,size_t);
bool write_async(const void*,size_t);
bool write_async(const char*);
/** Set up process for creation */
void setAppName(const char*);
void setCurrentDir(const char*);
void addArg(const char*);
void addQuotedArg(const char*);
void addPathArg(const char *parent_dir,const char *child_name);
void addEnv(const char *var,const char *val);
bool addParentEnv();
template<class ...ARGS>
void addArgEx(const char*,ARGS ...rest);
void addArgEx(const char*);
void setConsoleTitle(const char*);
void setConsoleWidth(int);
void setConsoleHeight(int);
void setConsoleTextAndFillColor(int);
void setWindowVisibility(int); //SW_SHOW, SW_HIDE, etc
void setWindowXpos(int);
void setWindowYpos(int);
void setWindowWidth(int);
void setWindowHeight(int);
void setWaitTime(uint);
void setParentMode(uint);
void addParentMode(uint);
void removeParentMode(uint);
Jav::cstring toString();
Jav::cstring getError() { return error; }
private:
Jav::cstring buildEnvironment();
Jav::cstring getCombinedEnvVar(const std::string &name);
private:
PROCESS_INFORMATION info = {};
Jav::rFile m_read_end;
Jav::rFile m_error_end;
Jav::wFile m_write_end;
OVERLAPPED async_struct;
uint flags = 0;
uint waitTime = INFINITE;
Builder *builder;
Jav::Error error = "";
};
Jav::cstring getEnvVarA(const char *name);
Jav::cstring FindFileExtExeA(const char *ext);
Jav::cstring FindFileNameExeA(const char *fname);
void setEnvironmentStringsA(const char *env);
class ProcessW
{
public:
enum { INVALID_EXIT_CODE = -1 };
enum { AUTO_KILL=1, WAIT=2, DEBUG=4, ADD_PARENT_ENV=8 };
struct Builder
{
struct ICompare { bool operator()(const std::wstring &l,const std::wstring &r)const; };
using EnvList = boost::container::flat_map<std::wstring,std::wostringstream,ICompare>;
std::wostringstream cmd;
EnvList env_list;
Jav::cstringw app_name;
Jav::cstringw current_dir;
STARTUPINFOW si = {sizeof(STARTUPINFOW)};
};
public:
ProcessW(const wchar_t *cmd=NULL,const wchar_t *current_dir=NULL,const wchar_t *app_name=NULL);
~ProcessW();
public:
bool launch();
int stop();
int wait(uint time=INFINITE);
void pause();
void resume();
bool isOpen();
bool isValid() { return info.hProcess; }
size_t getMsgSize();
size_t getErrorMsgSize();
size_t read(void*,size_t);
size_t readError(void*,size_t);
size_t write(const void*,size_t);
size_t write(const wchar_t*);
size_t write(const char*);
size_t read_async(void*,size_t);
size_t readError_async(void*,size_t);
size_t write_async(const void*,size_t);
size_t write_async(const wchar_t*);
size_t write_async(const char*);
void setAppName(const wchar_t*);
void setCurrentDir(const wchar_t*);
void addArg(const wchar_t*);
void addQuotedArg(const wchar_t*);
void addPathArg(const wchar_t *parent_dir,const wchar_t *child_name);
void addEnv(const wchar_t *var,const wchar_t *val);
bool addParentEnv();
void setAppName(const char*);
void setCurrentDir(const char*);
void addArg(const char*);
void addQuotedArg(const char*);
void addPathArg(const char *parent_dir,const char *child_name);
void addEnv(const char *var,const char *val);
template<class ...ARGS>
void addArgEx(const wchar_t*,ARGS ...rest);
template<class ...ARGS>
void addArgEx(const char*,ARGS ...rest);
void addArgEx(const wchar_t*);
void addArgEx(const char*);
void setConsoleTitle(const wchar_t*);
void setConsoleTitle(const char*);
void setConsoleWidth(int);
void setConsoleHeight(int);
void setConsoleTextAndFillColor(int);
void setWindowVisibility(int);
void setWindowXpos(int);
void setWindowYpos(int);
void setWindowWidth(int);
void setWindowHeight(int);
void setWaitTime(uint);
void setParentMode(uint);
void addParentMode(uint);
void removeParentMode(uint);
Jav::cstringw toString();
Jav::cstring getError() { return error; }
private:
Jav::cstringw buildEnvironment();
Jav::cstringw getCombinedEnvVar(const std::wstring &name);\
Jav::cstringw towstring(const char *s);
private:
PROCESS_INFORMATION info = {};
Jav::rFile m_read_end;
Jav::rFile m_error_end;
Jav::wFile m_write_end;
OVERLAPPED async_struct;
uint flags = 0;
uint waitTime = INFINITE;
Builder *builder;
Jav::Error error = "";
};
Jav::cstringw getEnvVarW(const wchar_t *name);
Jav::cstringw findFileExtExeW(const wchar_t *ext);
Jav::cstringw findFileNameExeW(const wchar_t *fname);
void setEnvironmentStringsW(const wchar_t *env);
///________________///___________
template<class ...ARGS>
void ProcessA::addArgEx(const char *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << first;
addArgEx(rest...);
}
inline void ProcessA::addArgEx(const char *s)
{
builder->cmd << ' ' << s;
}
template<class ...ARGS>
void ProcessW::addArgEx(const wchar_t *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << first;
addArgEx(rest...);
}
template<class ...ARGS>
void ProcessW::addArgEx(const char *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << towstring(first);
addArgEx(rest...);
}
inline void ProcessW::addArgEx(const wchar_t *s)
{
builder->cmd << ' ' << s;
}
inline void ProcessW::addArgEx(const char *s)
{
builder->cmd << ' ' << towstring(s);
}
}}
#endif // JAV_WIN32_PROCESS_HPP
src/Jav/win32/Process.cpp
#include <Jav/error/error.h>
#include <Jav/string.h>
#include <Jav/win32/Process.h>
#include <Jav/win32/debug.h>
#include <Jav/win32/registry.h>
#include <shlwapi.h>
namespace Jav { namespace win32 {
ProcessA::ProcessA(const char *cmd,const char *current_dir,const char *app_name)
: builder(new Builder())
{
if(cmd) builder->cmd << cmd;
builder->app_name = app_name;
builder->current_dir = current_dir;
}
ProcessA::~ProcessA()
{
stop();
delete builder;
}
bool ProcessA::launch()
{
Jav::rFile child_read_end;
Jav::wFile child_error_end;
Jav::wFile child_write_end;
if(!Jav::createPipe(m_read_end,child_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
//Jav::createPipe(m_error_end,child_error_end);
if(!Jav::createPipe(child_read_end,m_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
builder->si.dwFlags |= STARTF_USESTDHANDLES;
builder->si.hStdInput = (HANDLE)child_read_end;
builder->si.hStdOutput = (HANDLE)child_write_end;
builder->si.hStdError = (HANDLE)child_write_end;
auto env = buildEnvironment();
auto cmd = builder->cmd.str();
if(flags & ADD_PARENT_ENV)
{
auto parent_env = GetEnvironmentStringsA();
for(auto &elem : builder->env_list)
{
auto val = getCombinedEnvVar(elem.first);
SetEnvironmentVariableA(elem.first.c_str(),val);
}
auto child_env = GetEnvironmentStringsA();
if( !CreateProcessA(builder->app_name,&cmd[0],NULL,NULL,true,0,child_env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
setEnvironmentStringsA(parent_env);
FreeEnvironmentStringsA(child_env);
FreeEnvironmentStringsA(parent_env);
}
else
{
if( !CreateProcessA(builder->app_name,&cmd[0],NULL,NULL,true,0,env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
}
if( !(flags & DEBUG) )
{
delete builder;
builder = NULL;
}
/*
if( WaitForInputIdle(info.hProcess,INFINITE) == WAIT_FAILED)
{
error = Jav::DefferedError("Error: Failed to wait for input idle\n");
return false;
}
if( WaitForSingleObject(info.hProcess,INFINITE) == WAIT_FAILED)
{
error = Jav::InternalError("InternalError: Failed to wait for object\n");
return false;
}
*/
return true;
}
int ProcessA::wait(uint time)
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
int ProcessA::stop()
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
if(flags & WAIT) WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
void ProcessA::pause()
{
SuspendThread(info.hThread);
}
void ProcessA::resume()
{
ResumeThread(info.hThread);
}
bool ProcessA::isOpen()
{
if(!info.hProcess) { return false; }
/*
DWORD exitCode;
if(!GetExitCodeProcess(info.hProcess,&exitCode))
throw Jav::InternalError("InternalError: Failed to GetExitCodeProcess\n");
return exitCode == STILL_ACTIVE;
*/
switch(WaitForSingleObject(info.hProcess,0))
{
case WAIT_TIMEOUT: return true;
case WAIT_FAILED: throw Jav::Error("Error: Failed to wait for object\n");
case WAIT_ABANDONED: throw Jav::Error("Error: wait abondoned\n");
case WAIT_OBJECT_0: rep("WAIT_OBJECT_0"); return false;
default: throw Jav::Error("Error: Invalid Error Code, %s\n",__func__);
}
}
size_t ProcessA::getMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_read_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessA::getErrorMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_error_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessA::read(void *buf,size_t sz)
{
return m_read_end.read(buf,sz);
}
size_t ProcessA::readError(void *buf,size_t sz)
{
return m_error_end.read(buf,sz);
}
size_t ProcessA::write(const void *data,size_t sz)
{
return m_write_end.write(data,sz);
}
size_t ProcessA::write(const char *data)
{
return m_write_end.write(data);
}
void ProcessA::setAppName(const char *name)
{
builder->app_name = name;
}
void ProcessA::setCurrentDir(const char *dir)
{
builder->current_dir = dir;
}
void ProcessA::addArg(const char *arg)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << arg;
}
void ProcessA::addQuotedArg(const char *arg)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << '\"' << arg << '\"';
}
void ProcessA::addPathArg(const char *parent_dir,const char *child_name)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << '\"'<< parent_dir << '\\' << child_name << '\"';
}
void ProcessA::addEnv(const char *var,const char *val)
{
auto &stream = builder->env_list[var];
llong pos = stream.tellp();
pos == 0 ? stream << val : stream << ';' << val;
}
/*
void ProcessA::addEnv(const char *var,const char *val)
{
auto &stream = builder->env_list[var];
llong pos = stream.tellp();
if(pos == 0) stream << '\"' << val << '\"';
else
{
stream.seekp(pos-1);
stream << ';' << val << '\"';
}
}
*/
bool ProcessA::addParentEnv()
{
flags |= ADD_PARENT_ENV;
}
/*
bool ProcessA::addParentEnv()
{
LPCH parent_env = GetEnvironmentStringsA();
if(parent_env == NULL)
{
error = Jav::Error("Error: GetEnvironmentStrings failed\n");
FreeEnvironmentStringsA(parent_env);
return false;
}
for(auto it = Jav::MultiStringIterator(parent_env); it.has(); it.next())
{
auto var_end = Jav::nextPosOf('=',it.get()).it;
if(!var_end)
{
error = Jav::Error("Error: Invalid EnvironmentString\n");
FreeEnvironmentStringsA(parent_env);
return false;
}
Jav::cstring var(it.get(),var_end++);
addEnv(var,var_end);
}
FreeEnvironmentStringsA(parent_env);
return true;
}
*/
void ProcessA::setConsoleTitle(const char *title)
{
builder->si.lpTitle = (char*)title;
}
void ProcessA::setConsoleWidth(int w)
{
builder->si.dwFlags |= STARTF_USECOUNTCHARS;
builder->si.dwXCountChars = w;
}
void ProcessA::setConsoleHeight(int h)
{
builder->si.dwFlags |= STARTF_USECOUNTCHARS;
builder->si.dwYCountChars = h;
}
void ProcessA::setConsoleTextAndFillColor(int color)
{
builder->si.dwFlags |= STARTF_USEFILLATTRIBUTE;
builder->si.dwFillAttribute = color;
}
void ProcessA::setWindowVisibility(int visibility)
{
builder->si.dwFlags |= STARTF_USESHOWWINDOW;
builder->si.wShowWindow = visibility;
}
void ProcessA::setWindowXpos(int x)
{
builder->si.dwFlags |= STARTF_USEPOSITION;
builder->si.dwX = x;
}
void ProcessA::setWindowYpos(int y)
{
builder->si.dwFlags |= STARTF_USEPOSITION;
builder->si.dwY = y;
}
void ProcessA::setWindowWidth(int w)
{
builder->si.dwFlags |= STARTF_USESIZE;
builder->si.dwXSize = w;
}
void ProcessA::setWindowHeight(int h)
{
builder->si.dwFlags |= STARTF_USESIZE;
builder->si.dwYSize = h;
}
void ProcessA::setWaitTime(uint time)
{
waitTime = time;
}
void ProcessA::setParentMode(uint mode)
{
flags = mode;
}
void ProcessA::addParentMode(uint mode)
{
Jav::bitOn(flags,mode);
}
void ProcessA::removeParentMode(uint mode)
{
Jav::bitOff(flags,mode);
}
Jav::cstring ProcessA::toString()
{
if(!builder) return "NOTHING TO DEBUG";
std::ostringstream s;
auto env = buildEnvironment();
s << "AppName: " << builder->app_name << '\n';
if(env) s << "Env: " << Jav::MultiString{env} << '\n';
else s << "Env: " << "NO ENVIRONMENT" << '\n';
s << "CurrentDir: " << builder->current_dir << '\n';
s << "cmd: " << builder->cmd.str() << '\n';
return s.str().c_str();
}
Jav::cstring ProcessA::buildEnvironment()
{
if(!builder->env_list.size()) return NULL;
std::string env;
for(auto &elem : builder->env_list)
{
env += elem.first + '=' + elem.second.str() + '\0';
}
env += '\0';
return Jav::cstring(&env[0],&env[env.size()]);
}
Jav::cstring ProcessA::getCombinedEnvVar(const std::string &name)
{
Jav::cstring parent_val;
std::string child_val;
auto elem = builder->env_list.find(name);
if(elem != builder->env_list.end()) child_val = elem->second.str();
SetLastError(0);
auto sz = GetEnvironmentVariableA(name.c_str(),NULL,0);
if(sz == 0)
{
parent_val = GetLastError() ? NULL : "";
return child_val.empty() ? parent_val : Jav::cstring(child_val.c_str());
}
GetEnvironmentVariableA(name.c_str(),parent_val,sz);
if(child_val.empty()) return parent_val;
Jav::cstring val( strlen(parent_val + child_val.size() + 2) );
sprintf(val,"%s;%s",parent_val.str(),child_val.c_str());
return val;
}
bool ProcessA::Builder::ICompare::operator()(const std::string &l,const std::string &r)const
{
return boost::ilexicographical_compare<std::string,std::string>(l,r);
}
Jav::cstring getEnvVarA(const char *name)
{
SetLastError(0);
auto sz = GetEnvironmentVariableA(name,NULL,0);
if(sz == 0) return GetLastError() ? NULL : "";
Jav::cstring val(sz);
GetEnvironmentVariableA(name,val,sz);
return val;
}
Jav::cstring FindFileNameExe(const char *fname)
{
Jav::cstring exe_name(MAX_PATH);
auto e = (int)FindExecutableA(fname,NULL,exe_name);
if(e <= 32) throw Jav::Error("Error: unable to find association\n");
return exe_name;
}
/*
Jav::cstring FindFileExtExe(const char *ext)
{
Jav::cstring exe_name;
Jav::win32::RegistryKeyReader key(".cpp",HKEY_CLASSES_ROOT);
if(!key || !key.getString(NULL,exe_name))
throw Jav::DefferedError("Error: No program Associated with file");
return exe_name;
}
*/
/* Note uses AssocQueryString which doesn't seem to be compatible on winxp*/
Jav::cstring FindFileExtExe(const char *ext)
{
DWORD sz=0;
auto hr = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN,ASSOCSTR_EXECUTABLE,ext,NULL,NULL,&sz);
if( hr != S_FALSE)
throw Jav::InternalError("Error Failed to obtain extension association name length\n");
Jav::cstring exe_name(sz);
hr = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN,ASSOCSTR_EXECUTABLE,ext,NULL,exe_name,&sz);
if( hr != S_OK)
throw Jav::InternalError("Error Failed to obtain extension association\n");
return exe_name;
}
void setEnvironmentStringsA(const char *env)
{
if(env == NULL) throw Jav::Error("Error: environment string is null\n");
for(auto it = Jav::MultiStringIterator(env); it.has(); )
{
auto begin = it.get();
auto end = it.next('\0');
auto var_end = Jav::nextPosOf('=',{begin,end});
if(!var_end) throw Jav::Error("Error: Invalid EnvironmentString\n");
Jav::cstring var(begin,var_end.it++);
SetEnvironmentVariableA(var,var_end);
}
}
ProcessW::ProcessW(const wchar_t *cmd,const wchar_t *current_dir,const wchar_t *app_name)
: builder(new Builder())
{
if(cmd) builder->cmd << cmd;
builder->app_name = app_name;
builder->current_dir = current_dir;
}
ProcessW::~ProcessW()
{
stop();
delete builder;
}
bool ProcessW::launch()
{
Jav::rFile child_read_end;
Jav::wFile child_error_end;
Jav::wFile child_write_end;
if(!Jav::createPipe(m_read_end,child_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
//Jav::createPipe(m_error_end,child_error_end);
if(!Jav::createPipe(child_read_end,m_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
builder->si.dwFlags |= STARTF_USESTDHANDLES;
builder->si.hStdInput = (HANDLE)child_read_end;
builder->si.hStdOutput = (HANDLE)child_write_end;
builder->si.hStdError = (HANDLE)child_write_end;
auto env = buildEnvironment();
auto cmd = builder->cmd.str();
if(flags & ADD_PARENT_ENV)
{
auto parent_env = GetEnvironmentStringsW();
for(auto &elem : builder->env_list)
{
auto val = getCombinedEnvVar(elem.first);
SetEnvironmentVariableW(elem.first.c_str(),val);
}
auto child_env = GetEnvironmentStringsW();
if( !CreateProcessW(builder->app_name,&cmd[0],NULL,NULL,false,0,child_env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
setEnvironmentStringsW(parent_env);
FreeEnvironmentStringsW(child_env);
FreeEnvironmentStringsW(parent_env);
}
else
{
if( !CreateProcessW(builder->app_name,&cmd[0],NULL,NULL,false,0,env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
}
if( !(flags & DEBUG) )
{
delete builder;
builder = NULL;
}
return true;
}
int ProcessW::wait(uint time)
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
int ProcessW::stop()
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
if(flags & WAIT) WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
void ProcessW::pause()
{
SuspendThread(info.hThread);
}
void ProcessW::resume()
{
ResumeThread(info.hThread);
}
bool ProcessW::isOpen()
{
if(!info.hProcess) return false;
DWORD exitCode;
if(!GetExitCodeProcess(info.hProcess,&exitCode))
throw Jav::InternalError("InternalError: Failed to GetExitCodeProcess\n");
return exitCode == STILL_ACTIVE;
}
size_t ProcessW::getMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_read_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessW::getErrorMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_error_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessW::read(void *buf,size_t sz)
{
return m_read_end.read(buf,sz);
}
... ... ...
Upvotes: 0
Reputation: 18456
You misplaced the read and write ends :)
CreatePipe
has the prototype
BOOL CreatePipe(
PHANDLE hReadPipe, // can only read from this
PHANDLE hWritePipe, // can only write to this
LPSECURITY_ATTRIBUTES lpPipeAttributes,
DWORD nSize
);
You can't call ReadFile
(or in your case std::getline) from a Write-only handle, and vice versa. If you replaced your std::getline
calls with a simple ReadFile
call, you'd get an ACCESS_DENIED
error, confirming this fact, since your STD_INPUT_HANDLE
in the child process was not opened for GENERIC_READ
.
The fix is as follows:
suSource.hStdError = suSource.hStdOutput = hPipeOut; // must be the write pipe!
suDest.hStdInput = hPipeIn; // must be the read pipe.
Perhaps the names you assigned are confusing. If you called them as per the formal parameters, the error would be clearer:
suSource.hStdError = suSource.hStdOutput = hReadPipe; // clearly wrong.
suDest.hStdInput = hWritePipe; // as above -- expects a read-handle.
Upvotes: 5