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