shell32/tests: Use the available ARRAY_SIZE() macro.
[wine.git] / programs / regedit / edit.c
blob3e5b9dd6cb346d3d57bb069c4d7ecb303ccc640f
1 /*
2 * Registry editing UI functions.
4 * Copyright (C) 2003 Dimitrie O. Paun
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.
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.
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
21 #define WIN32_LEAN_AND_MEAN /* Exclude rarely-used stuff from Windows headers */
23 #include <windows.h>
24 #include <commctrl.h>
25 #include <commdlg.h>
26 #include <cderr.h>
27 #include <stdlib.h>
28 #include <shellapi.h>
29 #include <shlwapi.h>
31 #include "wine/heap.h"
32 #include "wine/unicode.h"
33 #include "main.h"
35 static const WCHAR* editValueName;
36 static WCHAR* stringValueData;
37 static BOOL isDecimal;
39 struct edit_params
41 HKEY hKey;
42 LPCWSTR lpszValueName;
43 void *pData;
44 LONG cbData;
47 static int vmessagebox(HWND hwnd, int buttons, int titleId, int resId, __ms_va_list va_args)
49 WCHAR title[256];
50 WCHAR fmt[1024];
51 WCHAR *str;
52 int ret;
54 LoadStringW(hInst, titleId, title, ARRAY_SIZE(title));
55 LoadStringW(hInst, resId, fmt, ARRAY_SIZE(fmt));
57 FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
58 fmt, 0, 0, (WCHAR *)&str, 0, &va_args);
59 ret = MessageBoxW(hwnd, str, title, buttons);
60 LocalFree(str);
62 return ret;
65 int WINAPIV messagebox(HWND hwnd, int buttons, int titleId, int resId, ...)
67 __ms_va_list ap;
68 INT result;
70 __ms_va_start(ap, resId);
71 result = vmessagebox(hwnd, buttons, titleId, resId, ap);
72 __ms_va_end(ap);
74 return result;
77 static void WINAPIV error_code_messagebox(HWND hwnd, unsigned int msg_id, ...)
79 __ms_va_list ap;
81 __ms_va_start(ap, msg_id);
82 vmessagebox(hwnd, MB_OK|MB_ICONERROR, IDS_ERROR, msg_id, ap);
83 __ms_va_end(ap);
86 static BOOL change_dword_base(HWND hwndDlg, BOOL toHex)
88 static const WCHAR percent_u[] = {'%','u',0};
89 static const WCHAR percent_x[] = {'%','x',0};
91 WCHAR buf[128];
92 DWORD val;
94 if (!GetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, buf, ARRAY_SIZE(buf))) return FALSE;
95 if (!swscanf(buf, toHex ? percent_u : percent_x, &val)) return FALSE;
96 wsprintfW(buf, toHex ? percent_x : percent_u, val);
97 return SetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, buf);
100 static INT_PTR CALLBACK modify_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
102 HWND hwndValue;
103 int len;
105 switch(uMsg) {
106 case WM_INITDIALOG:
107 SetDlgItemTextW(hwndDlg, IDC_VALUE_NAME, editValueName);
108 SetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, stringValueData);
109 CheckRadioButton(hwndDlg, IDC_DWORD_HEX, IDC_DWORD_DEC, isDecimal ? IDC_DWORD_DEC : IDC_DWORD_HEX);
110 return TRUE;
111 case WM_COMMAND:
112 switch (LOWORD(wParam)) {
113 case IDC_DWORD_HEX:
114 if (isDecimal && change_dword_base(hwndDlg, TRUE)) isDecimal = FALSE;
115 break;
116 case IDC_DWORD_DEC:
117 if (!isDecimal && change_dword_base(hwndDlg, FALSE)) isDecimal = TRUE;
118 break;
119 case IDOK:
120 if ((hwndValue = GetDlgItem(hwndDlg, IDC_VALUE_DATA))) {
121 len = GetWindowTextLengthW(hwndValue);
122 stringValueData = heap_xrealloc(stringValueData, (len + 1) * sizeof(WCHAR));
123 if (!GetWindowTextW(hwndValue, stringValueData, len + 1))
124 *stringValueData = 0;
126 /* Fall through */
127 case IDCANCEL:
128 EndDialog(hwndDlg, wParam);
129 return TRUE;
132 return FALSE;
135 static INT_PTR CALLBACK bin_modify_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
137 struct edit_params *params;
138 LPBYTE pData;
139 LONG cbData;
140 LONG lRet;
142 switch(uMsg) {
143 case WM_INITDIALOG:
144 params = (struct edit_params *)lParam;
145 SetWindowLongPtrW(hwndDlg, DWLP_USER, (ULONG_PTR)params);
146 if (params->lpszValueName)
147 SetDlgItemTextW(hwndDlg, IDC_VALUE_NAME, params->lpszValueName);
148 else
149 SetDlgItemTextW(hwndDlg, IDC_VALUE_NAME, g_pszDefaultValueName);
150 SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, HEM_SETDATA, (WPARAM)params->cbData, (LPARAM)params->pData);
151 SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, WM_SETFONT, (WPARAM) GetStockObject(ANSI_FIXED_FONT), TRUE);
152 return TRUE;
153 case WM_COMMAND:
154 switch (LOWORD(wParam)) {
155 case IDOK:
156 params = (struct edit_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
157 cbData = SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, HEM_GETDATA, 0, 0);
158 pData = heap_xalloc(cbData);
160 if (pData)
162 SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, HEM_GETDATA, (WPARAM)cbData, (LPARAM)pData);
163 lRet = RegSetValueExW(params->hKey, params->lpszValueName, 0, REG_BINARY, pData, cbData);
164 heap_free(pData);
166 else
167 lRet = ERROR_OUTOFMEMORY;
169 if (lRet == ERROR_SUCCESS)
170 EndDialog(hwndDlg, 1);
171 else
173 error_code_messagebox(hwndDlg, IDS_SET_VALUE_FAILED);
174 EndDialog(hwndDlg, 0);
176 return TRUE;
177 case IDCANCEL:
178 EndDialog(hwndDlg, 0);
179 return TRUE;
182 return FALSE;
185 static BOOL value_exists(HWND hwnd, HKEY hKey, const WCHAR *value_name)
187 return !RegQueryValueExW(hKey, value_name, NULL, NULL, NULL, NULL);
190 static LPWSTR read_value(HWND hwnd, HKEY hKey, LPCWSTR valueName, DWORD *lpType, LONG *len)
192 DWORD valueDataLen;
193 LPWSTR buffer = NULL;
194 LONG lRet;
196 lRet = RegQueryValueExW(hKey, valueName, NULL, lpType, NULL, &valueDataLen);
197 if (lRet) {
198 if (lRet == ERROR_FILE_NOT_FOUND && !valueName) { /* no default value here, make it up */
199 if (len) *len = 1;
200 if (lpType) *lpType = REG_SZ;
201 buffer = heap_xalloc(sizeof(WCHAR));
202 *buffer = '\0';
203 return buffer;
205 error_code_messagebox(hwnd, IDS_BAD_VALUE, valueName);
206 goto done;
208 if ( *lpType == REG_DWORD ) valueDataLen = sizeof(DWORD);
209 buffer = heap_xalloc(valueDataLen + sizeof(WCHAR));
210 lRet = RegQueryValueExW(hKey, valueName, 0, 0, (LPBYTE)buffer, &valueDataLen);
211 if (lRet) {
212 error_code_messagebox(hwnd, IDS_BAD_VALUE, valueName);
213 goto done;
215 if((valueDataLen % sizeof(WCHAR)) == 0)
216 buffer[valueDataLen / sizeof(WCHAR)] = 0;
217 if(len) *len = valueDataLen;
218 return buffer;
220 done:
221 heap_free(buffer);
222 return NULL;
225 BOOL CreateKey(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPWSTR keyName)
227 BOOL result = FALSE;
228 LONG lRet = ERROR_SUCCESS;
229 HKEY retKey = NULL;
230 WCHAR newKey[MAX_NEW_KEY_LEN - 4];
231 int keyNum;
232 HKEY hKey;
234 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_CREATE_SUB_KEY, &hKey);
235 if (lRet) {
236 error_code_messagebox(hwnd, IDS_CREATE_KEY_FAILED);
237 goto done;
240 if (!LoadStringW(GetModuleHandleW(0), IDS_NEWKEY, newKey, ARRAY_SIZE(newKey))) goto done;
242 /* try to find a name for the key being created (maximum = 100 attempts) */
243 for (keyNum = 1; keyNum < 100; keyNum++) {
244 wsprintfW(keyName, newKey, keyNum);
245 lRet = RegOpenKeyW(hKey, keyName, &retKey);
246 if (lRet) break;
247 RegCloseKey(retKey);
249 if (lRet == ERROR_SUCCESS) goto done;
251 lRet = RegCreateKeyW(hKey, keyName, &retKey);
252 if (lRet) {
253 error_code_messagebox(hwnd, IDS_CREATE_KEY_FAILED);
254 goto done;
257 result = TRUE;
259 done:
260 RegCloseKey(retKey);
261 return result;
264 BOOL ModifyValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPCWSTR valueName)
266 BOOL result = FALSE;
267 DWORD type;
268 LONG lRet;
269 HKEY hKey;
270 LONG len;
272 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
273 if (lRet) {
274 error_code_messagebox(hwnd, IDS_SET_VALUE_FAILED);
275 return FALSE;
278 editValueName = valueName ? valueName : g_pszDefaultValueName;
279 if(!(stringValueData = read_value(hwnd, hKey, valueName, &type, &len))) goto done;
281 if ( (type == REG_SZ) || (type == REG_EXPAND_SZ) ) {
282 if (DialogBoxW(0, MAKEINTRESOURCEW(IDD_EDIT_STRING), hwnd, modify_dlgproc) == IDOK) {
283 lRet = RegSetValueExW(hKey, valueName, 0, type, (LPBYTE)stringValueData, (lstrlenW(stringValueData) + 1) * sizeof(WCHAR));
284 if (lRet == ERROR_SUCCESS) result = TRUE;
285 else error_code_messagebox(hwnd, IDS_SET_VALUE_FAILED);
287 } else if ( type == REG_DWORD ) {
288 const WCHAR u[] = {'%','u',0};
289 const WCHAR x[] = {'%','x',0};
290 wsprintfW(stringValueData, isDecimal ? u : x, *((DWORD*)stringValueData));
291 if (DialogBoxW(0, MAKEINTRESOURCEW(IDD_EDIT_DWORD), hwnd, modify_dlgproc) == IDOK) {
292 DWORD val;
293 CHAR* valueA = GetMultiByteString(stringValueData);
294 if (sscanf(valueA, isDecimal ? "%u" : "%x", &val)) {
295 lRet = RegSetValueExW(hKey, valueName, 0, type, (BYTE*)&val, sizeof(val));
296 if (lRet == ERROR_SUCCESS) result = TRUE;
297 else error_code_messagebox(hwnd, IDS_SET_VALUE_FAILED);
299 heap_free(valueA);
301 } else if ( type == REG_MULTI_SZ ) {
302 WCHAR char1 = '\r', char2 = '\n';
303 WCHAR *tmpValueData = NULL;
304 INT i, j, count;
306 for ( i = 0, count = 0; i < len - 1; i++)
307 if ( !stringValueData[i] && stringValueData[i + 1] )
308 count++;
309 tmpValueData = heap_xalloc((len + count) * sizeof(WCHAR));
311 for ( i = 0, j = 0; i < len - 1; i++)
313 if ( !stringValueData[i] && stringValueData[i + 1])
315 tmpValueData[j++] = char1;
316 tmpValueData[j++] = char2;
318 else
319 tmpValueData[j++] = stringValueData[i];
321 tmpValueData[j] = stringValueData[i];
322 heap_free(stringValueData);
323 stringValueData = tmpValueData;
324 tmpValueData = NULL;
326 if (DialogBoxW(0, MAKEINTRESOURCEW(IDD_EDIT_MULTI_STRING), hwnd, modify_dlgproc) == IDOK)
328 len = lstrlenW( stringValueData );
329 tmpValueData = heap_xalloc((len + 2) * sizeof(WCHAR));
331 for ( i = 0, j = 0; i < len - 1; i++)
333 if ( stringValueData[i] == char1 && stringValueData[i + 1] == char2)
335 if ( tmpValueData[j - 1] != 0)
336 tmpValueData[j++] = 0;
337 i++;
339 else
340 tmpValueData[j++] = stringValueData[i];
342 tmpValueData[j++] = stringValueData[i];
343 tmpValueData[j++] = 0;
344 tmpValueData[j++] = 0;
345 heap_free(stringValueData);
346 stringValueData = tmpValueData;
348 lRet = RegSetValueExW(hKey, valueName, 0, type, (LPBYTE)stringValueData, j * sizeof(WCHAR));
349 if (lRet == ERROR_SUCCESS) result = TRUE;
350 else error_code_messagebox(hwnd, IDS_SET_VALUE_FAILED);
353 else /* hex data types */
355 struct edit_params params;
357 params.hKey = hKey;
358 params.lpszValueName = valueName;
359 params.pData = stringValueData;
360 params.cbData = len;
361 result = DialogBoxParamW(NULL, MAKEINTRESOURCEW(IDD_EDIT_BINARY), hwnd,
362 bin_modify_dlgproc, (LPARAM)&params);
365 /* Update the listview item with the new data string */
366 if (result)
368 int index = SendMessageW(g_pChildWnd->hListWnd, LVM_GETNEXTITEM, -1,
369 MAKELPARAM(LVNI_FOCUSED | LVNI_SELECTED, 0));
370 heap_free(stringValueData);
371 stringValueData = read_value(hwnd, hKey, valueName, &type, &len);
372 format_value_data(g_pChildWnd->hListWnd, index, type, stringValueData, len);
375 done:
376 heap_free(stringValueData);
377 stringValueData = NULL;
378 RegCloseKey(hKey);
379 return result;
382 BOOL DeleteKey(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath)
384 BOOL result = FALSE;
385 LONG lRet;
386 HKEY hKey;
388 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ|KEY_SET_VALUE, &hKey);
389 if (lRet) {
390 error_code_messagebox(hwnd, IDS_DELETE_KEY_FAILED);
391 return FALSE;
394 if (messagebox(hwnd, MB_YESNO | MB_ICONEXCLAMATION, IDS_DELETE_KEY_TITLE,
395 IDS_DELETE_KEY_TEXT) != IDYES)
396 goto done;
398 lRet = SHDeleteKeyW(hKeyRoot, keyPath);
399 if (lRet) {
400 error_code_messagebox(hwnd, IDS_BAD_KEY, keyPath);
401 goto done;
403 result = TRUE;
405 done:
406 RegCloseKey(hKey);
407 return result;
410 BOOL DeleteValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPCWSTR valueName)
412 BOOL result = FALSE;
413 LONG lRet;
414 HKEY hKey;
416 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
417 if (lRet) return FALSE;
419 lRet = RegDeleteValueW(hKey, valueName);
420 if (lRet && valueName) {
421 error_code_messagebox(hwnd, IDS_BAD_VALUE, valueName);
423 if (lRet) goto done;
424 result = TRUE;
426 done:
427 RegCloseKey(hKey);
428 return result;
431 BOOL CreateValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, DWORD valueType, LPWSTR valueName)
433 LONG lRet = ERROR_SUCCESS;
434 WCHAR newValue[256];
435 DWORD valueDword = 0;
436 BOOL result = FALSE;
437 int valueNum, index;
438 HKEY hKey;
439 LVITEMW item;
441 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
442 if (lRet) {
443 error_code_messagebox(hwnd, IDS_CREATE_VALUE_FAILED);
444 return FALSE;
447 if (!LoadStringW(GetModuleHandleW(0), IDS_NEWVALUE, newValue, ARRAY_SIZE(newValue))) goto done;
449 /* try to find a name for the value being created (maximum = 100 attempts) */
450 for (valueNum = 1; valueNum < 100; valueNum++) {
451 wsprintfW(valueName, newValue, valueNum);
452 lRet = RegQueryValueExW(hKey, valueName, 0, 0, 0, 0);
453 if (lRet == ERROR_FILE_NOT_FOUND) break;
455 if (lRet != ERROR_FILE_NOT_FOUND) {
456 error_code_messagebox(hwnd, IDS_CREATE_VALUE_FAILED);
457 goto done;
460 lRet = RegSetValueExW(hKey, valueName, 0, valueType, (BYTE*)&valueDword, sizeof(DWORD));
461 if (lRet) {
462 error_code_messagebox(hwnd, IDS_CREATE_VALUE_FAILED);
463 goto done;
466 /* Add the new item to the listview */
467 index = AddEntryToList(g_pChildWnd->hListWnd, valueName, valueType,
468 (BYTE *)&valueDword, sizeof(DWORD), -1);
469 item.state = LVIS_FOCUSED | LVIS_SELECTED;
470 item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
471 SendMessageW(g_pChildWnd->hListWnd, LVM_SETITEMSTATE, index, (LPARAM)&item);
473 result = TRUE;
475 done:
476 RegCloseKey(hKey);
477 return result;
480 BOOL RenameValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPCWSTR oldName, LPCWSTR newName)
482 LPWSTR value = NULL;
483 DWORD type;
484 LONG len, lRet;
485 BOOL result = FALSE;
486 HKEY hKey;
488 if (!oldName) return FALSE;
489 if (!newName) return FALSE;
491 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
492 if (lRet) {
493 error_code_messagebox(hwnd, IDS_RENAME_VALUE_FAILED);
494 return FALSE;
497 if (value_exists(hwnd, hKey, newName)) {
498 error_code_messagebox(hwnd, IDS_VALUE_EXISTS, oldName);
499 goto done;
501 value = read_value(hwnd, hKey, oldName, &type, &len);
502 if(!value) goto done;
503 lRet = RegSetValueExW(hKey, newName, 0, type, (BYTE*)value, len);
504 if (lRet) {
505 error_code_messagebox(hwnd, IDS_RENAME_VALUE_FAILED);
506 goto done;
508 lRet = RegDeleteValueW(hKey, oldName);
509 if (lRet) {
510 RegDeleteValueW(hKey, newName);
511 error_code_messagebox(hwnd, IDS_RENAME_VALUE_FAILED);
512 goto done;
514 result = TRUE;
516 done:
517 heap_free(value);
518 RegCloseKey(hKey);
519 return result;
523 BOOL RenameKey(HWND hwnd, HKEY hRootKey, LPCWSTR keyPath, LPCWSTR newName)
525 LPWSTR parentPath = 0;
526 LPCWSTR srcSubKey = 0;
527 HKEY parentKey = 0;
528 HKEY destKey = 0;
529 BOOL result = FALSE;
530 LONG lRet;
531 DWORD disposition;
533 if (!keyPath || !newName) return FALSE;
535 if (!strrchrW(keyPath, '\\')) {
536 parentKey = hRootKey;
537 srcSubKey = keyPath;
538 } else {
539 LPWSTR srcSubKey_copy;
541 parentPath = heap_xalloc((lstrlenW(keyPath) + 1) * sizeof(WCHAR));
542 lstrcpyW(parentPath, keyPath);
543 srcSubKey_copy = strrchrW(parentPath, '\\');
544 *srcSubKey_copy = 0;
545 srcSubKey = srcSubKey_copy + 1;
546 lRet = RegOpenKeyExW(hRootKey, parentPath, 0, KEY_READ | KEY_CREATE_SUB_KEY, &parentKey);
547 if (lRet) {
548 error_code_messagebox(hwnd, IDS_RENAME_KEY_FAILED);
549 goto done;
553 /* The following fails if the old name is the same as the new name. */
554 if (!lstrcmpW(srcSubKey, newName)) goto done;
556 lRet = RegCreateKeyExW(parentKey, newName, 0, NULL, REG_OPTION_NON_VOLATILE,
557 KEY_WRITE, NULL /* FIXME */, &destKey, &disposition);
558 if (disposition == REG_OPENED_EXISTING_KEY)
559 lRet = ERROR_FILE_EXISTS;
560 if (lRet) {
561 error_code_messagebox(hwnd, IDS_KEY_EXISTS, srcSubKey);
562 goto done;
565 /* FIXME: SHCopyKey does not copy the security attributes */
566 lRet = SHCopyKeyW(parentKey, srcSubKey, destKey, 0);
567 if (lRet) {
568 RegCloseKey(destKey);
569 RegDeleteKeyW(parentKey, newName);
570 error_code_messagebox(hwnd, IDS_RENAME_KEY_FAILED);
571 goto done;
574 lRet = SHDeleteKeyW(hRootKey, keyPath);
575 if (lRet) {
576 error_code_messagebox(hwnd, IDS_RENAME_KEY_FAILED);
577 goto done;
580 result = TRUE;
582 done:
583 RegCloseKey(destKey);
584 if (parentKey) {
585 RegCloseKey(parentKey);
586 heap_free(parentPath);
588 return result;