Vidalia  0.2.15
win32.cpp
Go to the documentation of this file.
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