Vidalia 0.2.10
|
00001 /* 00002 ** This file is part of Vidalia, and is subject to the license terms in the 00003 ** LICENSE file, found in the top level directory of this distribution. If you 00004 ** did not receive the LICENSE file with this file, you may obtain it from the 00005 ** Vidalia source package distributed by the Vidalia Project at 00006 ** http://www.vidalia-project.net/. No part of Vidalia, including this file, 00007 ** may be copied, modified, propagated, or distributed except according to the 00008 ** terms described in the LICENSE file. 00009 */ 00010 00011 /* 00012 ** \file win32.cpp 00013 ** \version $Id: win32.cpp 4294 2010-05-18 03:58:45Z edmanm $ 00014 ** \brief Win32-specific functions 00015 */ 00016 00017 #include "win32.h" 00018 00019 #include <QDir> 00020 #include <QLibrary> 00021 #include <QtDebug> 00022 00023 #include <tlhelp32.h> 00024 #include <shlobj.h> 00025 00026 #if defined(UNICODE) 00027 /* Force the ascii verisons of these functions, so we can run on Win98. We 00028 * don't pass any Unicode strings to these functions anyway. */ 00029 #undef PROCESSENTRY32 00030 #undef LPPROCESSENTRY32 00031 #undef Process32First 00032 #undef Process32Next 00033 #endif 00034 00035 /* Load the tool help functions dynamically, since they don't exist on 00036 * Windows NT 4.0 */ 00037 typedef HANDLE (WINAPI *CreateToolhelp32Snapshot_fn)(DWORD, DWORD); 00038 typedef BOOL (WINAPI *Process32First_fn)(HANDLE, LPPROCESSENTRY32); 00039 typedef BOOL (WINAPI *Process32Next_fn)(HANDLE, LPPROCESSENTRY32); 00040 00041 00042 /** Finds the location of the "special" Windows folder using the given CSIDL 00043 * value. If the folder cannot be found, the given default path is used. */ 00044 QString 00045 win32_get_folder_location(int folder, QString defaultPath) 00046 { 00047 TCHAR path[MAX_PATH+1]; 00048 LPITEMIDLIST idl; 00049 IMalloc *m; 00050 HRESULT result; 00051 00052 /* XXX: It would probably be a good idea to replace this function with simple 00053 * calls to QDesktopServices::storageLocation(). Note that it returns the 00054 * "Local Settings" application data directory though, so our installers 00055 * would need to be updated as well. 00056 */ 00057 00058 /* Find the location of %PROGRAMFILES% */ 00059 if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, folder, &idl))) { 00060 /* Get the path from the IDL */ 00061 result = SHGetPathFromIDList(idl, path); 00062 SHGetMalloc(&m); 00063 if (m) { 00064 m->Release(); 00065 } 00066 if (SUCCEEDED(result)) { 00067 #if defined(UNICODE) 00068 return QString::fromUtf16(static_cast<const ushort *>(path)); 00069 #else 00070 return QString::fromLocal8Bit(static_cast<const char *>(path)); 00071 #endif 00072 } 00073 } 00074 return defaultPath; 00075 } 00076 00077 /** Gets the location of the user's %PROGRAMFILES% folder. */ 00078 QString 00079 win32_program_files_folder() 00080 { 00081 return win32_get_folder_location( 00082 CSIDL_PROGRAM_FILES, QDir::rootPath() + "\\Program Files"); 00083 } 00084 00085 /** Gets the location of the user's %APPDATA% folder. */ 00086 QString 00087 win32_app_data_folder() 00088 { 00089 return win32_get_folder_location( 00090 CSIDL_APPDATA, QDir::homePath() + "\\Application Data"); 00091 } 00092 00093 /** Returns the value in keyName at keyLocation. 00094 * Returns an empty QString if the keyName doesn't exist */ 00095 QString 00096 win32_registry_get_key_value(QString keyLocation, QString keyName) 00097 { 00098 HKEY key; 00099 char data[255] = {0}; 00100 DWORD size = sizeof(data); 00101 00102 /* Open the key for reading (opens new key if it doesn't exist) */ 00103 if (RegOpenKeyExA(HKEY_CURRENT_USER, 00104 qPrintable(keyLocation), 00105 0L, KEY_READ, &key) == ERROR_SUCCESS) { 00106 00107 /* Key exists, so read the value into data */ 00108 RegQueryValueExA(key, qPrintable(keyName), 00109 NULL, NULL, (LPBYTE)data, &size); 00110 } 00111 00112 /* Close anything that was opened */ 00113 RegCloseKey(key); 00114 00115 return QString(data); 00116 } 00117 00118 /** Creates and/or sets the key to the specified value */ 00119 void 00120 win32_registry_set_key_value(QString keyLocation, QString keyName, QString keyValue) 00121 { 00122 HKEY key; 00123 00124 /* Open the key for writing (opens new key if it doesn't exist */ 00125 if (RegOpenKeyExA(HKEY_CURRENT_USER, 00126 qPrintable(keyLocation), 00127 0, KEY_WRITE, &key) != ERROR_SUCCESS) { 00128 00129 /* Key didn't exist, so write the newly opened key */ 00130 RegCreateKeyExA(HKEY_CURRENT_USER, 00131 qPrintable(keyLocation), 00132 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 00133 &key, NULL); 00134 } 00135 00136 /* Save the value in the key */ 00137 RegSetValueExA(key, qPrintable(keyName), 0, REG_SZ, 00138 (BYTE *)qPrintable(keyValue), 00139 (DWORD)keyValue.length() + 1); // include null terminator 00140 00141 /* Close the key */ 00142 RegCloseKey(key); 00143 } 00144 00145 /** Removes the key from the registry if it exists */ 00146 void 00147 win32_registry_remove_key(QString keyLocation, QString keyName) 00148 { 00149 HKEY key; 00150 00151 /* Open the key for writing (opens new key if it doesn't exist */ 00152 if (RegOpenKeyExA(HKEY_CURRENT_USER, 00153 qPrintable(keyLocation), 00154 0, KEY_SET_VALUE, &key) == ERROR_SUCCESS) { 00155 00156 /* Key exists so delete it */ 00157 RegDeleteValueA(key, qPrintable(keyName)); 00158 } 00159 00160 /* Close anything that was opened */ 00161 RegCloseKey(key); 00162 } 00163 00164 /** 00165 * Callback for EnumThreadWindows which sends the WM_QUIT message 00166 */ 00167 BOOL CALLBACK 00168 quitWindowCallback(HWND hwnd, LPARAM targetPID) 00169 { 00170 DWORD hwndPID = 0; 00171 00172 /* If the process ID for hwnd matches the target PID, post 00173 WM_QUIT to the window */ 00174 GetWindowThreadProcessId(hwnd, &hwndPID); 00175 if (hwndPID == (DWORD)targetPID) 00176 PostMessage(hwnd, WM_QUIT, 0, (LPARAM)NULL); 00177 return TRUE; 00178 } 00179 00180 /** 00181 * Close process with the specified PID. Sends WM_QUIT to all 00182 * top-level windows. 00183 */ 00184 void 00185 win32_end_process_by_pid(DWORD pid) 00186 { 00187 /* Send WM_QUIT to all windows */ 00188 EnumWindows(&quitWindowCallback, (LPARAM)pid); 00189 /* At this point we could kill the main thread, but how do we find 00190 the ID of the main thread? We can find the ID of all threads 00191 but killing them all seems to cause a problem for Firefox */ 00192 //PostThreadMessage(thread.th32ThreadID, WM_CLOSE, 0, (LPARAM)NULL); 00193 } 00194 00195 /** 00196 * Close all processes started from the specified filename. Sends 00197 * WM_QUIT to all top-level windows. Filename should be given in 00198 * lowercase, and comparison is case insensitive. Note: the MSDN 00199 * documentation for WM_QUIT states that the message should not be 00200 * sent by PostMessage(). However, sending WM_CLOSE leaves Firefox 00201 * running, whereas WM_QUIT seems to work. 00202 */ 00203 void 00204 win32_end_process_by_filename(QString filename) 00205 { 00206 /* Get list of running processes */ 00207 QHash<qint64, QString> procList = win32_process_list(); 00208 00209 /* On old versions of Windows win32_process_list() will return 00210 an empty list. In this case, just keep Vidalia open */ 00211 if (procList.isEmpty()) { 00212 return; 00213 } 00214 00215 /* Loop over all processes */ 00216 QHashIterator<qint64, QString> i(procList); 00217 while (i.hasNext()) { 00218 i.next(); 00219 if (i.value().toLower() == filename) { 00220 /* Kill this process */ 00221 win32_end_process_by_pid((DWORD)i.key()); 00222 } 00223 } 00224 } 00225 00226 /** Returns a list of all currently active processes, including their pid 00227 * and exe filename. */ 00228 QHash<qint64, QString> 00229 win32_process_list() 00230 { 00231 QHash<qint64, QString> procList; 00232 CreateToolhelp32Snapshot_fn pCreateToolhelp32Snapshot; 00233 Process32First_fn pProcess32First; 00234 Process32Next_fn pProcess32Next; 00235 HANDLE hSnapshot; 00236 PROCESSENTRY32 proc; 00237 QString exeFile; 00238 qint64 pid; 00239 00240 /* Load the tool help functions */ 00241 pCreateToolhelp32Snapshot = 00242 (CreateToolhelp32Snapshot_fn)QLibrary::resolve("kernel32", "CreateToolhelp32Snapshot"); 00243 pProcess32First = (Process32First_fn)QLibrary::resolve("kernel32", "Process32First"); 00244 pProcess32Next = (Process32Next_fn)QLibrary::resolve("kernel32", "Process32Next"); 00245 00246 if (!pCreateToolhelp32Snapshot || !pProcess32First || !pProcess32Next) { 00247 qWarning("Unable to load tool help functions. Running process information " 00248 "will be unavailable."); 00249 return QHash<qint64, QString>(); 00250 } 00251 00252 /* Create a snapshot of all active processes */ 00253 hSnapshot = pCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 00254 if (hSnapshot != INVALID_HANDLE_VALUE) { 00255 proc.dwSize = sizeof(PROCESSENTRY32); 00256 00257 /* Iterate through all the processes in the snapshot */ 00258 if (pProcess32First(hSnapshot, &proc)) { 00259 do { 00260 /* Extract the PID and exe filename from the process record */ 00261 pid = (qint64)proc.th32ProcessID; 00262 exeFile = QString::fromAscii((const char *)proc.szExeFile); 00263 00264 /* Add this process to our list */ 00265 procList.insert(pid, exeFile); 00266 } while (pProcess32Next(hSnapshot, &proc)); 00267 } 00268 CloseHandle(hSnapshot); 00269 } 00270 return procList; 00271 } 00272