OSDN Git Service

Updated Monkey's Audio binary to v4.11 (2013-01-20), including STDERR flush fix.
[lamexp/LameXP.git] / src / Tool_Abstract.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2013 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
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.
9 //
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.
14 //
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.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "Tool_Abstract.h"
23
24 #include "Global.h"
25
26 #include <QProcess>
27 #include <QMutex>
28 #include <QMutexLocker>
29 #include <QLibrary>
30 #include <QProcessEnvironment>
31 #include <QDir>
32
33 /*
34  * Win32 API definitions
35  */
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);
39
40 /*
41  * Static vars
42  */
43 quint64 AbstractTool::s_lastLaunchTime = 0ui64;
44 QMutex AbstractTool::s_mutex_startProcess;
45 HANDLE AbstractTool::s_handle_jobObject = NULL;
46 unsigned int AbstractTool::s_jobObjRefCount = 0U;
47
48 /*
49  * Const
50  */
51 static const DWORD START_DELAY = 333; //in milliseconds
52 static const quint64 START_DELAY_NANO = START_DELAY * 1000 * 10; //in 100-nanosecond intervals
53
54 /*
55  * Constructor
56  */
57 AbstractTool::AbstractTool(void)
58 {
59         static CreateJobObjectFun CreateJobObjectPtr = NULL;
60         static SetInformationJobObjectFun SetInformationJobObjectPtr = NULL;
61
62         QMutexLocker lock(&s_mutex_startProcess);
63
64         if(s_jobObjRefCount < 1U)
65         {
66                 const lamexp_os_version_t *osVersionNo = lamexp_get_os_version();
67                 if(LAMEXP_MIN_OS_VER(osVersionNo, 5, 1))
68                 {
69                         if((!CreateJobObjectPtr) || (!SetInformationJobObjectPtr))
70                         {
71                                 QLibrary Kernel32Lib("kernel32.dll");
72                                 CreateJobObjectPtr = (CreateJobObjectFun) Kernel32Lib.resolve("CreateJobObjectA");
73                                 SetInformationJobObjectPtr = (SetInformationJobObjectFun) Kernel32Lib.resolve("SetInformationJobObject");
74                         }
75                 }
76                 if(CreateJobObjectPtr && SetInformationJobObjectPtr)
77                 {
78                         HANDLE jobObject = CreateJobObjectPtr(NULL, NULL);
79                         if((jobObject != NULL) && (jobObject != INVALID_HANDLE_VALUE))
80                         {
81                                 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobExtendedLimitInfo;
82                                 memset(&jobExtendedLimitInfo, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
83                                 memset(&jobExtendedLimitInfo.BasicLimitInformation, 0, sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION));
84                                 jobExtendedLimitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
85                                 if(SetInformationJobObjectPtr(jobObject, JobObjectExtendedLimitInformation, &jobExtendedLimitInfo, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)))
86                                 {
87                                         s_handle_jobObject = jobObject;
88                                         s_jobObjRefCount = 1U;
89                                 }
90                                 else
91                                 {
92                                         qWarning("Failed to set job object information!");
93                                         CloseHandle(jobObject);
94                                 }
95                         }
96                         else
97                         {
98                                 qWarning("Failed to create the job object!");
99                         }
100                 }
101         }
102         else
103         {
104                 s_jobObjRefCount++;
105         }
106
107         m_firstLaunch = true;
108 }
109
110 /*
111  * Destructor
112  */
113 AbstractTool::~AbstractTool(void)
114 {
115         QMutexLocker lock(&s_mutex_startProcess);
116
117         if(s_jobObjRefCount >= 1U)
118         {
119                 s_jobObjRefCount--;
120                 if((s_jobObjRefCount < 1U) && s_handle_jobObject)
121                 {
122                         CloseHandle(s_handle_jobObject);
123                         s_handle_jobObject = NULL;
124                 }
125         }
126 }
127
128 /*
129  * Initialize and launch process object
130  */
131 bool AbstractTool::startProcess(QProcess &process, const QString &program, const QStringList &args)
132 {
133         static AssignProcessToJobObjectFun AssignProcessToJobObjectPtr = NULL;
134         
135         QMutexLocker lock(&s_mutex_startProcess);
136
137         if(currentTime() <= s_lastLaunchTime)
138         {
139                 Sleep(START_DELAY);
140         }
141
142         emit messageLogged(commandline2string(program, args) + "\n");
143
144         QProcessEnvironment env = process.processEnvironment();
145         if(env.isEmpty()) env = QProcessEnvironment::systemEnvironment();
146         env.insert("TEMP", QDir::toNativeSeparators(lamexp_temp_folder2()));
147         env.insert("TMP", QDir::toNativeSeparators(lamexp_temp_folder2()));
148         process.setProcessEnvironment(env);
149         
150         if(!AssignProcessToJobObjectPtr)
151         {
152                 QLibrary Kernel32Lib("kernel32.dll");
153                 AssignProcessToJobObjectPtr = (AssignProcessToJobObjectFun) Kernel32Lib.resolve("AssignProcessToJobObject");
154         }
155         
156         process.setProcessChannelMode(QProcess::MergedChannels);
157         process.setReadChannel(QProcess::StandardOutput);
158         process.start(program, args);
159         
160         if(process.waitForStarted())
161         {
162                 if(AssignProcessToJobObjectPtr && s_handle_jobObject)
163                 {
164                         if(!AssignProcessToJobObjectPtr(s_handle_jobObject, process.pid()->hProcess))
165                         {
166                                 qWarning("Failed to assign process to job object!");
167                         }
168                 }
169                 if(!SetPriorityClass(process.pid()->hProcess, BELOW_NORMAL_PRIORITY_CLASS))
170                 {
171                         SetPriorityClass(process.pid()->hProcess, IDLE_PRIORITY_CLASS);
172                 }
173                 
174                 lock.unlock();
175                 
176                 if(m_firstLaunch)
177                 {
178                         emit statusUpdated(0);
179                         m_firstLaunch = false;
180                 }
181                 
182                 s_lastLaunchTime = currentTime() + START_DELAY_NANO;
183                 return true;
184         }
185
186         emit messageLogged("Process creation has failed :-(");
187         QString errorMsg= process.errorString().trimmed();
188         if(!errorMsg.isEmpty()) emit messageLogged(errorMsg);
189
190         process.kill();
191         process.waitForFinished(-1);
192
193         s_lastLaunchTime = currentTime() + START_DELAY_NANO;
194         return false;
195 }
196
197 /*
198  * Convert program arguments to single string
199  */
200 QString AbstractTool::commandline2string(const QString &program, const QStringList &arguments)
201 {
202         QString commandline = (program.contains(' ') ? QString("\"%1\"").arg(program) : program);
203         
204         for(int i = 0; i < arguments.count(); i++)
205         {
206                 commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));
207         }
208
209         return commandline;
210 }
211
212 /*
213  * Convert long path to short path
214  */
215 QString AbstractTool::pathToShort(const QString &longPath)
216 {
217         QString shortPath;
218         DWORD buffSize = GetShortPathNameW(reinterpret_cast<const wchar_t*>(longPath.utf16()), NULL, NULL);
219         
220         if(buffSize > 0)
221         {
222                 wchar_t *buffer = new wchar_t[buffSize];
223                 DWORD result = GetShortPathNameW(reinterpret_cast<const wchar_t*>(longPath.utf16()), buffer, buffSize);
224
225                 if(result > 0 && result < buffSize)
226                 {
227                         shortPath = QString::fromUtf16(reinterpret_cast<const unsigned short*>(buffer));
228                 }
229
230                 delete[] buffer;
231         }
232
233         return (shortPath.isEmpty() ? longPath : shortPath);
234 }
235
236 const quint64 AbstractTool::currentTime(void)
237 {
238         FILETIME fileTime;
239         GetSystemTimeAsFileTime(&fileTime);
240
241         ULARGE_INTEGER temp;
242         temp.HighPart = fileTime.dwHighDateTime;
243         temp.LowPart = fileTime.dwLowDateTime;
244
245         return temp.QuadPart;
246 }