如何在 Win32 中的两个子进程之间设置管道?

发布于 2024-10-05 06:37:33 字数 2803 浏览 2 评论 0原文

对于我的一生,我无法弄清楚为什么这不起作用。基本上,我创建了继承位设置为 true 的管道,并创建了两个子进程,并使用 STARTUPINFO 结构根据需要设置输入和输出句柄,但管道似乎已损坏(第二个进程没有向其中写入任何输出)控制台,即使输出是预期的)

我知道问题不在于我的测试程序 (BitTwiddler.exe),因为我使用 CMD.exe 执行了相同的操作,并且一切都按预期进行。

下面是我所拥有的的最小复制品。我做错了什么?

#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;
}

(如果有人感兴趣,BitTwiddler 是

#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;
}

:),但我真的认为这并不重要。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

青萝楚歌 2024-10-12 06:37:33

您放错了读取和写入端:)

CreatePipe 具有原型

BOOL CreatePipe(
  PHANDLE hReadPipe,  // can only read from this
  PHANDLE hWritePipe,  // can only write to this
  LPSECURITY_ATTRIBUTES lpPipeAttributes,
  DWORD nSize
);

您无法从只写句柄调用 ReadFile (或在您的情况下为 std::getline),反之亦然。如果您将 std::getline 调用替换为简单的 ReadFile 调用,则会收到 ACCESS_DENIED 错误,这证实了这一事实,因为您的子进程中的STD_INPUT_HANDLE未针对GENERIC_READ打开。

修复方法如下:

suSource.hStdError = suSource.hStdOutput = hPipeOut;  // must be the write pipe!
suDest.hStdInput = hPipeIn;  // must be the read pipe.

也许您指定的名称令人困惑。如果你按照形参调用它们,错误会更清楚:

suSource.hStdError = suSource.hStdOutput = hReadPipe;  // clearly wrong.
suDest.hStdInput = hWritePipe;  // as above -- expects a read-handle.

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.
风苍溪 2024-10-12 06:37:33

我对你的问题投了赞成票,因为这是一个非常重要的问题。自从几年前我从 FamTrinli 的演示中学习 Windows 编程以来,CreateProcess 一直是 Win32 API 中最难的函数之一。如果没有,那是最难的。

CreateProcess 有很多参数。每个参数都需要仔细阅读文档才能正确使用。很多论证结构复杂;例如 SECURITY_ATTRIBUTES 和 STARTUPINFO。 STARTUPINFO 结构中的 HANDLE 名称可能会令人困惑。当你迷茫的时候。使用 CreateProcess 涉及的步骤。是否需要立即关闭句柄(注意:不需要)。而进程之间的实际通信可能会导致死锁。

使用 CreateProcess 多年后,我对该 API 进行了广泛的研究和测试。然后我设计了一个包含所有这些知识的库。该库是我的 Jav/win32 库的一部分。该库类似于Java 的Process 类。

我无法在这里发布所有源代码。如果您对源代码感兴趣,我可以通过电子邮件发送给您。让 Cpp 成为编程的首选。

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);
}

...
...
...

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);
}

...
...
...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文