1 
2 
3 #include "subprocess_windows.h"
4 
5 #include <windows.h>
6 #include <winbase.h>
7 #include <shlwapi.h>
8 #include <iostream>
9 #include <sstream>
10 
WinImpl()11 WinImpl::WinImpl():
12   m_pid(0),
13   m_running(false),
14   m_subprocessHandle(INVALID_HANDLE_VALUE),
15   m_waitingThreadHandle(INVALID_HANDLE_VALUE)
16 {
17   InitializeCriticalSection(&m_criticalSection);
18 }
19 
~WinImpl()20 WinImpl::~WinImpl()
21 {
22   kill();
23   WaitForSingleObject(m_waitingThreadHandle, INFINITE);
24   CloseHandle(m_subprocessHandle);
25   DeleteCriticalSection(&m_criticalSection);
26 }
27 
waitForPID(void * _self)28 DWORD WINAPI WinImpl::waitForPID(void* _self)
29 {
30   WinImpl* self = static_cast<WinImpl*>(_self);
31   WaitForSingleObject(self->m_subprocessHandle, INFINITE);
32 
33   EnterCriticalSection(&self->m_criticalSection);
34   self->m_running = false;
35   LeaveCriticalSection(&self->m_criticalSection);
36 
37   return 0;
38 }
39 
toWideChar(const std::string & value,size_t min_size=0)40 std::unique_ptr<wchar_t[]> toWideChar(const std::string& value, size_t min_size = 0)
41 {
42   size_t size = MultiByteToWideChar(CP_UTF8, 0,
43                 value.c_str(), -1, nullptr, 0);
44   auto wdata = std::unique_ptr<wchar_t[]>(new wchar_t[size>min_size?size:min_size]);
45   auto ret = MultiByteToWideChar(CP_UTF8, 0,
46                 value.c_str(), -1, wdata.get(), size);
47   if (0 == ret) {
48     std::ostringstream oss;
49     oss << "Cannot convert to wchar : " << GetLastError();
50     throw std::runtime_error(oss.str());
51   }
52   if (size < min_size) {
53     memset(wdata.get() + size, 0, min_size-size);
54   }
55   return wdata;
56 }
57 
58 
run(commandLine_t & commandLine)59 void WinImpl::run(commandLine_t& commandLine)
60 {
61   STARTUPINFOW startInfo = {0};
62   PROCESS_INFORMATION procInfo;
63   startInfo.cb = sizeof(startInfo);
64   std::wostringstream oss;
65   for(auto& item: commandLine) {
66     auto witem = toWideChar(item, MAX_PATH);
67     PathQuoteSpacesW(witem.get());
68     oss << witem.get() << " ";
69   }
70   auto wCommandLine = oss.str();
71   if (CreateProcessW(
72     NULL,
73     const_cast<wchar_t*>(wCommandLine.c_str()),
74     NULL,
75     NULL,
76     false,
77     CREATE_NO_WINDOW,
78     NULL,
79     NULL,
80     &startInfo,
81     &procInfo))
82   {
83     m_pid = procInfo.dwProcessId;
84     m_subprocessHandle = procInfo.hProcess;
85     CloseHandle(procInfo.hThread);
86     m_running = true;
87     m_waitingThreadHandle = CreateThread(NULL, 0, &waitForPID, this, 0, NULL);
88   }
89 }
90 
kill()91 bool WinImpl::kill()
92 {
93   return TerminateProcess(m_subprocessHandle, 0);
94 }
95 
isRunning()96 bool WinImpl::isRunning()
97 {
98   EnterCriticalSection(&m_criticalSection);
99   bool ret = m_running;
100   LeaveCriticalSection(&m_criticalSection);
101   return ret;
102 }
103