blob: e7a6b4ab8e96c9b1d3f6ede5748dfdb401e64669 [file] [log] [blame]
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +00001/*
2 * WineCfg configuration management
3 *
4 * Copyright 2002 Jaco Greeff
5 * Copyright 2003 Dimitrie O. Paun
Mike Hearna5ce4ee2004-09-28 03:16:43 +00006 * Copyright 2003-2004 Mike Hearn
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +00007 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
Mike Hearn21840302003-09-15 22:09:03 +000022 * TODO: (in rough order of priority)
23 * - A mind bogglingly vast amount of stuff
24 *
Mike Hearn21840302003-09-15 22:09:03 +000025 * - Implement autodetect for drive configuration
26 * - Figure out whether we need the virtual vs real drive selection stuff at the top of the property page
27 * - Implement explicit mode vs instant-apply mode
Mike Hearn21840302003-09-15 22:09:03 +000028 * - DLL editing
Mike Hearn21840302003-09-15 22:09:03 +000029 * - Multimedia page
30 * - Settings migration code (from old configs)
Mike Hearn81be7f72003-09-22 19:26:10 +000031 * - Clean up resource.h, it's a bog
Mike Hearn21840302003-09-15 22:09:03 +000032 *
Mike Hearn5d249cb2003-09-18 04:32:01 +000033 * Minor things that should be done someday:
34 * - Make the desktop size UI a combo box, with a Custom option, so it's more obvious what you might want to choose here
35 *
Mike Hearn498e1ce2003-09-30 00:27:55 +000036 * BUGS:
Mike Hearn498e1ce2003-09-30 00:27:55 +000037 * - x11drv page triggers key writes on entry
38 *
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +000039 */
40
Mike Hearncd0b7892003-09-08 18:58:07 +000041#include <assert.h>
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +000042#include <stdio.h>
43#include <limits.h>
44#include <windows.h>
Matthew Davison5101dfc2003-04-27 00:33:07 +000045#include <winreg.h>
46#include <wine/debug.h>
47
48WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +000049
50#include "winecfg.h"
51
Alexandre Julliard3db78b82003-08-30 00:40:46 +000052HKEY configKey = NULL;
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +000053
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +000054
Mike Hearncd0b7892003-09-08 18:58:07 +000055int initialize(void) {
56 DWORD res = RegCreateKey(HKEY_LOCAL_MACHINE, WINE_KEY_ROOT, &configKey);
57 if (res != ERROR_SUCCESS) {
58 WINE_ERR("RegOpenKey failed on wine config key (%ld)\n", res);
59 return 1;
60 }
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +000061 return 0;
62}
63
Mike Hearncd0b7892003-09-08 18:58:07 +000064
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +000065/*****************************************************************************
Mike Hearn4e1afc62003-08-30 00:27:08 +000066 * getConfigValue: Retrieves a configuration value from the registry
67 *
Daniel Marmier8e5bb202003-10-09 04:39:01 +000068 * const char *subKey : the name of the config section
69 * const char *valueName : the name of the config value
70 * const char *defaultResult : if the key isn't found, return this value instead
Mike Hearn4e1afc62003-08-30 00:27:08 +000071 *
Mike Hearncd0b7892003-09-08 18:58:07 +000072 * Returns a buffer holding the value if successful, NULL if not. Caller is responsible for freeing the result.
Mike Hearn4e1afc62003-08-30 00:27:08 +000073 *
Matthew Davison5101dfc2003-04-27 00:33:07 +000074 */
Daniel Marmier8e5bb202003-10-09 04:39:01 +000075char *getConfigValue (const char *subkey, const char *valueName, const char *defaultResult)
Matthew Davison5101dfc2003-04-27 00:33:07 +000076{
Mike Hearncd0b7892003-09-08 18:58:07 +000077 char *buffer = NULL;
Mike Hearn4e1afc62003-08-30 00:27:08 +000078 DWORD dataLength;
79 HKEY hSubKey = NULL;
Mike Hearncd0b7892003-09-08 18:58:07 +000080 DWORD res;
Matthew Davison5101dfc2003-04-27 00:33:07 +000081
Mike Hearn4e1afc62003-08-30 00:27:08 +000082 WINE_TRACE("subkey=%s, valueName=%s, defaultResult=%s\n", subkey, valueName, defaultResult);
Mike Hearncd0b7892003-09-08 18:58:07 +000083
84 res = RegOpenKeyEx( configKey, subkey, 0, KEY_ALL_ACCESS, &hSubKey );
85 if(res != ERROR_SUCCESS) {
Matthew Davison5101dfc2003-04-27 00:33:07 +000086 if( res==ERROR_FILE_NOT_FOUND )
87 {
Mike Hearn4e1afc62003-08-30 00:27:08 +000088 WINE_TRACE("Section key not present - using default\n");
Mike Hearna5ce4ee2004-09-28 03:16:43 +000089 return defaultResult ? strdup(defaultResult) : NULL;
Matthew Davison5101dfc2003-04-27 00:33:07 +000090 }
91 else
92 {
Mike Hearn4e1afc62003-08-30 00:27:08 +000093 WINE_ERR("RegOpenKey failed on wine config key (res=%ld)\n", res);
Matthew Davison5101dfc2003-04-27 00:33:07 +000094 }
95 goto end;
96 }
Mike Hearn498e1ce2003-09-30 00:27:55 +000097
Matthew Davison5101dfc2003-04-27 00:33:07 +000098 res = RegQueryValueExA( hSubKey, valueName, NULL, NULL, NULL, &dataLength);
Mike Hearncd0b7892003-09-08 18:58:07 +000099 if( res == ERROR_FILE_NOT_FOUND ) {
Matthew Davison5101dfc2003-04-27 00:33:07 +0000100 WINE_TRACE("Value not present - using default\n");
Mike Hearna5ce4ee2004-09-28 03:16:43 +0000101 buffer = defaultResult ? strdup(defaultResult) : NULL;
Mike Hearncd0b7892003-09-08 18:58:07 +0000102 goto end;
103 } else if( res!=ERROR_SUCCESS ) {
Mike Hearn4e1afc62003-08-30 00:27:08 +0000104 WINE_ERR("Couldn't query value's length (res=%ld)\n", res );
Matthew Davison5101dfc2003-04-27 00:33:07 +0000105 goto end;
106 }
107
Mike Hearncd0b7892003-09-08 18:58:07 +0000108 buffer = malloc(dataLength);
Matthew Davison5101dfc2003-04-27 00:33:07 +0000109 if( buffer==NULL )
110 {
111 WINE_ERR("Couldn't allocate %lu bytes for the value\n", dataLength );
Matthew Davison5101dfc2003-04-27 00:33:07 +0000112 goto end;
113 }
114
Mike Hearn4e1afc62003-08-30 00:27:08 +0000115 RegQueryValueEx(hSubKey, valueName, NULL, NULL, (LPBYTE)buffer, &dataLength);
Matthew Davison5101dfc2003-04-27 00:33:07 +0000116
117end:
Mike Hearn4e1afc62003-08-30 00:27:08 +0000118 if( hSubKey!=NULL )
Matthew Davison5101dfc2003-04-27 00:33:07 +0000119 RegCloseKey( hSubKey );
120
Mike Hearncd0b7892003-09-08 18:58:07 +0000121 return buffer;
Matthew Davison5101dfc2003-04-27 00:33:07 +0000122
123}
124
125/*****************************************************************************
Mike Hearna5ce4ee2004-09-28 03:16:43 +0000126 * setConfigValue : Sets a configuration key in the registry. Section
127 * will be created if it doesn't already exist
Mike Hearn4e1afc62003-08-30 00:27:08 +0000128 *
129 * HKEY hCurrent : the registry key that the configuration is rooted at
Daniel Marmier8e5bb202003-10-09 04:39:01 +0000130 * const char *subKey : the name of the config section
131 * const char *valueName : the name of the config value
132 * const char *value : the value to set the configuration key to
Mike Hearn4e1afc62003-08-30 00:27:08 +0000133 *
134 * Returns 0 on success, non-zero otherwise
Robert van Herk5fd5f382004-01-07 00:43:40 +0000135 *
136 * If *valueName or *value is NULL, an empty section will be created
Mike Hearn4e1afc62003-08-30 00:27:08 +0000137 */
Daniel Marmier8e5bb202003-10-09 04:39:01 +0000138int setConfigValue (const char *subkey, const char *valueName, const char *value) {
Mike Hearn4e1afc62003-08-30 00:27:08 +0000139 DWORD res = 1;
140 HKEY key = NULL;
141
142 WINE_TRACE("subkey=%s, valueName=%s, value=%s\n", subkey, valueName, value);
Mike Hearncd0b7892003-09-08 18:58:07 +0000143
144 assert( subkey != NULL );
Mike Hearn4e1afc62003-08-30 00:27:08 +0000145
Mike Hearncd0b7892003-09-08 18:58:07 +0000146 res = RegCreateKey(configKey, subkey, &key);
Mike Hearn4e1afc62003-08-30 00:27:08 +0000147 if (res != ERROR_SUCCESS) goto end;
Robert van Herk5fd5f382004-01-07 00:43:40 +0000148 if (value == NULL || valueName == NULL) goto end;
Mike Hearn4e1afc62003-08-30 00:27:08 +0000149
150 res = RegSetValueEx(key, valueName, 0, REG_SZ, value, strlen(value) + 1);
151 if (res != ERROR_SUCCESS) goto end;
152
153 res = 0;
154end:
155 if (key) RegCloseKey(key);
156 if (res != 0) WINE_ERR("Unable to set configuration key %s in section %s to %s, res=%ld\n", valueName, subkey, value, res);
157 return res;
158}
159
Mike Hearncd0b7892003-09-08 18:58:07 +0000160/* returns 0 on success, an HRESULT from the registry funtions otherwise */
Daniel Marmier8e5bb202003-10-09 04:39:01 +0000161HRESULT doesConfigValueExist(const char *subkey, const char *valueName) {
Mike Hearncd0b7892003-09-08 18:58:07 +0000162 HRESULT hr;
163 HKEY key;
164
165 WINE_TRACE("subkey=%s, valueName=%s - ", subkey, valueName);
166
167 hr = RegOpenKeyEx(configKey, subkey, 0, KEY_READ, &key);
168 if (hr != S_OK) {
169 WINE_TRACE("no: subkey does not exist\n");
170 return hr;
171 }
172
173 hr = RegQueryValueEx(key, valueName, NULL, NULL, NULL, NULL);
174 if (hr != S_OK) {
175 WINE_TRACE("no: key does not exist\n");
176 return hr;
177 }
178
179 RegCloseKey(key);
180 WINE_TRACE("yes\n");
181 return S_OK;
182}
183
184/* removes the requested value from the registry, however, does not remove the section if empty. Returns S_OK (0) on success. */
Daniel Marmier8e5bb202003-10-09 04:39:01 +0000185HRESULT removeConfigValue(const char *subkey, const char *valueName) {
Mike Hearncd0b7892003-09-08 18:58:07 +0000186 HRESULT hr;
187 HKEY key;
188 WINE_TRACE("subkey=%s, valueName=%s\n", subkey, valueName);
189
190 hr = RegOpenKeyEx(configKey, subkey, 0, KEY_READ, &key);
191 if (hr != S_OK) return hr;
192
193 hr = RegDeleteValue(key, valueName);
194 if (hr != ERROR_SUCCESS) return hr;
195
196 return S_OK;
197}
198
Mike Hearn5a2cde62003-09-17 22:40:38 +0000199/* removes the requested configuration section (subkey) from the registry, assuming it exists */
200/* this function might be slightly pointless, but in future we may wish to treat recursion specially etc, so we'll keep it for now */
201HRESULT removeConfigSection(char *section) {
202 HRESULT hr;
203 WINE_TRACE("section=%s\n", section);
204
205 return hr = RegDeleteKey(configKey, section);
206}
207
Mike Hearn4e1afc62003-08-30 00:27:08 +0000208
Mike Hearncd0b7892003-09-08 18:58:07 +0000209/* ========================================================================= */
210/* Transaction management code */
211
212struct transaction *tqhead, *tqtail;
213int instantApply = 1;
214
215void destroyTransaction(struct transaction *trans) {
216 assert( trans != NULL );
Mike Hearn4e1afc62003-08-30 00:27:08 +0000217
Mike Hearncd0b7892003-09-08 18:58:07 +0000218 WINE_TRACE("destroying %p\n", trans);
219
220 free(trans->section);
Mike Hearn5a2cde62003-09-17 22:40:38 +0000221 if (trans->key) free(trans->key);
Mike Hearncd0b7892003-09-08 18:58:07 +0000222 if (trans->newValue) free(trans->newValue);
223
224 if (trans->next) trans->next->prev = trans->prev;
225 if (trans->prev) trans->prev->next = trans->next;
226 if (trans == tqhead) tqhead = NULL;
227 if (trans == tqtail) tqtail = NULL;
228
229 free(trans);
230}
231
Daniel Marmier8e5bb202003-10-09 04:39:01 +0000232void addTransaction(const char *section, const char *key, enum transaction_action action, const char *newValue) {
Mike Hearn5a2cde62003-09-17 22:40:38 +0000233 struct transaction *trans = calloc(sizeof(struct transaction),1);
Mike Hearncd0b7892003-09-08 18:58:07 +0000234
235 assert( section != NULL );
Mike Hearncd0b7892003-09-08 18:58:07 +0000236 if (action == ACTION_SET) assert( newValue != NULL );
Mike Hearn5a2cde62003-09-17 22:40:38 +0000237 if (action == ACTION_SET) assert( key != NULL );
238
Mike Hearn0f4c6c92003-09-10 03:41:44 +0000239 trans->section = strdup(section);
Mike Hearn5a2cde62003-09-17 22:40:38 +0000240 if (key) trans->key = strdup(key);
241 if (newValue) trans->newValue = strdup(newValue);
Mike Hearncd0b7892003-09-08 18:58:07 +0000242 trans->action = action;
Mike Hearncd0b7892003-09-08 18:58:07 +0000243
244 if (tqtail == NULL) {
245 tqtail = trans;
246 tqhead = tqtail;
247 } else {
248 tqhead->next = trans;
249 trans->prev = tqhead;
250 tqhead = trans;
Mike Hearn4e1afc62003-08-30 00:27:08 +0000251 }
252
Mike Hearn0f4c6c92003-09-10 03:41:44 +0000253 if (instantApply) {
254 processTransaction(trans);
255 destroyTransaction(trans);
256 }
Mike Hearncd0b7892003-09-08 18:58:07 +0000257}
258
259void processTransaction(struct transaction *trans) {
260 if (trans->action == ACTION_SET) {
261 WINE_TRACE("Setting %s\\%s to '%s'\n", trans->section, trans->key, trans->newValue);
262 setConfigValue(trans->section, trans->key, trans->newValue);
263 } else if (trans->action == ACTION_REMOVE) {
Mike Hearn5a2cde62003-09-17 22:40:38 +0000264 if (trans->key) {
265 WINE_TRACE("Removing %s\\%s\n", trans->section, trans->key);
266 removeConfigValue(trans->section, trans->key);
267 } else {
268 /* NULL key means remove that section entirely */
269 WINE_TRACE("Removing section %s\n", trans->section);
270 removeConfigSection(trans->section);
271 }
Mike Hearncd0b7892003-09-08 18:58:07 +0000272 }
273 /* TODO: implement notifications here */
274}
275
Mike Hearn0f4c6c92003-09-10 03:41:44 +0000276void processTransQueue(void)
277{
Mike Hearncd0b7892003-09-08 18:58:07 +0000278 WINE_TRACE("\n");
Mike Hearn0f4c6c92003-09-10 03:41:44 +0000279 while (tqtail != NULL) {
280 struct transaction *next = tqtail->next;
281 processTransaction(tqtail);
282 destroyTransaction(tqtail);
283 tqtail = next;
Mike Hearncd0b7892003-09-08 18:58:07 +0000284 }
Dimitrie O. Paun82ce2cc2003-03-31 19:41:55 +0000285}
Mike Hearn0f4c6c92003-09-10 03:41:44 +0000286
Mike Hearn0f4c6c92003-09-10 03:41:44 +0000287/* ================================== utility functions ============================ */
288
Mike Hearna5ce4ee2004-09-28 03:16:43 +0000289char *currentApp = NULL; /* the app we are currently editing, or NULL if editing global */
290
291/* returns a registry key path suitable for passing to addTransaction */
292char *keypath(char *section)
293{
294 static char *result = NULL;
295
296 if (result) release(result);
297
298 if (currentApp)
299 {
300 result = alloc(strlen("AppDefaults\\") + strlen(currentApp) + 2 /* \\ */ + strlen(section) + 1 /* terminator */);
301 sprintf(result, "AppDefaults\\%s\\%s", currentApp, section);
302 }
303 else
304 {
305 result = strdupA(section);
306 }
307
308 return result;
309}
310
Mike Hearn0f4c6c92003-09-10 03:41:44 +0000311/* returns a string with the window text of the dialog item. user is responsible for freeing the result */
312char *getDialogItemText(HWND hDlg, WORD controlID) {
313 HWND item = GetDlgItem(hDlg, controlID);
314 int len = GetWindowTextLength(item) + 1;
315 char *result = malloc(len);
316 if (GetWindowText(item, result, len) == 0) return NULL;
317 return result;
318}
Chris Morgan49f0dd32004-05-04 02:56:46 +0000319
320void PRINTERROR(void)
321{
322 LPSTR msg;
323
324 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
325 0, GetLastError(), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
326 (LPSTR)&msg, 0, NULL);
327 WINE_TRACE("error: '%s'\n", msg);
328}