winecfg: Update French translation.
[wine] / programs / regedit / edit.c
1 /*
2  * Registry editing UI functions.
3  *
4  * Copyright (C) 2003 Dimitrie O. Paun
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define WIN32_LEAN_AND_MEAN     /* Exclude rarely-used stuff from Windows headers */
22
23 #include <windows.h>
24 #include <tchar.h>
25 #include <commctrl.h>
26 #include <commdlg.h>
27 #include <cderr.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <shellapi.h>
31 #include <shlwapi.h>
32
33 #include "wine/unicode.h"
34 #include "main.h"
35 #include "regproc.h"
36 #include "resource.h"
37
38 static const WCHAR* editValueName;
39 static WCHAR* stringValueData;
40 static BOOL isDecimal;
41
42 struct edit_params
43 {
44     HKEY    hKey;
45     LPCWSTR lpszValueName;
46     void   *pData;
47     LONG    cbData;
48 };
49
50 static INT vmessagebox(HWND hwnd, INT buttons, INT titleId, INT resId, va_list ap)
51 {
52     static const WCHAR errorW[] = {'E','r','r','o','r',0};
53     static const WCHAR unknownW[] = {'U','n','k','n','o','w','n',' ','e','r','r','o','r',' ','s','t','r','i','n','g','!',0};
54
55     WCHAR title[256];
56     WCHAR errfmt[1024];
57     WCHAR errstr[1024];
58
59     if (!LoadStringW(hInst, titleId, title, COUNT_OF(title))) lstrcpyW(title, errorW);
60     if (!LoadStringW(hInst, resId, errfmt, COUNT_OF(errfmt))) lstrcpyW(errfmt, unknownW);
61
62     vsnprintfW(errstr, COUNT_OF(errstr), errfmt, ap);
63
64     return MessageBoxW(hwnd, errstr, title, buttons);
65 }
66
67 static INT messagebox(HWND hwnd, INT buttons, INT titleId, INT resId, ...)
68 {
69     va_list ap;
70     INT result;
71
72     va_start(ap, resId);
73     result = vmessagebox(hwnd, buttons, titleId, resId, ap);
74     va_end(ap);
75
76     return result;
77 }
78
79 void error(HWND hwnd, INT resId, ...)
80 {
81     va_list ap;
82
83     va_start(ap, resId);
84     vmessagebox(hwnd, MB_OK | MB_ICONERROR, IDS_ERROR, resId, ap);
85     va_end(ap);
86 }
87
88 static void error_code_messagebox(HWND hwnd, DWORD error_code)
89 {
90     LPWSTR lpMsgBuf;
91     DWORD status;
92     WCHAR title[256];
93     static WCHAR fallback[] = {'E','r','r','o','r',' ','d','i','s','p','l','a','y','i','n','g',' ','e','r','r','o','r',' ','m','e','s','s','a','g','e','.','\n',0};
94     static const WCHAR title_error[] = {'E','r','r','o','r',0};
95
96     if (!LoadStringW(hInst, IDS_ERROR, title, COUNT_OF(title)))
97         lstrcpyW(title, title_error);
98     status = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
99                            NULL, error_code, 0, (LPWSTR)&lpMsgBuf, 0, NULL);
100     if (!status)
101         lpMsgBuf = fallback;
102     MessageBoxW(hwnd, lpMsgBuf, title, MB_OK | MB_ICONERROR);
103     if (lpMsgBuf != fallback)
104         LocalFree(lpMsgBuf);
105 }
106
107 static BOOL change_dword_base(HWND hwndDlg, BOOL toHex)
108 {
109     static const WCHAR percent_u[] = {'%','u',0};
110     static const WCHAR percent_x[] = {'%','x',0};
111
112     WCHAR buf[128];
113     DWORD val;
114
115     if (!GetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, buf, COUNT_OF(buf))) return FALSE;
116     if (!swscanf(buf, toHex ? percent_u : percent_x, &val)) return FALSE;
117     wsprintfW(buf, toHex ? percent_x : percent_u, val);
118     return SetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, buf);
119 }
120
121 static INT_PTR CALLBACK modify_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
122 {
123     WCHAR* valueData;
124     HWND hwndValue;
125     int len;
126
127     switch(uMsg) {
128     case WM_INITDIALOG:
129         SetDlgItemTextW(hwndDlg, IDC_VALUE_NAME, editValueName);
130         SetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, stringValueData);
131         CheckRadioButton(hwndDlg, IDC_DWORD_HEX, IDC_DWORD_DEC, isDecimal ? IDC_DWORD_DEC : IDC_DWORD_HEX);
132         return TRUE;
133     case WM_COMMAND:
134         switch (LOWORD(wParam)) {
135         case IDC_DWORD_HEX:
136             if (isDecimal && change_dword_base(hwndDlg, TRUE)) isDecimal = FALSE;
137         break;
138         case IDC_DWORD_DEC:
139             if (!isDecimal && change_dword_base(hwndDlg, FALSE)) isDecimal = TRUE;
140         break;
141         case IDOK:
142             if ((hwndValue = GetDlgItem(hwndDlg, IDC_VALUE_DATA))) {
143                 len = GetWindowTextLengthW(hwndValue);
144                 if ((valueData = HeapReAlloc(GetProcessHeap(), 0, stringValueData, (len + 1) * sizeof(WCHAR)))) {
145                     stringValueData = valueData;
146                     if (!GetWindowTextW(hwndValue, stringValueData, len + 1))
147                         *stringValueData = 0;
148                 }
149             }
150             /* Fall through */
151         case IDCANCEL:
152             EndDialog(hwndDlg, wParam);
153             return TRUE;
154         }
155     }
156     return FALSE;
157 }
158
159 static INT_PTR CALLBACK bin_modify_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
160 {
161     struct edit_params *params;
162     LPBYTE pData;
163     LONG cbData;
164     LONG lRet;
165
166     switch(uMsg) {
167     case WM_INITDIALOG:
168         params = (struct edit_params *)lParam;
169         SetWindowLongPtrW(hwndDlg, DWLP_USER, (ULONG_PTR)params);
170         if (params->lpszValueName)
171             SetDlgItemTextW(hwndDlg, IDC_VALUE_NAME, params->lpszValueName);
172         else
173             SetDlgItemTextW(hwndDlg, IDC_VALUE_NAME, g_pszDefaultValueName);
174         SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, HEM_SETDATA, (WPARAM)params->cbData, (LPARAM)params->pData);
175         return TRUE;
176     case WM_COMMAND:
177         switch (LOWORD(wParam)) {
178         case IDOK:
179             params = (struct edit_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
180             cbData = SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, HEM_GETDATA, 0, 0);
181             pData = HeapAlloc(GetProcessHeap(), 0, cbData);
182
183             if (pData)
184             {
185                 SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, HEM_GETDATA, (WPARAM)cbData, (LPARAM)pData);
186                 lRet = RegSetValueExW(params->hKey, params->lpszValueName, 0, REG_BINARY, pData, cbData);
187             }
188             else
189                 lRet = ERROR_OUTOFMEMORY;
190
191             if (lRet == ERROR_SUCCESS)
192                 EndDialog(hwndDlg, 1);
193             else
194             {
195                 error_code_messagebox(hwndDlg, lRet);
196                 EndDialog(hwndDlg, 0);
197             }
198             return TRUE;
199         case IDCANCEL:
200             EndDialog(hwndDlg, 0);
201             return TRUE;
202         }
203     }
204     return FALSE;
205 }
206
207 static BOOL check_value(HWND hwnd, HKEY hKey, LPCWSTR valueName)
208 {
209     WCHAR empty = 0;
210     LONG lRet = RegQueryValueExW(hKey, valueName ? valueName : &empty, 0, NULL, 0, NULL);
211     if(lRet != ERROR_SUCCESS) return FALSE;
212     return TRUE;
213 }
214
215 static LPWSTR read_value(HWND hwnd, HKEY hKey, LPCWSTR valueName, DWORD *lpType, LONG *len)
216 {
217     DWORD valueDataLen;
218     LPWSTR buffer = NULL;
219     LONG lRet;
220         WCHAR empty = 0;
221
222     lRet = RegQueryValueExW(hKey, valueName ? valueName : &empty, 0, lpType, 0, &valueDataLen);
223     if (lRet != ERROR_SUCCESS) {
224         if (lRet == ERROR_FILE_NOT_FOUND && !valueName) { /* no default value here, make it up */
225             if (len) *len = 1;
226             if (lpType) *lpType = REG_SZ;
227             buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR));
228             *buffer = '\0';
229             return buffer;
230         }
231         error(hwnd, IDS_BAD_VALUE, valueName);
232         goto done;
233     }
234     if ( *lpType == REG_DWORD ) valueDataLen = sizeof(DWORD);
235     if (!(buffer = HeapAlloc(GetProcessHeap(), 0, valueDataLen+sizeof(WCHAR)))) {
236         error(hwnd, IDS_TOO_BIG_VALUE, valueDataLen);
237         goto done;
238     }
239     lRet = RegQueryValueExW(hKey, valueName, 0, 0, (LPBYTE)buffer, &valueDataLen);
240     if (lRet != ERROR_SUCCESS) {
241         error(hwnd, IDS_BAD_VALUE, valueName);
242         goto done;
243     }
244     if((valueDataLen % sizeof(WCHAR)) == 0)
245         buffer[valueDataLen / sizeof(WCHAR)] = 0;
246     if(len) *len = valueDataLen;
247     return buffer;
248
249 done:
250     HeapFree(GetProcessHeap(), 0, buffer);
251     return NULL;
252 }
253
254 BOOL CreateKey(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPWSTR keyName)
255 {
256     BOOL result = FALSE;
257     LONG lRet = ERROR_SUCCESS;
258     HKEY retKey = NULL;
259     WCHAR newKey[MAX_NEW_KEY_LEN - 4];
260     int keyNum;
261     HKEY hKey;
262          
263     lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_CREATE_SUB_KEY, &hKey);
264     if (lRet != ERROR_SUCCESS) {
265         error_code_messagebox(hwnd, lRet);
266         goto done;
267     }
268
269     if (!LoadStringW(GetModuleHandleW(0), IDS_NEWKEY, newKey, COUNT_OF(newKey))) goto done;
270
271     /* try to find out a name for the newly create key (max 100 times) */
272     for (keyNum = 1; keyNum < 100; keyNum++) {
273         wsprintfW(keyName, newKey, keyNum);
274         lRet = RegOpenKeyW(hKey, keyName, &retKey);
275         if (lRet != ERROR_SUCCESS) break;
276         RegCloseKey(retKey);
277     }
278     if (lRet == ERROR_SUCCESS) goto done;
279     
280     lRet = RegCreateKeyW(hKey, keyName, &retKey);
281     if (lRet != ERROR_SUCCESS) {
282         error_code_messagebox(hwnd, lRet);
283         goto done;
284     }
285
286     result = TRUE;
287
288 done:
289     RegCloseKey(retKey);
290     return result;
291 }
292
293 BOOL ModifyValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPCWSTR valueName)
294 {
295     BOOL result = FALSE;
296     DWORD type;
297     LONG lRet;
298     HKEY hKey;
299     LONG len;
300
301     lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
302     if (lRet != ERROR_SUCCESS) {
303         error_code_messagebox(hwnd, lRet);
304         return FALSE;
305     }
306
307     editValueName = valueName ? valueName : g_pszDefaultValueName;
308     if(!(stringValueData = read_value(hwnd, hKey, valueName, &type, &len))) goto done;
309
310     if ( (type == REG_SZ) || (type == REG_EXPAND_SZ) ) {
311         if (DialogBoxW(0, MAKEINTRESOURCEW(IDD_EDIT_STRING), hwnd, modify_dlgproc) == IDOK) {
312             lRet = RegSetValueExW(hKey, valueName, 0, type, (LPBYTE)stringValueData, (lstrlenW(stringValueData) + 1) * sizeof(WCHAR));
313             if (lRet == ERROR_SUCCESS) result = TRUE;
314             else error_code_messagebox(hwnd, lRet);
315         }
316     } else if ( type == REG_DWORD ) {
317         const WCHAR u[] = {'%','u',0};
318         const WCHAR x[] = {'%','x',0};
319         wsprintfW(stringValueData, isDecimal ? u : x, *((DWORD*)stringValueData));
320         if (DialogBoxW(0, MAKEINTRESOURCEW(IDD_EDIT_DWORD), hwnd, modify_dlgproc) == IDOK) {
321             DWORD val;
322             CHAR* valueA = GetMultiByteString(stringValueData);
323             if (_stscanf(valueA, isDecimal ? "%u" : "%x", &val)) {
324                 lRet = RegSetValueExW(hKey, valueName, 0, type, (BYTE*)&val, sizeof(val));
325                 if (lRet == ERROR_SUCCESS) result = TRUE;
326                 else error_code_messagebox(hwnd, lRet);
327             }
328             HeapFree(GetProcessHeap(), 0, valueA);
329         }
330     } else if ( type == REG_BINARY ) {
331         struct edit_params params;
332         params.hKey = hKey;
333         params.lpszValueName = valueName;
334         params.pData = stringValueData;
335         params.cbData = len;
336         result = DialogBoxParamW(NULL, MAKEINTRESOURCEW(IDD_EDIT_BINARY), hwnd,
337             bin_modify_dlgproc, (LPARAM)&params);
338     } else if ( type == REG_MULTI_SZ ) {
339         WCHAR char1 = '\r', char2 = '\n';
340         WCHAR *tmpValueData = NULL;
341         INT i, j, count;
342
343         for ( i = 0, count = 0; i < len - 1; i++)
344             if ( !stringValueData[i] && stringValueData[i + 1] )
345                 count++;
346         tmpValueData = HeapAlloc( GetProcessHeap(), 0, ( len + count ) * sizeof(WCHAR));
347         if ( !tmpValueData ) goto done;
348
349         for ( i = 0, j = 0; i < len - 1; i++)
350         {
351             if ( !stringValueData[i] && stringValueData[i + 1])
352             {
353                 tmpValueData[j++] = char1;
354                 tmpValueData[j++] = char2;
355             }
356             else
357                 tmpValueData[j++] = stringValueData[i];
358         }
359         tmpValueData[j] = stringValueData[i];
360         HeapFree( GetProcessHeap(), 0, stringValueData);
361         stringValueData = tmpValueData;
362         tmpValueData = NULL;
363
364         if (DialogBoxW(0, MAKEINTRESOURCEW(IDD_EDIT_MULTI_STRING), hwnd, modify_dlgproc) == IDOK)
365         {
366             len = lstrlenW( stringValueData );
367             tmpValueData = HeapAlloc( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR));
368             if ( !tmpValueData ) goto done;
369
370             for ( i = 0, j = 0; i < len - 1; i++)
371             {
372                 if ( stringValueData[i] == char1 && stringValueData[i + 1] == char2)
373                 {
374                     if ( tmpValueData[j - 1] != 0)
375                         tmpValueData[j++] = 0;
376                     i++;
377                 }
378                 else
379                     tmpValueData[j++] = stringValueData[i];
380             }
381             tmpValueData[j++] = stringValueData[i];
382             tmpValueData[j++] = 0;
383             tmpValueData[j++] = 0;
384             HeapFree( GetProcessHeap(), 0, stringValueData);
385             stringValueData = tmpValueData;
386
387             lRet = RegSetValueExW(hKey, valueName, 0, type, (LPBYTE)stringValueData, j * sizeof(WCHAR));
388             if (lRet == ERROR_SUCCESS) result = TRUE;
389             else error_code_messagebox(hwnd, lRet);
390         }
391     } else {
392         error(hwnd, IDS_UNSUPPORTED_TYPE, type);
393     }
394
395 done:
396     HeapFree(GetProcessHeap(), 0, stringValueData);
397     stringValueData = NULL;
398     RegCloseKey(hKey);
399     return result;
400 }
401
402 BOOL DeleteKey(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath)
403 {
404     BOOL result = FALSE;
405     LONG lRet;
406     HKEY hKey;
407
408     lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ|KEY_SET_VALUE, &hKey);
409     if (lRet != ERROR_SUCCESS) {
410         error_code_messagebox(hwnd, lRet);
411         return FALSE;
412     }
413     
414     if (messagebox(hwnd, MB_YESNO | MB_ICONEXCLAMATION, IDS_DELETE_BOX_TITLE, IDS_DELETE_BOX_TEXT, keyPath) != IDYES)
415         goto done;
416         
417     lRet = SHDeleteKeyW(hKeyRoot, keyPath);
418     if (lRet != ERROR_SUCCESS) {
419         error(hwnd, IDS_BAD_KEY, keyPath);
420         goto done;
421     }
422     result = TRUE;
423     
424 done:
425     RegCloseKey(hKey);
426     return result;
427 }
428
429 BOOL DeleteValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPCWSTR valueName, BOOL showMessageBox)
430 {
431     BOOL result = FALSE;
432     LONG lRet;
433     HKEY hKey;
434     LPCWSTR visibleValueName = valueName ? valueName : g_pszDefaultValueName;
435     WCHAR empty = 0;
436
437     lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
438     if (lRet != ERROR_SUCCESS) return FALSE;
439
440     if (showMessageBox)
441     {
442         if (messagebox(hwnd, MB_YESNO | MB_ICONEXCLAMATION, IDS_DELETE_BOX_TITLE, IDS_DELETE_BOX_TEXT, visibleValueName) != IDYES)
443             goto done;
444     }
445
446     lRet = RegDeleteValueW(hKey, valueName ? valueName : &empty);
447     if (lRet != ERROR_SUCCESS && valueName) {
448         error(hwnd, IDS_BAD_VALUE, valueName);
449     }
450     if (lRet != ERROR_SUCCESS) goto done;
451     result = TRUE;
452
453 done:
454     RegCloseKey(hKey);
455     return result;
456 }
457
458 BOOL CreateValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, DWORD valueType, LPWSTR valueName)
459 {
460     LONG lRet = ERROR_SUCCESS;
461     WCHAR newValue[256];
462     DWORD valueDword = 0;
463     BOOL result = FALSE;
464     int valueNum;
465     HKEY hKey;
466          
467     lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
468     if (lRet != ERROR_SUCCESS) {
469         error_code_messagebox(hwnd, lRet);
470         return FALSE;
471     }
472
473     if (!LoadStringW(GetModuleHandleW(0), IDS_NEWVALUE, newValue, COUNT_OF(newValue))) goto done;
474
475     /* try to find out a name for the newly create key (max 100 times) */
476     for (valueNum = 1; valueNum < 100; valueNum++) {
477         wsprintfW(valueName, newValue, valueNum);
478         lRet = RegQueryValueExW(hKey, valueName, 0, 0, 0, 0);
479         if (lRet == ERROR_FILE_NOT_FOUND) break;
480     }
481     if (lRet != ERROR_FILE_NOT_FOUND) {
482         error_code_messagebox(hwnd, lRet);
483         goto done;
484     }
485    
486     lRet = RegSetValueExW(hKey, valueName, 0, valueType, (BYTE*)&valueDword, sizeof(DWORD));
487     if (lRet != ERROR_SUCCESS) {
488         error_code_messagebox(hwnd, lRet);
489         goto done;
490     }
491     result = TRUE;
492
493 done:
494     RegCloseKey(hKey);
495     return result;
496 }
497
498 BOOL RenameValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPCWSTR oldName, LPCWSTR newName)
499 {
500     LPWSTR value = NULL;
501     DWORD type;
502     LONG len, lRet;
503     BOOL result = FALSE;
504     HKEY hKey;
505
506     if (!oldName) return FALSE;
507     if (!newName) return FALSE;
508
509     lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
510     if (lRet != ERROR_SUCCESS) {
511         error_code_messagebox(hwnd, lRet);
512         return FALSE;
513     }
514     /* check if value already exists */
515     if (check_value(hwnd, hKey, newName)) goto done;
516     value = read_value(hwnd, hKey, oldName, &type, &len);
517     if(!value) goto done;
518     lRet = RegSetValueExW(hKey, newName, 0, type, (BYTE*)value, len);
519     if (lRet != ERROR_SUCCESS) {
520         error_code_messagebox(hwnd, lRet);
521         goto done;
522     }
523     lRet = RegDeleteValueW(hKey, oldName);
524     if (lRet != ERROR_SUCCESS) {
525         RegDeleteValueW(hKey, newName);
526         error_code_messagebox(hwnd, lRet);
527         goto done;
528     }
529     result = TRUE;
530
531 done:
532     HeapFree(GetProcessHeap(), 0, value);
533     RegCloseKey(hKey);
534     return result;
535 }
536
537
538 BOOL RenameKey(HWND hwnd, HKEY hRootKey, LPCWSTR keyPath, LPCWSTR newName)
539 {
540     LPWSTR parentPath = 0;
541     LPCWSTR srcSubKey = 0;
542     HKEY parentKey = 0;
543     HKEY destKey = 0;
544     BOOL result = FALSE;
545     LONG lRet;
546     DWORD disposition;
547
548     if (!keyPath || !newName) return FALSE;
549
550     if (!strrchrW(keyPath, '\\')) {
551         parentKey = hRootKey;
552         srcSubKey = keyPath;
553     } else {
554         LPWSTR srcSubKey_copy;
555
556         parentPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(keyPath)+1)*sizeof(WCHAR));
557         lstrcpyW(parentPath, keyPath);
558         srcSubKey_copy = strrchrW(parentPath, '\\');
559         *srcSubKey_copy = 0;
560         srcSubKey = srcSubKey_copy + 1;
561         lRet = RegOpenKeyExW(hRootKey, parentPath, 0, KEY_READ | KEY_CREATE_SUB_KEY, &parentKey);
562         if (lRet != ERROR_SUCCESS) {
563             error_code_messagebox(hwnd, lRet);
564             goto done;
565         }
566     }
567
568     /* The following fails if the old name is the same as the new name. */
569     if (!lstrcmpW(srcSubKey, newName)) goto done;
570
571     lRet = RegCreateKeyExW(parentKey, newName, 0, NULL, REG_OPTION_NON_VOLATILE,
572         KEY_WRITE, NULL /* FIXME */, &destKey, &disposition);
573     if (disposition == REG_OPENED_EXISTING_KEY)
574         lRet = ERROR_FILE_EXISTS; /* FIXME: we might want a better error message than this */
575     if (lRet != ERROR_SUCCESS) {
576         error_code_messagebox(hwnd, lRet);
577         goto done;
578     }
579
580     /* FIXME: SHCopyKey does not copy the security attributes */
581     lRet = SHCopyKeyW(parentKey, srcSubKey, destKey, 0);
582     if (lRet != ERROR_SUCCESS) {
583         RegCloseKey(destKey);
584         RegDeleteKeyW(parentKey, newName);
585         error_code_messagebox(hwnd, lRet);
586         goto done;
587     }
588
589     lRet = SHDeleteKeyW(hRootKey, keyPath);
590     if (lRet != ERROR_SUCCESS) {
591         error_code_messagebox(hwnd, lRet);
592         goto done;
593     }
594
595     result = TRUE;
596
597 done:
598     RegCloseKey(destKey);
599     if (parentKey) {
600         RegCloseKey(parentKey); 
601         HeapFree(GetProcessHeap(), 0, parentPath);
602     }
603     return result;
604 }