1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "Tool_Abstract.h"
28 #include <QMutexLocker>
30 #include <QProcessEnvironment>
34 * Win32 API definitions
36 typedef HANDLE (WINAPI
*CreateJobObjectFun
)(__in_opt LPSECURITY_ATTRIBUTES lpJobAttributes
, __in_opt LPCSTR lpName
);
37 typedef BOOL (WINAPI
*SetInformationJobObjectFun
)(__in HANDLE hJob
, __in JOBOBJECTINFOCLASS JobObjectInformationClass
, __in_bcount(cbJobObjectInformationLength
) LPVOID lpJobObjectInformation
, __in DWORD cbJobObjectInformationLength
);
38 typedef BOOL (WINAPI
*AssignProcessToJobObjectFun
)(__in HANDLE hJob
, __in HANDLE hProcess
);
43 quint64
AbstractTool::m_lastLaunchTime
= 0ui
64;
44 QMutex
*AbstractTool::m_mutex_startProcess
= NULL
;
45 HANDLE
AbstractTool::m_handle_jobObject
= NULL
;
46 unsigned int AbstractTool::m_jobObjRefCount
= 0U;
51 static const DWORD START_DELAY
= 333; //in milliseconds
52 static const quint64 START_DELAY_NANO
= START_DELAY
* 1000 * 10; //in 100-nanosecond intervals
57 AbstractTool::AbstractTool(void)
59 static CreateJobObjectFun CreateJobObjectPtr
= NULL
;
60 static SetInformationJobObjectFun SetInformationJobObjectPtr
= NULL
;
62 if(!m_mutex_startProcess
)
64 m_mutex_startProcess
= new QMutex();
67 QMutexLocker
lock(m_mutex_startProcess
);
69 if(m_jobObjRefCount
< 1U)
71 DWORD osVersionNo
= lamexp_get_os_version();
72 if(((HIWORD(osVersionNo
) == 5) && (LOWORD(osVersionNo
) >= 1)) || (HIWORD(osVersionNo
) > 5))
74 if((!CreateJobObjectPtr
) || (!SetInformationJobObjectPtr
))
76 QLibrary
Kernel32Lib("kernel32.dll");
77 CreateJobObjectPtr
= (CreateJobObjectFun
) Kernel32Lib
.resolve("CreateJobObjectA");
78 SetInformationJobObjectPtr
= (SetInformationJobObjectFun
) Kernel32Lib
.resolve("SetInformationJobObject");
81 if(CreateJobObjectPtr
&& SetInformationJobObjectPtr
)
83 HANDLE jobObject
= CreateJobObjectPtr(NULL
, NULL
);
84 if((jobObject
!= NULL
) && (jobObject
!= INVALID_HANDLE_VALUE
))
86 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobExtendedLimitInfo
;
87 memset(&jobExtendedLimitInfo
, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION
));
88 memset(&jobExtendedLimitInfo
.BasicLimitInformation
, 0, sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION
));
89 jobExtendedLimitInfo
.BasicLimitInformation
.LimitFlags
= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
| JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
;
90 if(SetInformationJobObjectPtr(jobObject
, JobObjectExtendedLimitInformation
, &jobExtendedLimitInfo
, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION
)))
92 m_handle_jobObject
= jobObject
;
93 m_jobObjRefCount
= 1U;
97 qWarning("Failed to set job object information!");
98 CloseHandle(jobObject
);
103 qWarning("Failed to create the job object!");
112 m_firstLaunch
= true;
118 AbstractTool::~AbstractTool(void)
120 QMutexLocker
lock(m_mutex_startProcess
);
122 if(m_jobObjRefCount
>= 1U)
125 if((m_jobObjRefCount
< 1U) && m_handle_jobObject
)
127 CloseHandle(m_handle_jobObject
);
128 m_handle_jobObject
= NULL
;
134 * Initialize and launch process object
136 bool AbstractTool::startProcess(QProcess
&process
, const QString
&program
, const QStringList
&args
)
138 static AssignProcessToJobObjectFun AssignProcessToJobObjectPtr
= NULL
;
140 QMutexLocker
lock(m_mutex_startProcess
);
142 if(currentTime() <= m_lastLaunchTime
)
147 emit
messageLogged(commandline2string(program
, args
) + "\n");
149 QProcessEnvironment env
= process
.processEnvironment();
150 if(env
.isEmpty()) env
= QProcessEnvironment::systemEnvironment();
151 env
.insert("TEMP", QDir::toNativeSeparators(lamexp_temp_folder2()));
152 env
.insert("TMP", QDir::toNativeSeparators(lamexp_temp_folder2()));
153 process
.setProcessEnvironment(env
);
155 if(!AssignProcessToJobObjectPtr
)
157 QLibrary
Kernel32Lib("kernel32.dll");
158 AssignProcessToJobObjectPtr
= (AssignProcessToJobObjectFun
) Kernel32Lib
.resolve("AssignProcessToJobObject");
161 process
.setProcessChannelMode(QProcess::MergedChannels
);
162 process
.setReadChannel(QProcess::StandardOutput
);
163 process
.start(program
, args
);
165 if(process
.waitForStarted())
167 if(AssignProcessToJobObjectPtr
&& m_handle_jobObject
)
169 if(!AssignProcessToJobObjectPtr(m_handle_jobObject
, process
.pid()->hProcess
))
171 qWarning("Failed to assign process to job object!");
174 if(!SetPriorityClass(process
.pid()->hProcess
, BELOW_NORMAL_PRIORITY_CLASS
))
176 SetPriorityClass(process
.pid()->hProcess
, IDLE_PRIORITY_CLASS
);
183 emit
statusUpdated(0);
184 m_firstLaunch
= false;
187 m_lastLaunchTime
= currentTime() + START_DELAY_NANO
;
191 emit
messageLogged("Process creation has failed :-(");
192 QString errorMsg
= process
.errorString().trimmed();
193 if(!errorMsg
.isEmpty()) emit
messageLogged(errorMsg
);
196 process
.waitForFinished(-1);
198 m_lastLaunchTime
= currentTime() + START_DELAY_NANO
;
203 * Convert program arguments to single string
205 QString
AbstractTool::commandline2string(const QString
&program
, const QStringList
&arguments
)
207 QString commandline
= (program
.contains(' ') ? QString("\"%1\"").arg(program
) : program
);
209 for(int i
= 0; i
< arguments
.count(); i
++)
211 commandline
+= (arguments
.at(i
).contains(' ') ? QString(" \"%1\"").arg(arguments
.at(i
)) : QString(" %1").arg(arguments
.at(i
)));
218 * Convert long path to short path
220 QString
AbstractTool::pathToShort(const QString
&longPath
)
223 DWORD buffSize
= GetShortPathNameW(reinterpret_cast<const wchar_t*>(longPath
.utf16()), NULL
, NULL
);
227 wchar_t *buffer
= new wchar_t[buffSize
];
228 DWORD result
= GetShortPathNameW(reinterpret_cast<const wchar_t*>(longPath
.utf16()), buffer
, buffSize
);
230 if(result
> 0 && result
< buffSize
)
232 shortPath
= QString::fromUtf16(reinterpret_cast<const unsigned short*>(buffer
));
238 return (shortPath
.isEmpty() ? longPath
: shortPath
);
241 const quint64
AbstractTool::currentTime(void)
244 GetSystemTimeAsFileTime(&fileTime
);
247 temp
.HighPart
= fileTime
.dwHighDateTime
;
248 temp
.LowPart
= fileTime
.dwLowDateTime
;
250 return temp
.QuadPart
;