2 This file is part of Cute Chess.
4 Cute Chess is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 Cute Chess is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with Cute Chess. If not, see <http://www.gnu.org/licenses/>.
18 #include "engineprocess_win.h"
22 #include "pipereader_win.h"
25 EngineProcess::EngineProcess(QObject
* parent
)
30 m_exitStatus(EngineProcess::NormalExit
),
31 m_inWrite(INVALID_HANDLE_VALUE
),
32 m_outRead(INVALID_HANDLE_VALUE
),
37 EngineProcess::~EngineProcess()
41 qWarning("EngineProcess: Destroyed while process is still running.");
48 int EngineProcess::exitCode() const
50 return (int)m_exitCode
;
53 EngineProcess::ExitStatus
EngineProcess::exitStatus() const
58 qint64
EngineProcess::bytesAvailable() const
60 qint64 n
= QIODevice::bytesAvailable();
64 return m_reader
->bytesAvailable() + n
;
67 bool EngineProcess::canReadLine() const
70 return QIODevice::canReadLine();
71 return m_reader
->canReadLine() || QIODevice::canReadLine();
74 void EngineProcess::killHandle(HANDLE
* handle
)
76 if (*handle
== INVALID_HANDLE_VALUE
)
79 *handle
= INVALID_HANDLE_VALUE
;
82 void EngineProcess::cleanup()
86 if (m_reader
->isRunning())
88 qWarning("EngineProcess: pipe reader was terminated");
89 m_reader
->terminate();
95 killHandle(&m_inWrite
);
96 killHandle(&m_outRead
);
98 killHandle(&m_processInfo
.hProcess
);
99 killHandle(&m_processInfo
.hThread
);
104 void EngineProcess::close()
116 bool EngineProcess::isSequential() const
121 void EngineProcess::setWorkingDirectory(const QString
& dir
)
126 static QString
quoteString(QString str
)
128 if (!str
.contains(' '))
131 if (!str
.startsWith('\"'))
133 if (!str
.endsWith('\"'))
139 static QString
commandLine(const QString
& prog
, const QStringList
& args
)
141 QString cmd
= QDir::toNativeSeparators(quoteString(prog
));
142 foreach (const QString
& arg
, args
)
143 cmd
+= ' ' + quoteString(arg
);
148 void EngineProcess::start(const QString
& program
,
149 const QStringList
& arguments
,
158 m_exitStatus
= NormalExit
;
160 // Temporary handles for the child process' end of the pipes
164 // Security attributes. Use the same one for both pipes.
165 SECURITY_ATTRIBUTES saAttr
;
166 saAttr
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
167 saAttr
.bInheritHandle
= TRUE
;
168 saAttr
.lpSecurityDescriptor
= NULL
;
170 CreatePipe(&m_outRead
, &outWrite
, &saAttr
, 0);
171 CreatePipe(&inRead
, &m_inWrite
, &saAttr
, 0);
173 STARTUPINFO startupInfo
;
174 ZeroMemory(&startupInfo
, sizeof(startupInfo
));
175 startupInfo
.cb
= sizeof(startupInfo
);
176 startupInfo
.hStdError
= outWrite
;
177 startupInfo
.hStdOutput
= outWrite
;
178 startupInfo
.hStdInput
= inRead
;
179 startupInfo
.dwFlags
|= STARTF_USESTDHANDLES
;
181 // Call DuplicateHandle with a NULL target to get non-inheritable
182 // handles for the parent process' ends of the pipes
183 DuplicateHandle(GetCurrentProcess(),
184 m_outRead
, // child's stdout read end
188 FALSE
, // not inheritable
189 DUPLICATE_SAME_ACCESS
); // same handle access
190 DuplicateHandle(GetCurrentProcess(),
191 m_inWrite
, // child's stdin write end
195 FALSE
, // not inheritable
196 DUPLICATE_SAME_ACCESS
); // same handle access
199 QString cmd
= commandLine(program
, arguments
);
200 QString wdir
= QDir::toNativeSeparators(m_workDir
);
201 ZeroMemory(&m_processInfo
, sizeof(m_processInfo
));
204 ok
= CreateProcessW(NULL
,
206 NULL
, // process attributes
207 NULL
, // thread attributes
208 TRUE
, // inherit handles
209 CREATE_NEW_PROCESS_GROUP
, // creation flags
211 wdir
.isEmpty() ? NULL
: (WCHAR
*)wdir
.utf16(),
215 ok
= CreateProcessA(NULL
,
216 cmd
.toLocal8Bit().data(),
217 NULL
, // process attributes
218 NULL
, // thread attributes
219 TRUE
, // inherit handles
220 CREATE_NEW_PROCESS_GROUP
, // creation flags
222 wdir
.isEmpty() ? NULL
: wdir
.toLocal8Bit().data(),
225 #endif // not UNICODE
227 m_started
= (bool)ok
;
230 // Close the child process' ends of the pipes to make sure
231 // that ReadFile and WriteFile will return when the child
232 // terminates and closes its pipes
233 killHandle(&outWrite
);
236 // Start reading input from the child
237 m_reader
= new PipeReader(m_outRead
, this);
238 connect(m_reader
, SIGNAL(finished()), this, SLOT(onFinished()));
239 connect(m_reader
, SIGNAL(finished()), this, SIGNAL(readChannelFinished()));
240 connect(m_reader
, SIGNAL(readyRead()), this, SIGNAL(readyRead()));
243 // Make QIODevice aware that the device is now open
244 QIODevice::open(mode
);
250 void EngineProcess::start(const QString
& program
,
255 QRegExp
rx("((?:[^\\s\"]+)|(?:\"(?:\\\\\"|[^\"])*\"))");
257 while ((pos
= rx
.indexIn(program
, pos
)) != -1)
260 pos
+= rx
.matchedLength();
265 QString prog
= args
.first();
267 start(prog
, args
, mode
);
270 void EngineProcess::kill()
273 TerminateProcess(m_processInfo
.hProcess
, 0xf291);
276 void EngineProcess::onFinished()
278 if (!m_started
|| m_finished
)
281 if (GetExitCodeProcess(m_processInfo
.hProcess
, &m_exitCode
)
282 && m_exitCode
!= STILL_ACTIVE
)
285 m_exitStatus
= NormalExit
;
287 m_exitStatus
= CrashExit
;
289 Q_ASSERT(m_reader
== 0 || m_reader
->isFinished());
291 emit
finished((int)m_exitCode
, m_exitStatus
);
295 bool EngineProcess::waitForFinished(int msecs
)
306 DWORD ret
= WaitForSingleObject(m_processInfo
.hProcess
, dwWait
);
307 if (ret
== WAIT_OBJECT_0
)
309 // The blocking ReadFile call in the pipe reader should
310 // return now that the pipes are closed. But if it doesn't
311 // happen, the pipe reader will be terminated violently
312 // after the timeout.
313 m_reader
->wait(10000);
321 bool EngineProcess::waitForStarted(int msecs
)
323 // Don't wait here because CreateProcess already did the waiting
328 QString
EngineProcess::workingDirectory() const
333 qint64
EngineProcess::readData(char* data
, qint64 maxSize
)
338 return m_reader
->readData(data
, maxSize
);
341 qint64
EngineProcess::writeData(const char* data
, qint64 maxSize
)
347 if (!WriteFile(m_inWrite
, data
, (DWORD
)maxSize
, &dwWritten
, 0))
349 return (qint64
)dwWritten
;