MediaInfoXP/src/Utils.cpp

591 lines
15 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// MediaInfoXP
// Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// http://www.gnu.org/licenses/gpl-2.0.txt
///////////////////////////////////////////////////////////////////////////////
#include "Utils.h"
#include "Config.h"
//Win32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <io.h>
#include <fcntl.h>
#include <Objbase.h>
#include <Psapi.h>
#include <Shlobj.h>
#include <Shlwapi.h>
//StdLib
#include <cstdio>
#include <iostream>
#include <fstream>
#include <ctime>
#pragma intrinsic(_InterlockedExchange)
//Qt
#include <QLibrary>
#include <QDir>
#include <QFileInfo>
#include <QUuid>
#include <QDate>
#include <QReadWriteLock>
#include <QMap>
#include <QIcon>
#include <QWidget>
//Function pointers
typedef HRESULT (WINAPI *SHGetKnownFolderPath_t)(const GUID &rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
typedef HRESULT (WINAPI *SHGetFolderPath_t)(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
//Known folders
typedef enum
{
mixp_folder_localappdata = 0,
mixp_folder_programfiles = 2,
mixp_folder_systemfolder = 3,
mixp_folder_systroot_dir = 4
}
mixp_known_folder_t;
//Known folder cache
static struct
{
bool initialized;
QMap<size_t, QString> knownFolders;
SHGetKnownFolderPath_t getKnownFolderPath;
SHGetFolderPath_t getFolderPath;
QReadWriteLock lock;
}
g_mixp_known_folder;
/*
* Try to lock folder
*/
static QString mixp_tryLockFolder(const QString &folderPath, QFile **lockfile)
{
const QByteArray WRITE_TEST_DATA = QByteArray("Lorem ipsum dolor sit amet, consetetur sadipscing elitr!");
for(int i = 0; i < 32; i++)
{
QDir folder(folderPath);
if(!folder.exists())
{
folder.mkdir(".");
}
if(folder.exists())
{
const QString SUB_FOLDER = QUuid::createUuid().toString().remove('{').remove('}').remove('-').right(16);
folder.mkdir(SUB_FOLDER);
if(folder.cd(SUB_FOLDER) && folder.exists())
{
QFile *testFile = new QFile(QString("%1/~lock.tmp").arg(folder.canonicalPath()));
if(testFile->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Unbuffered))
{
if(testFile->write(WRITE_TEST_DATA) >= WRITE_TEST_DATA.size())
{
*lockfile = testFile;
return folder.canonicalPath();
}
testFile->remove();
}
MIXP_DELETE_OBJ(testFile);
}
}
}
return QString();
}
/*
* Locate known folder on local system
*/
static const QString &mixp_known_folder(mixp_known_folder_t folder_id)
{
//static const int CSIDL_FLAG_CREATE = 0x8000;
//typedef enum { KF_FLAG_CREATE = 0x00008000 } kf_flags_t;
struct
{
const int csidl;
const GUID guid;
}
static s_folders[] =
{
{ 0x001c, {0xF1B32785,0x6FBA,0x4FCF,{0x9D,0x55,0x7B,0x8E,0x7F,0x15,0x70,0x91}} }, //CSIDL_LOCAL_APPDATA
{ 0x0026, {0x905e63b6,0xc1bf,0x494e,{0xb2,0x9c,0x65,0xb7,0x32,0xd3,0xd2,0x1a}} }, //CSIDL_PROGRAM_FILES
{ 0x0024, {0xF38BF404,0x1D43,0x42F2,{0x93,0x05,0x67,0xDE,0x0B,0x28,0xFC,0x23}} }, //CSIDL_WINDOWS_FOLDER
{ 0x0025, {0x1AC14E77,0x02E7,0x4E5D,{0xB7,0x44,0x2E,0xB1,0xAE,0x51,0x98,0xB7}} }, //CSIDL_SYSTEM_FOLDER
};
size_t folderId = size_t(-1);
switch(folder_id)
{
case mixp_folder_localappdata: folderId = 0; break;
case mixp_folder_programfiles: folderId = 1; break;
case mixp_folder_systroot_dir: folderId = 2; break;
case mixp_folder_systemfolder: folderId = 3; break;
}
if(folderId == size_t(-1))
{
qWarning("Invalid 'known' folder was requested!");
return *reinterpret_cast<QString*>(NULL);
}
QReadLocker readLock(&g_mixp_known_folder.lock);
//Already in cache?
if(g_mixp_known_folder.knownFolders.contains(folderId))
{
return g_mixp_known_folder.knownFolders[folderId];
}
//Obtain write lock to initialize
readLock.unlock();
QWriteLocker writeLock(&g_mixp_known_folder.lock);
//Still not in cache?
if(g_mixp_known_folder.knownFolders.contains(folderId))
{
return g_mixp_known_folder.knownFolders[folderId];
}
//Initialize on first call
if(!g_mixp_known_folder.initialized)
{
QLibrary shell32("shell32.dll");
if(shell32.load())
{
g_mixp_known_folder.getFolderPath = (SHGetFolderPath_t) shell32.resolve("SHGetFolderPathW");
g_mixp_known_folder.getKnownFolderPath = (SHGetKnownFolderPath_t) shell32.resolve("SHGetKnownFolderPath");
}
g_mixp_known_folder.initialized = true;
}
QString folderPath;
//Now try to get the folder path!
if(g_mixp_known_folder.getKnownFolderPath)
{
WCHAR *path = NULL;
if(g_mixp_known_folder.getKnownFolderPath(s_folders[folderId].guid, KF_FLAG_CREATE, NULL, &path) == S_OK)
{
//MessageBoxW(0, path, L"SHGetKnownFolderPath", MB_TOPMOST);
QDir folderTemp = QDir(QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast<const unsigned short*>(path))));
if(folderTemp.exists())
{
folderPath = folderTemp.canonicalPath();
}
CoTaskMemFree(path);
}
}
else if(g_mixp_known_folder.getFolderPath)
{
WCHAR *path = new WCHAR[4096];
if(g_mixp_known_folder.getFolderPath(NULL, s_folders[folderId].csidl | CSIDL_FLAG_CREATE, NULL, NULL, path) == S_OK)
{
//MessageBoxW(0, path, L"SHGetFolderPathW", MB_TOPMOST);
QDir folderTemp = QDir(QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast<const unsigned short*>(path))));
if(folderTemp.exists())
{
folderPath = folderTemp.canonicalPath();
}
}
MIXP_DELETE_ARR(path);
}
//Update cache
g_mixp_known_folder.knownFolders.insert(folderId, folderPath);
return g_mixp_known_folder.knownFolders[folderId];
}
/*
* Detect the TEMP folder
*/
QString mixp_getTempFolder(QFile **lockfile)
{
*lockfile = NULL;
//Try the %TMP% or %TEMP% directory first
QString tempPath = mixp_tryLockFolder(QDir::temp().absolutePath(), lockfile);
if(!tempPath.isEmpty())
{
return tempPath;
}
qWarning("Failed to init %%TEMP%%, falling back to %%LOCALAPPDATA%% or %%SYSTEMROOT%%\n");
//Create TEMP folder in %LOCALAPPDATA%
for(int i = 0; i < 2; i++)
{
static const mixp_known_folder_t folderId[2] = { mixp_folder_localappdata, mixp_folder_systroot_dir };
const QString &localAppDataPath = mixp_known_folder(folderId[i]);
if(!localAppDataPath.isEmpty())
{
if(QDir(localAppDataPath).exists())
{
tempPath = mixp_tryLockFolder(QString("%1/Temp").arg(localAppDataPath), lockfile);
if(!tempPath.isEmpty())
{
return tempPath;
}
}
}
}
return QString();
}
/*
* Safely remove a file
*/
static bool mixp_remove_file(const QString &filename)
{
if(!QFileInfo(filename).exists() || !QFileInfo(filename).isFile())
{
return true;
}
else
{
if(!QFile::remove(filename))
{
static const DWORD attrMask = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
const DWORD attributes = GetFileAttributesW(QWCHAR(filename));
if(attributes & attrMask)
{
SetFileAttributesW(QWCHAR(filename), FILE_ATTRIBUTE_NORMAL);
}
if(!QFile::remove(filename))
{
qWarning("Could not delete \"%s\"", filename.toLatin1().constData());
return false;
}
else
{
return true;
}
}
else
{
return true;
}
}
}
/*
* Clean folder
*/
bool mixp_clean_folder(const QString &folderPath)
{
QDir tempFolder(folderPath);
if(tempFolder.exists())
{
QFileInfoList entryList = tempFolder.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
for(int i = 0; i < entryList.count(); i++)
{
if(entryList.at(i).isDir())
{
mixp_clean_folder(entryList.at(i).canonicalFilePath());
}
else
{
for(int j = 0; j < 3; j++)
{
if(mixp_remove_file(entryList.at(i).canonicalFilePath()))
{
break;
}
}
}
}
return tempFolder.rmdir(".");
}
return true;
}
/*
* Get build date
*/
QDate mixp_get_build_date(void)
{
QDate buildDate(2000, 1, 1);
static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
int date[3] = {0, 0, 0}; char temp[12] = {'\0'};
strncpy_s(temp, 12, mixp_buildDate, _TRUNCATE);
if(strlen(temp) == 11)
{
temp[3] = temp[6] = '\0';
date[2] = atoi(&temp[4]);
date[0] = atoi(&temp[7]);
for(int j = 0; j < 12; j++)
{
if(!_strcmpi(&temp[0], months[j]))
{
date[1] = j+1;
break;
}
}
buildDate = QDate(date[0], date[1], date[2]);
}
return buildDate;
}
/*
* Get current date
*/
QDate mixp_get_current_date(void)
{
const DWORD MAX_PROC = 1024;
DWORD *processes = new DWORD[MAX_PROC];
DWORD bytesReturned = 0;
if(!EnumProcesses(processes, sizeof(DWORD) * MAX_PROC, &bytesReturned))
{
MIXP_DELETE_ARR(processes);
return QDate::currentDate();
}
const DWORD procCount = bytesReturned / sizeof(DWORD);
ULARGE_INTEGER lastStartTime;
memset(&lastStartTime, 0, sizeof(ULARGE_INTEGER));
for(DWORD i = 0; i < procCount; i++)
{
HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processes[i]);
if(hProc)
{
FILETIME processTime[4];
if(GetProcessTimes(hProc, &processTime[0], &processTime[1], &processTime[2], &processTime[3]))
{
ULARGE_INTEGER timeCreation;
timeCreation.LowPart = processTime[0].dwLowDateTime;
timeCreation.HighPart = processTime[0].dwHighDateTime;
if(timeCreation.QuadPart > lastStartTime.QuadPart)
{
lastStartTime.QuadPart = timeCreation.QuadPart;
}
}
CloseHandle(hProc);
}
}
MIXP_DELETE_ARR(processes);
FILETIME lastStartTime_fileTime;
lastStartTime_fileTime.dwHighDateTime = lastStartTime.HighPart;
lastStartTime_fileTime.dwLowDateTime = lastStartTime.LowPart;
FILETIME lastStartTime_localTime;
if(!FileTimeToLocalFileTime(&lastStartTime_fileTime, &lastStartTime_localTime))
{
memcpy(&lastStartTime_localTime, &lastStartTime_fileTime, sizeof(FILETIME));
}
SYSTEMTIME lastStartTime_system;
if(!FileTimeToSystemTime(&lastStartTime_localTime, &lastStartTime_system))
{
memset(&lastStartTime_system, 0, sizeof(SYSTEMTIME));
lastStartTime_system.wYear = 1970; lastStartTime_system.wMonth = lastStartTime_system.wDay = 1;
}
const QDate currentDate = QDate::currentDate();
const QDate processDate = QDate(lastStartTime_system.wYear, lastStartTime_system.wMonth, lastStartTime_system.wDay);
return (currentDate >= processDate) ? currentDate : processDate;
}
/*
* Convert QIcon to HICON -> caller is responsible for destroying the HICON!
*/
static HICON mixp_qicon2hicon(const QIcon &icon, const int w, const int h)
{
if(!icon.isNull())
{
QPixmap pixmap = icon.pixmap(w, h);
if(!pixmap.isNull())
{
return pixmap.toWinHICON();
}
}
return NULL;
}
/*
* Update the window icon
*/
mixp_icon_t *mixp_set_window_icon(QWidget *window, const QIcon &icon, const bool bIsBigIcon)
{
if(!icon.isNull())
{
const int extend = (bIsBigIcon ? 32 : 16);
if(HICON hIcon = mixp_qicon2hicon(icon, extend, extend))
{
SendMessage(window->winId(), WM_SETICON, (bIsBigIcon ? ICON_BIG : ICON_SMALL), LPARAM(hIcon));
return reinterpret_cast<mixp_icon_t*>(hIcon);
}
}
return NULL;
}
/*
* Free window icon
*/
void mixp_free_window_icon(mixp_icon_t *icon)
{
if(HICON hIcon = reinterpret_cast<HICON>(icon))
{
DestroyIcon(hIcon);
}
}
/*
* Message Beep
*/
bool mixp_beep(int beepType)
{
switch(beepType)
{
case mixp_beep_info: return (MessageBeep(MB_ICONASTERISK) != FALSE); break;
case mixp_beep_warning: return (MessageBeep(MB_ICONEXCLAMATION) != FALSE); break;
case mixp_beep_error: return (MessageBeep(MB_ICONHAND) != FALSE); break;
default: return false;
}
}
/*
* Bring the specifed window to the front
*/
bool mixp_bring_to_front(const QWidget *window)
{
bool ret = false;
if(window)
{
for(int i = 0; (i < 5) && (!ret); i++)
{
ret = (SetForegroundWindow(window->winId()) != FALSE);
SwitchToThisWindow(window->winId(), TRUE);
}
LockSetForegroundWindow(LSFW_LOCK);
}
return ret;
}
/*
* Registry root key
*/
static HKEY mixp_reg_root(int rootKey)
{
switch(rootKey)
{
case mixp_root_classes: return HKEY_CLASSES_ROOT; break;
case mixp_root_user: return HKEY_CURRENT_USER; break;
case mixp_root_machine: return HKEY_LOCAL_MACHINE; break;
default: throw "Unknown root reg value was specified!";
}
}
/*
* Write registry value
*/
bool mixp_reg_value_write(int rootKey, const QString &keyName, const QString &valueName, const quint32 value)
{
bool success = false; HKEY hKey = NULL;
if(RegCreateKeyEx(mixp_reg_root(rootKey), QWCHAR(keyName), 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS)
{
if(RegSetValueEx(hKey, valueName.isEmpty() ? NULL : QWCHAR(valueName), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(quint32)) == ERROR_SUCCESS)
{
success = true;
}
CloseHandle(hKey);
}
return success;
}
/*
* Write registry value
*/
bool mixp_reg_value_write(int rootKey, const QString &keyName, const QString &valueName, const QString &value)
{
bool success = false; HKEY hKey = NULL;
if(RegCreateKeyEx(mixp_reg_root(rootKey), QWCHAR(keyName), 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS)
{
if(RegSetValueEx(hKey, valueName.isEmpty() ? NULL : QWCHAR(valueName), 0, REG_SZ, reinterpret_cast<const BYTE*>(value.utf16()), (value.length() + 1) * sizeof(wchar_t)) == ERROR_SUCCESS)
{
success = true;
}
CloseHandle(hKey);
}
return success;
}
/*
* Read registry value
*/
bool mixp_reg_value_read(int rootKey, const QString &keyName, const QString &valueName, quint32 &value)
{
bool success = false; HKEY hKey = NULL;
if(RegOpenKeyEx(mixp_reg_root(rootKey), QWCHAR(keyName), 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD size = sizeof(quint32), type = -1;
if(RegQueryValueEx(hKey, valueName.isEmpty() ? NULL : QWCHAR(valueName), 0, &type, reinterpret_cast<BYTE*>(&value), &size) == ERROR_SUCCESS)
{
success = (type == REG_DWORD);
}
CloseHandle(hKey);
}
return success;
}
/*
* Delete registry key
*/
bool mixp_reg_key_delete(int rootKey, const QString &keyName)
{
return (SHDeleteKey( mixp_reg_root(rootKey), QWCHAR(keyName)) == ERROR_SUCCESS);
}
/*
* Shell notification
*/
void mixp_shell_change_notification(void)
{
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
}
/*
* Global init
*/
void _mixp_global_init(void)
{
g_mixp_known_folder.initialized = false;
}