Vidalia  0.3.1
win32.cpp
Go to the documentation of this file.
1 /*
2 ** This file is part of Vidalia, and is subject to the license terms in the
3 ** LICENSE file, found in the top level directory of this distribution. If you
4 ** did not receive the LICENSE file with this file, you may obtain it from the
5 ** Vidalia source package distributed by the Vidalia Project at
6 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7 ** including this file, may be copied, modified, propagated, or distributed
8 ** except according to the terms described in the LICENSE file.
9 */
10 
11 /*
12 ** \file win32.cpp
13 ** \brief Win32-specific functions
14 */
15 
16 #include "win32.h"
17 
18 #include <QDir>
19 #include <QLibrary>
20 #include <QtDebug>
21 
22 #include <tlhelp32.h>
23 #include <shlobj.h>
24 
25 #if defined(UNICODE)
26 /* Force the ascii verisons of these functions, so we can run on Win98. We
27  * don't pass any Unicode strings to these functions anyway. */
28 #undef PROCESSENTRY32
29 #undef LPPROCESSENTRY32
30 #undef Process32First
31 #undef Process32Next
32 #endif
33 
34 /* Load the tool help functions dynamically, since they don't exist on
35  * Windows NT 4.0 */
36 typedef HANDLE (WINAPI *CreateToolhelp32Snapshot_fn)(DWORD, DWORD);
37 typedef BOOL (WINAPI *Process32First_fn)(HANDLE, LPPROCESSENTRY32);
38 typedef BOOL (WINAPI *Process32Next_fn)(HANDLE, LPPROCESSENTRY32);
39 
40 
41 /** Finds the location of the "special" Windows folder using the given CSIDL
42  * value. If the folder cannot be found, the given default path is used. */
43 QString
44 win32_get_folder_location(int folder, QString defaultPath)
45 {
46  TCHAR path[MAX_PATH+1];
47  LPITEMIDLIST idl;
48  IMalloc *m;
49  HRESULT result;
50 
51  /* XXX: It would probably be a good idea to replace this function with simple
52  * calls to QDesktopServices::storageLocation(). Note that it returns the
53  * "Local Settings" application data directory though, so our installers
54  * would need to be updated as well.
55  */
56 
57  /* Find the location of %PROGRAMFILES% */
58  if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, folder, &idl))) {
59  /* Get the path from the IDL */
60  result = SHGetPathFromIDList(idl, path);
61  SHGetMalloc(&m);
62  if (m) {
63  m->Release();
64  }
65  if (SUCCEEDED(result)) {
66 #if defined(UNICODE)
67  return QString::fromUtf16(static_cast<const ushort *>(path));
68 #else
69  return QString::fromLocal8Bit(static_cast<const char *>(path));
70 #endif
71  }
72  }
73  return defaultPath;
74 }
75 
76 /** Gets the location of the user's %PROGRAMFILES% folder. */
77 QString
79 {
81  CSIDL_PROGRAM_FILES, QDir::rootPath() + "\\Program Files");
82 }
83 
84 /** Gets the location of the user's %APPDATA% folder. */
85 QString
87 {
89  CSIDL_LOCAL_APPDATA, QDir::homePath() + "\\Application Data");
90 }
91 
92 /** Returns the value in keyName at keyLocation.
93  * Returns an empty QString if the keyName doesn't exist */
94 QString
95 win32_registry_get_key_value(QString keyLocation, QString keyName)
96 {
97  HKEY key;
98  char data[255] = {0};
99  DWORD size = sizeof(data);
100 
101  /* Open the key for reading (opens new key if it doesn't exist) */
102  if (RegOpenKeyExA(HKEY_CURRENT_USER,
103  qPrintable(keyLocation),
104  0L, KEY_READ, &key) == ERROR_SUCCESS) {
105 
106  /* Key exists, so read the value into data */
107  RegQueryValueExA(key, qPrintable(keyName),
108  NULL, NULL, (LPBYTE)data, &size);
109  }
110 
111  /* Close anything that was opened */
112  RegCloseKey(key);
113 
114  return QString(data);
115 }
116 
117 /** Creates and/or sets the key to the specified value */
118 void
119 win32_registry_set_key_value(QString keyLocation, QString keyName, QString keyValue)
120 {
121  HKEY key;
122 
123  /* Open the key for writing (opens new key if it doesn't exist */
124  if (RegOpenKeyExA(HKEY_CURRENT_USER,
125  qPrintable(keyLocation),
126  0, KEY_WRITE, &key) != ERROR_SUCCESS) {
127 
128  /* Key didn't exist, so write the newly opened key */
129  RegCreateKeyExA(HKEY_CURRENT_USER,
130  qPrintable(keyLocation),
131  0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
132  &key, NULL);
133  }
134 
135  /* Save the value in the key */
136  RegSetValueExA(key, qPrintable(keyName), 0, REG_SZ,
137  (BYTE *)qPrintable(keyValue),
138  (DWORD)keyValue.length() + 1); // include null terminator
139 
140  /* Close the key */
141  RegCloseKey(key);
142 }
143 
144 /** Removes the key from the registry if it exists */
145 void
146 win32_registry_remove_key(QString keyLocation, QString keyName)
147 {
148  HKEY key;
149 
150  /* Open the key for writing (opens new key if it doesn't exist */
151  if (RegOpenKeyExA(HKEY_CURRENT_USER,
152  qPrintable(keyLocation),
153  0, KEY_SET_VALUE, &key) == ERROR_SUCCESS) {
154 
155  /* Key exists so delete it */
156  RegDeleteValueA(key, qPrintable(keyName));
157  }
158 
159  /* Close anything that was opened */
160  RegCloseKey(key);
161 }
162 
163 /**
164  * Callback for EnumThreadWindows which sends the WM_QUIT message
165  */
166 BOOL CALLBACK
167 quitWindowCallback(HWND hwnd, LPARAM targetPID)
168 {
169  DWORD hwndPID = 0;
170 
171  /* If the process ID for hwnd matches the target PID, post
172  WM_QUIT to the window */
173  GetWindowThreadProcessId(hwnd, &hwndPID);
174  if (hwndPID == (DWORD)targetPID)
175  PostMessage(hwnd, WM_QUIT, 0, (LPARAM)NULL);
176  return TRUE;
177 }
178 
179 /**
180  * Close process with the specified PID. Sends WM_QUIT to all
181  * top-level windows.
182  */
183 void
185 {
186  /* Send WM_QUIT to all windows */
187  EnumWindows(&quitWindowCallback, (LPARAM)pid);
188  /* At this point we could kill the main thread, but how do we find
189  the ID of the main thread? We can find the ID of all threads
190  but killing them all seems to cause a problem for Firefox */
191  //PostThreadMessage(thread.th32ThreadID, WM_CLOSE, 0, (LPARAM)NULL);
192 }
193 
194 /**
195  * Close all processes started from the specified filename. Sends
196  * WM_QUIT to all top-level windows. Filename should be given in
197  * lowercase, and comparison is case insensitive. Note: the MSDN
198  * documentation for WM_QUIT states that the message should not be
199  * sent by PostMessage(). However, sending WM_CLOSE leaves Firefox
200  * running, whereas WM_QUIT seems to work.
201  */
202 void
204 {
205  /* Get list of running processes */
206  QHash<qint64, QString> procList = win32_process_list();
207 
208  /* On old versions of Windows win32_process_list() will return
209  an empty list. In this case, just keep Vidalia open */
210  if (procList.isEmpty()) {
211  return;
212  }
213 
214  /* Loop over all processes */
215  QHashIterator<qint64, QString> i(procList);
216  while (i.hasNext()) {
217  i.next();
218  if (i.value().toLower() == filename) {
219  /* Kill this process */
220  win32_end_process_by_pid((DWORD)i.key());
221  }
222  }
223 }
224 
225 /** Returns a list of all currently active processes, including their pid
226  * and exe filename. */
227 QHash<qint64, QString>
229 {
230  QHash<qint64, QString> procList;
231  CreateToolhelp32Snapshot_fn pCreateToolhelp32Snapshot;
232  Process32First_fn pProcess32First;
233  Process32Next_fn pProcess32Next;
234  HANDLE hSnapshot;
235  PROCESSENTRY32 proc;
236  QString exeFile;
237  qint64 pid;
238 
239  /* Load the tool help functions */
240  pCreateToolhelp32Snapshot =
241  (CreateToolhelp32Snapshot_fn)QLibrary::resolve("kernel32", "CreateToolhelp32Snapshot");
242  pProcess32First = (Process32First_fn)QLibrary::resolve("kernel32", "Process32First");
243  pProcess32Next = (Process32Next_fn)QLibrary::resolve("kernel32", "Process32Next");
244 
245  if (!pCreateToolhelp32Snapshot || !pProcess32First || !pProcess32Next) {
246  qWarning("Unable to load tool help functions. Running process information "
247  "will be unavailable.");
248  return QHash<qint64, QString>();
249  }
250 
251  /* Create a snapshot of all active processes */
252  hSnapshot = pCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
253  if (hSnapshot != INVALID_HANDLE_VALUE) {
254  proc.dwSize = sizeof(PROCESSENTRY32);
255 
256  /* Iterate through all the processes in the snapshot */
257  if (pProcess32First(hSnapshot, &proc)) {
258  do {
259  /* Extract the PID and exe filename from the process record */
260  pid = (qint64)proc.th32ProcessID;
261  exeFile = QString::fromAscii((const char *)proc.szExeFile);
262 
263  /* Add this process to our list */
264  procList.insert(pid, exeFile);
265  } while (pProcess32Next(hSnapshot, &proc));
266  }
267  CloseHandle(hSnapshot);
268  }
269  return procList;
270 }
271 
BOOL CALLBACK quitWindowCallback(HWND hwnd, LPARAM targetPID)
Definition: win32.cpp:167
QString win32_registry_get_key_value(QString keyLocation, QString keyName)
Definition: win32.cpp:95
void win32_registry_set_key_value(QString keyLocation, QString keyName, QString keyValue)
Definition: win32.cpp:119
QHash< qint64, QString > win32_process_list()
Definition: win32.cpp:228
BOOL(WINAPI * Process32First_fn)(HANDLE, LPPROCESSENTRY32)
Definition: win32.cpp:37
BOOL(WINAPI * Process32Next_fn)(HANDLE, LPPROCESSENTRY32)
Definition: win32.cpp:38
QString i(QString str)
Definition: html.cpp:32
void win32_end_process_by_filename(QString filename)
Definition: win32.cpp:203
HANDLE(WINAPI * CreateToolhelp32Snapshot_fn)(DWORD, DWORD)
Definition: win32.cpp:36
QString win32_get_folder_location(int folder, QString defaultPath)
Definition: win32.cpp:44
void win32_registry_remove_key(QString keyLocation, QString keyName)
Definition: win32.cpp:146
void win32_end_process_by_pid(DWORD pid)
Definition: win32.cpp:184
QString win32_program_files_folder()
Definition: win32.cpp:78
QString win32_app_data_folder()
Definition: win32.cpp:86