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.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 

Generated on Mon Aug 30 19:14:03 2010 for Vidalia by  doxygen 1.5.9