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