#include #include "stdafx.h" #include "shellapi.h" #include "PlayerTaskServiceWin.h" static const int MAX_LOG_LENGTH = 16 * 1024;// from 2dx PLAYER_NS_BEGIN PlayerTaskWin *PlayerTaskWin::create(const std::string &name, const std::string &executePath, const std::string &commandLineArguments) { PlayerTaskWin *task = new PlayerTaskWin(name, executePath, commandLineArguments); task->autorelease(); return task; } PlayerTaskWin::PlayerTaskWin(const std::string &name, const std::string &executePath, const std::string &commandLineArguments) : PlayerTask(name, executePath, commandLineArguments) , _childStdInRead(NULL) , _childStdInWrite(NULL) , _childStdOutRead(NULL) , _childStdOutWrite(NULL) , _outputBuff(NULL) , _outputBuffWide(NULL) { ZeroMemory(&_pi, sizeof(_pi)); } PlayerTaskWin::~PlayerTaskWin() { cleanup(); } bool PlayerTaskWin::run() { if (!isIdle()) { CCLOG("PlayerTaskWin::run() - task is not idle"); return false; } //BOOL WINAPI CreateProcess( // _In_opt_ LPCTSTR lpApplicationName, // _Inout_opt_ LPTSTR lpCommandLine, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, // _In_ BOOL bInheritHandles, // _In_ DWORD dwCreationFlags, // _In_opt_ LPVOID lpEnvironment, // _In_opt_ LPCTSTR lpCurrentDirectory, // _In_ LPSTARTUPINFO lpStartupInfo, // _Out_ LPPROCESS_INFORMATION lpProcessInformation //); // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx SECURITY_ATTRIBUTES sa = {0}; sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; // Create a pipe for the child process's STDOUT. if (!CreatePipe(&_childStdOutRead, &_childStdOutWrite, &sa, 0) || !SetHandleInformation(_childStdOutRead, HANDLE_FLAG_INHERIT, 0)) { CCLOG("PlayerTaskWin::run() - create stdout handle failed, for execute %s", _executePath.c_str()); cleanup(); return false; } // Create a pipe for the child process's STDIN. if (!CreatePipe(&_childStdInRead, &_childStdInWrite, &sa, 0) || !SetHandleInformation(_childStdInWrite, HANDLE_FLAG_INHERIT, 0)) { CCLOG("PlayerTaskWin::run() - create stdout handle failed, for execute %s", _executePath.c_str()); cleanup(); return false; } ZeroMemory(&_pi, sizeof(_pi)); STARTUPINFO si = {0}; si.cb = sizeof(STARTUPINFO); si.hStdError = _childStdOutWrite; si.hStdOutput = _childStdOutWrite; si.hStdInput = _childStdInRead; si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.wShowWindow = SW_HIDE; #define MAX_COMMAND 4096 //MAX_PATH const std::u16string u16command = makeCommandLine(); WCHAR command[MAX_COMMAND]; wcscpy_s(command, MAX_COMMAND, (WCHAR*)u16command.c_str()); BOOL success = CreateProcess(NULL, command, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited 0, // creation flags NULL, // use parent's environment NULL, // use parent's current directory &si, // STARTUPINFO pointer &_pi); // receives PROCESS_INFORMATION if (!success) { CCLOG("PlayerTaskWin::run() - create process failed, for execute %s", _executePath.c_str()); cleanup(); return false; } _outputBuff = new CHAR[BUFF_SIZE + 1]; _outputBuffWide = new WCHAR[BUFF_SIZE]; _state = STATE_RUNNING; cocos2d::Director::getInstance()->getScheduler()->scheduleUpdate(this, 0, false); return true; } void PlayerTaskWin::runInTerminal() { std::stringstream buf; buf << "/K "; buf << _executePath; buf << " "; buf << _commandLineArguments; std::u16string u16command; cocos2d::StringUtils::UTF8ToUTF16(buf.str(), u16command); ShellExecute(NULL, NULL, L"CMD.EXE", (WCHAR*)u16command.c_str(), NULL, SW_SHOWNORMAL); } void PlayerTaskWin::stop() { if (_pi.hProcess) { TerminateProcess(_pi.hProcess, 0); _resultCode = -1; } cleanup(); } void PlayerTaskWin::update(float dt) { _lifetime += dt; // read output for (;;) { DWORD readCount = 0; PeekNamedPipe(_childStdOutRead, NULL, NULL, NULL, &readCount, NULL); if (readCount == 0) break; if (_output.length() > MAX_LOG_LENGTH) break; readCount = 0; ZeroMemory(_outputBuff, BUFF_SIZE + 1); BOOL success = ReadFile(_childStdOutRead, _outputBuff, BUFF_SIZE - 1, &readCount, NULL); if (!success || readCount == 0) break; int chars = MultiByteToWideChar(CP_OEMCP, 0, _outputBuff, readCount, _outputBuffWide, BUFF_SIZE); if (chars) { ZeroMemory(_outputBuff, BUFF_SIZE + 1); WideCharToMultiByte(CP_UTF8, 0, _outputBuffWide, chars, _outputBuff, BUFF_SIZE + 1, 0, NULL); _output.append(_outputBuff); if (_output.length() > MAX_LOG_LENGTH) break; } } // get child process exit code DWORD resultCode = 0; if (GetExitCodeProcess(_pi.hProcess, &resultCode)) { if (resultCode == STILL_ACTIVE) return; _resultCode = (int)resultCode; } else { // unexpected error _resultCode = (int)GetLastError(); } cocos2d::Director::getInstance()->getScheduler()->unscheduleAllForTarget(this); cleanup(); } void PlayerTaskWin::cleanup() { if (_pi.hProcess) CloseHandle(_pi.hProcess); if (_pi.hThread) CloseHandle(_pi.hThread); ZeroMemory(&_pi, sizeof(_pi)); if (_outputBuff) delete[] _outputBuff; _outputBuff = NULL; if (_outputBuffWide) delete[] _outputBuffWide; _outputBuffWide = NULL; if (_childStdOutRead) CloseHandle(_childStdOutRead); if (_childStdOutWrite) CloseHandle(_childStdOutWrite); if (_childStdInRead) CloseHandle(_childStdInRead); if (_childStdInWrite) CloseHandle(_childStdInWrite); _childStdOutRead = NULL; _childStdOutWrite = NULL; _childStdInRead = NULL; _childStdInWrite = NULL; _state = STATE_COMPLETED; CCLOG("CMD: %s", _output.c_str()); cocos2d::Director::getInstance()->getEventDispatcher()->dispatchCustomEvent(_name); } std::u16string PlayerTaskWin::makeCommandLine() const { std::stringstream buf; buf << "\""; buf << _executePath; buf << "\" "; buf << _commandLineArguments; std::u16string u16command; cocos2d::StringUtils::UTF8ToUTF16(buf.str(), u16command); return u16command; } PlayerTaskServiceWin::PlayerTaskServiceWin(HWND hwnd) : _hwnd(hwnd) { } PlayerTaskServiceWin::~PlayerTaskServiceWin() { for (auto it = _tasks.begin(); it != _tasks.end(); ++it) { it->second->stop(); } } PlayerTask *PlayerTaskServiceWin::createTask(const std::string &name, const std::string &executePath, const std::string &commandLineArguments) { CCASSERT(_tasks.find(name) == _tasks.end(), "Task already exists."); PlayerTaskWin *task = PlayerTaskWin::create(name, executePath, commandLineArguments); _tasks.insert(name, task); return task; } PlayerTask *PlayerTaskServiceWin::getTask(const std::string &name) { auto it = _tasks.find(name); return it != _tasks.end() ? it->second : nullptr; } void PlayerTaskServiceWin::removeTask(const std::string &name) { auto it = _tasks.find(name); if (it != _tasks.end()) { if (!it->second->isCompleted()) { it->second->stop(); } _tasks.erase(it); } } PLAYER_NS_END