winecfg: Fix deletion of registry keys for Wow64 mode.
[wine] / programs / winecfg / winecfg.c
1 /*
2  * WineCfg configuration management
3  *
4  * Copyright 2002 Jaco Greeff
5  * Copyright 2003 Dimitrie O. Paun
6  * Copyright 2003-2004 Mike Hearn
7  *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  *
22  * TODO:
23  *  - Use unicode
24  *  - Icons in listviews/icons
25  *  - Better add app dialog, scan c: for EXE files and add to list in background
26  *  - Use [GNOME] HIG style groupboxes rather than win32 style (looks nicer, imho)
27  *
28  */
29
30 #define WIN32_LEAN_AND_MEAN
31
32 #include <assert.h>
33 #include <stdio.h>
34 #include <limits.h>
35 #include <windows.h>
36 #include <winreg.h>
37 #include <wine/unicode.h>
38 #include <wine/debug.h>
39 #include <wine/list.h>
40
41 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
42
43 #include "winecfg.h"
44 #include "resource.h"
45
46 static const int is_win64 = (sizeof(void *) > sizeof(int));
47
48 HKEY config_key = NULL;
49 HMENU hPopupMenus = 0;
50
51
52 /* this is called from the WM_SHOWWINDOW handlers of each tab page.
53  *
54  * it's a nasty hack, necessary because the property sheet insists on resetting the window title
55  * to the title of the tab, which is utterly useless. dropping the property sheet is on the todo list.
56  */
57 void set_window_title(HWND dialog)
58 {
59     WCHAR newtitle[256];
60
61     /* update the window title  */
62     if (current_app)
63     {
64         WCHAR apptitle[256];
65         LoadStringW (GetModuleHandle(NULL), IDS_WINECFG_TITLE_APP, apptitle,
66             sizeof(apptitle)/sizeof(apptitle[0]));
67         wsprintfW (newtitle, apptitle, current_app);
68     }
69     else
70     {
71         LoadStringW (GetModuleHandle(NULL), IDS_WINECFG_TITLE, newtitle,
72             sizeof(newtitle)/sizeof(newtitle[0]));
73     }
74
75     WINE_TRACE("setting title to %s\n", wine_dbgstr_w (newtitle));
76     SendMessageW (GetParent(dialog), PSM_SETTITLEW, 0, (LPARAM) newtitle);
77 }
78
79
80 WCHAR* load_string (UINT id)
81 {
82     WCHAR buf[1024];
83     int len;
84     WCHAR* newStr;
85
86     LoadStringW (GetModuleHandle (NULL), id, buf, sizeof(buf)/sizeof(buf[0]));
87
88     len = lstrlenW (buf);
89     newStr = HeapAlloc (GetProcessHeap(), 0, (len + 1) * sizeof (WCHAR));
90     memcpy (newStr, buf, len * sizeof (WCHAR));
91     newStr[len] = 0;
92     return newStr;
93 }
94
95 /**
96  * get_config_key: Retrieves a configuration value from the registry
97  *
98  * char *subkey : the name of the config section
99  * char *name : the name of the config value
100  * char *default : if the key isn't found, return this value instead
101  *
102  * Returns a buffer holding the value if successful, NULL if
103  * not. Caller is responsible for releasing the result.
104  *
105  */
106 static WCHAR *get_config_key (HKEY root, const WCHAR *subkey, const WCHAR *name, const WCHAR *def)
107 {
108     LPWSTR buffer = NULL;
109     DWORD len;
110     HKEY hSubKey = NULL;
111     DWORD res;
112
113     WINE_TRACE("subkey=%s, name=%s, def=%s\n", wine_dbgstr_w(subkey),
114                wine_dbgstr_w(name), wine_dbgstr_w(def));
115
116     res = RegOpenKeyExW(root, subkey, 0, MAXIMUM_ALLOWED, &hSubKey);
117     if (res != ERROR_SUCCESS)
118     {
119         if (res == ERROR_FILE_NOT_FOUND)
120         {
121             WINE_TRACE("Section key not present - using default\n");
122             return def ? strdupW(def) : NULL;
123         }
124         else
125         {
126             WINE_ERR("RegOpenKey failed on wine config key (res=%d)\n", res);
127         }
128         goto end;
129     }
130
131     res = RegQueryValueExW(hSubKey, name, NULL, NULL, NULL, &len);
132     if (res == ERROR_FILE_NOT_FOUND)
133     {
134         WINE_TRACE("Value not present - using default\n");
135         buffer = def ? strdupW(def) : NULL;
136         goto end;
137     } else if (res != ERROR_SUCCESS)
138     {
139         WINE_ERR("Couldn't query value's length (res=%d)\n", res);
140         goto end;
141     }
142
143     buffer = HeapAlloc(GetProcessHeap(), 0, len + sizeof(WCHAR));
144
145     RegQueryValueExW(hSubKey, name, NULL, NULL, (LPBYTE) buffer, &len);
146
147     WINE_TRACE("buffer=%s\n", wine_dbgstr_w(buffer));
148 end:
149     RegCloseKey(hSubKey);
150
151     return buffer;
152 }
153
154 /**
155  * set_config_key: convenience wrapper to set a key/value pair
156  *
157  * const char *subKey : the name of the config section
158  * const char *valueName : the name of the config value
159  * const char *value : the value to set the configuration key to
160  *
161  * Returns 0 on success, non-zero otherwise
162  *
163  * If valueName or value is NULL, an empty section will be created
164  */
165 static int set_config_key(HKEY root, const WCHAR *subkey, REGSAM access, const WCHAR *name, const void *value, DWORD type)
166 {
167     DWORD res = 1;
168     HKEY key = NULL;
169
170     WINE_TRACE("subkey=%s: name=%s, value=%p, type=%d\n", wine_dbgstr_w(subkey),
171                wine_dbgstr_w(name), value, type);
172
173     assert( subkey != NULL );
174
175     if (subkey[0])
176     {
177         res = RegCreateKeyExW( root, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
178                                access, NULL, &key, NULL );
179         if (res != ERROR_SUCCESS) goto end;
180     }
181     else key = root;
182     if (name == NULL || value == NULL) goto end;
183
184     switch (type)
185     {
186         case REG_SZ: res = RegSetValueExW(key, name, 0, REG_SZ, value, (lstrlenW(value)+1)*sizeof(WCHAR)); break;
187         case REG_DWORD: res = RegSetValueExW(key, name, 0, REG_DWORD, value, sizeof(DWORD)); break;
188     }
189     if (res != ERROR_SUCCESS) goto end;
190
191     res = 0;
192 end:
193     if (key && key != root) RegCloseKey(key);
194     if (res != 0)
195         WINE_ERR("Unable to set configuration key %s in section %s, res=%d\n",
196                  wine_dbgstr_w(name), wine_dbgstr_w(subkey), res);
197     return res;
198 }
199
200 /* ========================================================================= */
201
202 /* This code exists for the following reasons:
203  *
204  * - It makes working with the registry easier
205  * - By storing a mini cache of the registry, we can more easily implement
206  *   cancel/revert and apply. The 'settings list' is an overlay on top of
207  *   the actual registry data that we can write out at will.
208  *
209  * Rather than model a tree in memory, we simply store each absolute (rooted
210  * at the config key) path.
211  *
212  */
213
214 struct setting
215 {
216     struct list entry;
217     HKEY root;    /* the key on which path is rooted */
218     WCHAR *path;   /* path in the registry rooted at root  */
219     WCHAR *name;   /* name of the registry value. if null, this means delete the key  */
220     WCHAR *value;  /* contents of the registry value. if null, this means delete the value  */
221     DWORD type;   /* type of registry value. REG_SZ or REG_DWORD for now */
222 };
223
224 struct list *settings;
225
226 static void free_setting(struct setting *setting)
227 {
228     assert( setting != NULL );
229     assert( setting->path );
230
231     WINE_TRACE("destroying %p: %s\n", setting,
232                wine_dbgstr_w(setting->path));
233
234     HeapFree(GetProcessHeap(), 0, setting->path);
235     HeapFree(GetProcessHeap(), 0, setting->name);
236     HeapFree(GetProcessHeap(), 0, setting->value);
237
238     list_remove(&setting->entry);
239
240     HeapFree(GetProcessHeap(), 0, setting);
241 }
242
243 /**
244  * Returns the contents of the value at path. If not in the settings
245  * list, it will be fetched from the registry - failing that, the
246  * default will be used.
247  *
248  * If already in the list, the contents as given there will be
249  * returned. You are expected to HeapFree the result.
250  */
251 WCHAR *get_reg_keyW(HKEY root, const WCHAR *path, const WCHAR *name, const WCHAR *def)
252 {
253     struct list *cursor;
254     struct setting *s;
255     WCHAR *val;
256
257     WINE_TRACE("path=%s, name=%s, def=%s\n", wine_dbgstr_w(path),
258                wine_dbgstr_w(name), wine_dbgstr_w(def));
259
260     /* check if it's in the list */
261     LIST_FOR_EACH( cursor, settings )
262     {
263         s = LIST_ENTRY(cursor, struct setting, entry);
264
265         if (root != s->root) continue;
266         if (lstrcmpiW(path, s->path) != 0) continue;
267         if (!s->name) continue;
268         if (lstrcmpiW(name, s->name) != 0) continue;
269
270         WINE_TRACE("found %s:%s in settings list, returning %s\n",
271                    wine_dbgstr_w(path), wine_dbgstr_w(name),
272                    wine_dbgstr_w(s->value));
273         return s->value ? strdupW(s->value) : NULL;
274     }
275
276     /* no, so get from the registry */
277     val = get_config_key(root, path, name, def);
278
279     WINE_TRACE("returning %s\n", wine_dbgstr_w(val));
280
281     return val;
282 }
283
284 char *get_reg_key(HKEY root, const char *path, const char *name, const char *def)
285 {
286     WCHAR *wpath, *wname, *wdef = NULL, *wRet = NULL;
287     char *szRet = NULL;
288     int len;
289
290     WINE_TRACE("path=%s, name=%s, def=%s\n", path, name, def);
291
292     wpath = HeapAlloc(GetProcessHeap(), 0, (strlen(path)+1)*sizeof(WCHAR));
293     wname = HeapAlloc(GetProcessHeap(), 0, (strlen(name)+1)*sizeof(WCHAR));
294
295     MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, strlen(path)+1);
296     MultiByteToWideChar(CP_ACP, 0, name, -1, wname, strlen(name)+1);
297
298     if (def)
299     {
300         wdef = HeapAlloc(GetProcessHeap(), 0, (strlen(def)+1)*sizeof(WCHAR));
301         MultiByteToWideChar(CP_ACP, 0, def, -1, wdef, strlen(def)+1);
302     }
303
304     wRet = get_reg_keyW(root, wpath, wname, wdef);
305
306     len = WideCharToMultiByte(CP_ACP, 0, wRet, -1, NULL, 0, NULL, NULL);
307     if (len)
308     {
309         szRet = HeapAlloc(GetProcessHeap(), 0, len);
310         WideCharToMultiByte(CP_ACP, 0, wRet, -1, szRet, len, NULL, NULL);
311     }
312
313     HeapFree(GetProcessHeap(), 0, wpath);
314     HeapFree(GetProcessHeap(), 0, wname);
315     HeapFree(GetProcessHeap(), 0, wdef);
316     HeapFree(GetProcessHeap(), 0, wRet);
317
318     return szRet;
319 }
320
321 /**
322  * Used to set a registry key.
323  *
324  * path is rooted at the config key, ie use "Version" or
325  * "AppDefaults\\fooapp.exe\\Version". You can use keypath()
326  * to get such a string.
327  *
328  * name is the value name, or NULL to delete the path.
329  *
330  * value is what to set the value to, or NULL to delete it.
331  *
332  * type is REG_SZ or REG_DWORD.
333  *
334  * These values will be copied when necessary.
335  */
336 static void set_reg_key_ex(HKEY root, const WCHAR *path, const WCHAR *name, const void *value, DWORD type)
337 {
338     struct list *cursor;
339     struct setting *s;
340
341     assert( path != NULL );
342
343     WINE_TRACE("path=%s, name=%s, value=%s\n", wine_dbgstr_w(path),
344                wine_dbgstr_w(name), wine_dbgstr_w(value));
345
346     /* firstly, see if we already set this setting  */
347     LIST_FOR_EACH( cursor, settings )
348     {
349         struct setting *s = LIST_ENTRY(cursor, struct setting, entry);
350
351         if (root != s->root) continue;
352         if (lstrcmpiW(s->path, path) != 0) continue;
353         if ((s->name && name) && lstrcmpiW(s->name, name) != 0) continue;
354
355         /* are we attempting a double delete? */
356         if (!s->name && !name) return;
357
358         /* do we want to undelete this key? */
359         if (!s->name && name) s->name = strdupW(name);
360
361         /* yes, we have already set it, so just replace the content and return  */
362         HeapFree(GetProcessHeap(), 0, s->value);
363         s->type = type;
364         switch (type)
365         {
366             case REG_SZ:
367                 s->value = value ? strdupW(value) : NULL;
368                 break;
369             case REG_DWORD:
370                 s->value = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
371                 memcpy( s->value, value, sizeof(DWORD) );
372                 break;
373         }
374
375         /* are we deleting this key? this won't remove any of the
376          * children from the overlay so if the user adds it again in
377          * that session it will appear to undelete the settings, but
378          * in reality only the settings actually modified by the user
379          * in that session will be restored. we might want to fix this
380          * corner case in future by actually deleting all the children
381          * here so that once it's gone, it's gone.
382          */
383         if (!name) s->name = NULL;
384
385         return;
386     }
387
388     /* otherwise add a new setting for it  */
389     s = HeapAlloc(GetProcessHeap(), 0, sizeof(struct setting));
390     s->root  = root;
391     s->path  = strdupW(path);
392     s->name  = name  ? strdupW(name)  : NULL;
393     s->type  = type;
394     switch (type)
395     {
396         case REG_SZ:
397             s->value = value ? strdupW(value) : NULL;
398             break;
399         case REG_DWORD:
400             s->value = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
401             memcpy( s->value, value, sizeof(DWORD) );
402             break;
403     }
404
405     list_add_tail(settings, &s->entry);
406 }
407
408 void set_reg_key(HKEY root, const char *path, const char *name, const char *value)
409 {
410     WCHAR *wpath, *wname = NULL, *wvalue = NULL;
411
412     wpath = HeapAlloc(GetProcessHeap(), 0, (strlen(path)+1)*sizeof(WCHAR));
413     MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, strlen(path)+1);
414
415     if (name)
416     {
417         wname = HeapAlloc(GetProcessHeap(), 0, (strlen(name)+1)*sizeof(WCHAR));
418         MultiByteToWideChar(CP_ACP, 0, name, -1, wname, strlen(name)+1);
419     }
420
421     if (value)
422     {
423         wvalue = HeapAlloc(GetProcessHeap(), 0, (strlen(value)+1)*sizeof(WCHAR));
424         MultiByteToWideChar(CP_ACP, 0, value, -1, wvalue, strlen(value)+1);
425     }
426
427     set_reg_key_ex(root, wpath, wname, wvalue, REG_SZ);
428
429     HeapFree(GetProcessHeap(), 0, wpath);
430     HeapFree(GetProcessHeap(), 0, wname);
431     HeapFree(GetProcessHeap(), 0, wvalue);
432 }
433
434 void set_reg_key_dword(HKEY root, const char *path, const char *name, DWORD value)
435 {
436     WCHAR *wpath, *wname;
437
438     wpath = HeapAlloc(GetProcessHeap(), 0, (strlen(path)+1)*sizeof(WCHAR));
439     wname = HeapAlloc(GetProcessHeap(), 0, (strlen(name)+1)*sizeof(WCHAR));
440
441     MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, strlen(path)+1);
442     MultiByteToWideChar(CP_ACP, 0, name, -1, wname, strlen(name)+1);
443
444     set_reg_key_ex(root, wpath, wname, &value, REG_DWORD);
445
446     HeapFree(GetProcessHeap(), 0, wpath);
447     HeapFree(GetProcessHeap(), 0, wname);
448 }
449
450 void set_reg_keyW(HKEY root, const WCHAR *path, const WCHAR *name, const WCHAR *value)
451 {
452     set_reg_key_ex(root, path, name, value, REG_SZ);
453 }
454
455 void set_reg_key_dwordW(HKEY root, const WCHAR *path, const WCHAR *name, DWORD value)
456 {
457     set_reg_key_ex(root, path, name, &value, REG_DWORD);
458 }
459
460 /**
461  * enumerates the value names at the given path, taking into account
462  * the changes in the settings list.
463  *
464  * you are expected to HeapFree each element of the array, which is null
465  * terminated, as well as the array itself.
466  */
467 static WCHAR **enumerate_valuesW(HKEY root, WCHAR *path)
468 {
469     HKEY key;
470     DWORD res, i = 0, valueslen = 0;
471     WCHAR **values = NULL;
472     struct list *cursor;
473
474     res = RegOpenKeyExW(root, path, 0, MAXIMUM_ALLOWED, &key);
475     if (res == ERROR_SUCCESS)
476     {
477         while (TRUE)
478         {
479             WCHAR name[1024];
480             DWORD namesize = sizeof(name)/sizeof(name[0]);
481             BOOL removed = FALSE;
482
483             /* find out the needed size, allocate a buffer, read the value  */
484             if ((res = RegEnumValueW(key, i, name, &namesize, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
485                 break;
486
487             WINE_TRACE("name=%s\n", wine_dbgstr_w(name));
488
489             /* check if this value name has been removed in the settings list  */
490             LIST_FOR_EACH( cursor, settings )
491             {
492                 struct setting *s = LIST_ENTRY(cursor, struct setting, entry);
493                 if (lstrcmpiW(s->path, path) != 0) continue;
494                 if (lstrcmpiW(s->name, name) != 0) continue;
495
496                 if (!s->value)
497                 {
498                     WINE_TRACE("this key has been removed, so skipping\n");
499                     removed = TRUE;
500                     break;
501                 }
502             }
503
504             if (removed)            /* this value was deleted by the user, so don't include it */
505             {
506                 HeapFree(GetProcessHeap(), 0, name);
507                 i++;
508                 continue;
509             }
510
511             /* grow the array if necessary, add buffer to it, iterate  */
512             if (values) values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(WCHAR*) * (valueslen + 1));
513             else values = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR*));
514
515             values[valueslen++] = strdupW(name);
516             WINE_TRACE("valueslen is now %d\n", valueslen);
517             i++;
518         }
519     }
520     else
521     {
522         WINE_WARN("failed opening registry key %s, res=0x%x\n",
523                   wine_dbgstr_w(path), res);
524     }
525
526     WINE_TRACE("adding settings in list but not registry\n");
527
528     /* now we have to add the values that aren't in the registry but are in the settings list */
529     LIST_FOR_EACH( cursor, settings )
530     {
531         struct setting *setting = LIST_ENTRY(cursor, struct setting, entry);
532         BOOL found = FALSE;
533
534         if (lstrcmpiW(setting->path, path) != 0) continue;
535
536         if (!setting->value) continue;
537
538         for (i = 0; i < valueslen; i++)
539         {
540             if (lstrcmpiW(setting->name, values[i]) == 0)
541             {
542                 found = TRUE;
543                 break;
544             }
545         }
546
547         if (found) continue;
548
549         WINE_TRACE("%s in list but not registry\n", wine_dbgstr_w(setting->name));
550
551         /* otherwise it's been set by the user but isn't in the registry */
552         if (values) values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(WCHAR*) * (valueslen + 1));
553         else values = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR*));
554
555         values[valueslen++] = strdupW(setting->name);
556     }
557
558     WINE_TRACE("adding null terminator\n");
559     if (values)
560     {
561         values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(WCHAR*) * (valueslen + 1));
562         values[valueslen] = NULL;
563     }
564
565     RegCloseKey(key);
566
567     return values;
568 }
569
570 char **enumerate_values(HKEY root, char *path)
571 {
572     WCHAR *wpath;
573     WCHAR **wret;
574     char **ret=NULL;
575     int i=0, len=0;
576
577     wpath = HeapAlloc(GetProcessHeap(), 0, (strlen(path)+1)*sizeof(WCHAR));
578     MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, strlen(path)+1);
579
580     wret = enumerate_valuesW(root, wpath);
581
582     if (wret)
583     {
584         for(len=0; wret[len]; len++);
585         ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(char*));
586
587         /* convert WCHAR ** to char ** and HeapFree each WCHAR * element on our way */
588         for (i=0; i<len; i++)
589         {
590             ret[i] = HeapAlloc(GetProcessHeap(), 0,
591                                (lstrlenW(wret[i]) + 1) * sizeof(char));
592             WideCharToMultiByte(CP_ACP, 0, wret[i], -1, ret[i],
593                                 lstrlenW(wret[i]) + 1, NULL, NULL);
594             HeapFree(GetProcessHeap(), 0, wret[i]);
595         }
596         ret[len] = NULL;
597     }
598
599     HeapFree(GetProcessHeap(), 0, wpath);
600     HeapFree(GetProcessHeap(), 0, wret);
601
602     return ret;
603 }
604
605 /**
606  * returns true if the given key/value pair exists in the registry or
607  * has been written to.
608  */
609 BOOL reg_key_exists(HKEY root, const char *path, const char *name)
610 {
611     char *val = get_reg_key(root, path, name, NULL);
612
613     if (val)
614     {
615         HeapFree(GetProcessHeap(), 0, val);
616         return TRUE;
617     }
618
619     return FALSE;
620 }
621
622 static void process_setting(struct setting *s)
623 {
624     static const WCHAR softwareW[] = {'S','o','f','t','w','a','r','e','\\'};
625     HKEY key;
626     BOOL needs_wow64 = (is_win64 && s->root == HKEY_LOCAL_MACHINE && s->path &&
627                         !strncmpiW( s->path, softwareW, sizeof(softwareW)/sizeof(WCHAR) ));
628
629     if (s->value)
630     {
631         WINE_TRACE("Setting %s:%s to '%s'\n", wine_dbgstr_w(s->path),
632                    wine_dbgstr_w(s->name), wine_dbgstr_w(s->value));
633         set_config_key(s->root, s->path, MAXIMUM_ALLOWED, s->name, s->value, s->type);
634         if (needs_wow64)
635         {
636             WINE_TRACE("Setting 32-bit %s:%s to '%s'\n", wine_dbgstr_w(s->path),
637                        wine_dbgstr_w(s->name), wine_dbgstr_w(s->value));
638             set_config_key(s->root, s->path, MAXIMUM_ALLOWED | KEY_WOW64_32KEY, s->name, s->value, s->type);
639         }
640     }
641     else
642     {
643         WINE_TRACE("Removing %s:%s\n", wine_dbgstr_w(s->path), wine_dbgstr_w(s->name));
644         if (!RegOpenKeyExW( s->root, s->path, 0, MAXIMUM_ALLOWED, &key ))
645         {
646             /* NULL name means remove that path/section entirely */
647             if (s->name) RegDeleteValueW( key, s->name );
648             else
649             {
650                 RegDeleteTreeW( key, NULL );
651                 RegDeleteKeyW( s->root, s->path );
652             }
653             RegCloseKey( key );
654         }
655         if (needs_wow64)
656         {
657             WINE_TRACE("Removing 32-bit %s:%s\n", wine_dbgstr_w(s->path), wine_dbgstr_w(s->name));
658             if (!RegOpenKeyExW( s->root, s->path, 0, MAXIMUM_ALLOWED | KEY_WOW64_32KEY, &key ))
659             {
660                 if (s->name) RegDeleteValueW( key, s->name );
661                 else
662                 {
663                     RegDeleteTreeW( key, NULL );
664                     RegDeleteKeyExW( s->root, s->path, KEY_WOW64_32KEY, 0 );
665                 }
666                 RegCloseKey( key );
667             }
668         }
669     }
670 }
671
672 void apply(void)
673 {
674     if (list_empty(settings)) return; /* we will be called for each page when the user clicks OK */
675
676     WINE_TRACE("()\n");
677
678     while (!list_empty(settings))
679     {
680         struct setting *s = (struct setting *) list_head(settings);
681         process_setting(s);
682         free_setting(s);
683     }
684 }
685
686 /* ================================== utility functions ============================ */
687
688 WCHAR* current_app = NULL; /* the app we are currently editing, or NULL if editing global */
689
690 /* returns a registry key path suitable for passing to addTransaction  */
691 char *keypath(const char *section)
692 {
693     static char *result = NULL;
694
695     HeapFree(GetProcessHeap(), 0, result);
696
697     if (current_app)
698     {
699         result = HeapAlloc(GetProcessHeap(), 0, strlen("AppDefaults\\") + lstrlenW(current_app)*2 + 2 /* \\ */ + strlen(section) + 1 /* terminator */);
700         wsprintf(result, "AppDefaults\\%ls", current_app);
701         if (section[0]) sprintf( result + strlen(result), "\\%s", section );
702     }
703     else
704     {
705         result = strdupA(section);
706     }
707
708     return result;
709 }
710
711 WCHAR *keypathW(const WCHAR *section)
712 {
713     static const WCHAR appdefaultsW[] = {'A','p','p','D','e','f','a','u','l','t','s','\\',0};
714     static WCHAR *result = NULL;
715
716     HeapFree(GetProcessHeap(), 0, result);
717
718     if (current_app)
719     {
720         DWORD len = sizeof(appdefaultsW) + (lstrlenW(current_app) + lstrlenW(section) + 1) * sizeof(WCHAR);
721         result = HeapAlloc(GetProcessHeap(), 0, len );
722         lstrcpyW( result, appdefaultsW );
723         lstrcatW( result, current_app );
724         if (section[0])
725         {
726             len = lstrlenW(result);
727             result[len++] = '\\';
728             lstrcpyW( result + len, section );
729         }
730     }
731     else
732     {
733         result = strdupW(section);
734     }
735
736     return result;
737 }
738
739 void PRINTERROR(void)
740 {
741         LPSTR msg;
742
743         FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
744                        0, GetLastError(), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
745                        (LPSTR)&msg, 0, NULL);
746
747         /* eliminate trailing newline, is this a Wine bug? */
748         *(strrchr(msg, '\r')) = '\0';
749         
750         WINE_TRACE("error: '%s'\n", msg);
751 }
752
753 int initialize(HINSTANCE hInstance)
754 {
755     DWORD res = RegCreateKey(HKEY_CURRENT_USER, WINE_KEY_ROOT, &config_key);
756
757     if (res != ERROR_SUCCESS) {
758         WINE_ERR("RegOpenKey failed on wine config key (%d)\n", res);
759         return 1;
760     }
761
762     /* load any menus */
763     hPopupMenus = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_WINECFG));
764
765     /* we could probably just have the list as static data  */
766     settings = HeapAlloc(GetProcessHeap(), 0, sizeof(struct list));
767     list_init(settings);
768
769     return 0;
770 }