ntdll: Implement retrieving DOS attributes in NtQueryInformationFile.
[wine.git] / programs / regedit / edit.c
blob66d3a27dc682c3155abd92b78a30ba0510a616df
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 <shellapi.h>
28 #include <shlwapi.h>
30 #include "main.h"
32 static BOOL isDecimal;
34 struct edit_params
36 HKEY hkey;
37 const WCHAR *value_name;
38 DWORD type;
39 void *data;
40 DWORD size;
43 static int vmessagebox(HWND hwnd, int buttons, int titleId, int resId, va_list va_args)
45 WCHAR title[256];
46 WCHAR fmt[1024];
47 WCHAR *str;
48 int ret;
50 LoadStringW(hInst, titleId, title, ARRAY_SIZE(title));
51 LoadStringW(hInst, resId, fmt, ARRAY_SIZE(fmt));
53 FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
54 fmt, 0, 0, (WCHAR *)&str, 0, &va_args);
55 ret = MessageBoxW(hwnd, str, title, buttons);
56 LocalFree(str);
58 return ret;
61 int WINAPIV messagebox(HWND hwnd, int buttons, int titleId, int resId, ...)
63 va_list ap;
64 INT result;
66 va_start(ap, resId);
67 result = vmessagebox(hwnd, buttons, titleId, resId, ap);
68 va_end(ap);
70 return result;
73 static void WINAPIV error_code_messagebox(HWND hwnd, unsigned int msg_id, ...)
75 va_list ap;
77 va_start(ap, msg_id);
78 vmessagebox(hwnd, MB_OK|MB_ICONERROR, IDS_ERROR, msg_id, ap);
79 va_end(ap);
82 static BOOL update_registry_value(HWND hwndDlg, struct edit_params *params)
84 HWND hwndValue;
85 unsigned int len;
86 WCHAR *buf;
87 LONG ret;
89 hwndValue = GetDlgItem(hwndDlg, IDC_VALUE_DATA);
90 len = GetWindowTextLengthW(hwndValue);
91 buf = malloc((len + 1) * sizeof(WCHAR));
92 len = GetWindowTextW(hwndValue, buf, len + 1);
94 free(params->data);
96 switch (params->type)
98 case REG_SZ:
99 case REG_EXPAND_SZ:
100 params->data = buf;
101 params->size = (len + 1) * sizeof(WCHAR);
102 break;
103 case REG_DWORD:
104 params->size = sizeof(DWORD);
105 params->data = malloc(params->size);
106 swscanf(buf, isDecimal ? L"%lu" : L"%lx", params->data);
107 free(buf);
108 break;
109 case REG_QWORD:
110 params->size = sizeof(UINT64);
111 params->data = malloc(params->size);
112 swscanf(buf, isDecimal ? L"%I64u" : L"%I64x", params->data);
113 free(buf);
114 break;
115 case REG_MULTI_SZ:
117 WCHAR *tmp = malloc((len + 2) * sizeof(WCHAR));
118 unsigned int i, j;
120 for (i = 0, j = 0; i < len; i++)
122 if (buf[i] == '\r' && buf[i + 1] == '\n')
124 if (tmp[j - 1]) tmp[j++] = 0;
125 i++;
127 else tmp[j++] = buf[i];
130 tmp[j++] = 0;
131 tmp[j++] = 0;
133 free(buf);
134 params->data = tmp;
135 params->size = j * sizeof(WCHAR);
136 break;
138 default: /* hex data types */
139 free(buf);
140 params->size = SendMessageW(hwndValue, HEM_GETDATA, 0, 0);
141 params->data = malloc(params->size);
142 SendMessageW(hwndValue, HEM_GETDATA, (WPARAM)params->size, (LPARAM)params->data);
145 ret = RegSetValueExW(params->hkey, params->value_name, 0, params->type, (BYTE *)params->data, params->size);
146 if (ret) error_code_messagebox(hwndDlg, IDS_SET_VALUE_FAILED);
148 return !ret;
151 static void set_dlgproc_value_name(HWND hwndDlg, struct edit_params *params)
153 if (params->value_name)
154 SetDlgItemTextW(hwndDlg, IDC_VALUE_NAME, params->value_name);
155 else
156 SetDlgItemTextW(hwndDlg, IDC_VALUE_NAME, g_pszDefaultValueName);
159 static INT_PTR CALLBACK modify_string_dlgproc(HWND hwndDlg, UINT msg, WPARAM wparam, LPARAM lparam)
161 struct edit_params *params;
162 int ret = 0;
164 switch (msg)
166 case WM_INITDIALOG:
167 params = (struct edit_params *)lparam;
168 SetWindowLongPtrW(hwndDlg, DWLP_USER, (ULONG_PTR)params);
169 set_dlgproc_value_name(hwndDlg, params);
170 SetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, params->data);
171 return TRUE;
172 case WM_COMMAND:
173 switch (LOWORD(wparam))
175 case IDOK:
176 params = (struct edit_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
177 ret = update_registry_value(hwndDlg, params);
178 /* fall through */
179 case IDCANCEL:
180 EndDialog(hwndDlg, ret);
181 return TRUE;
184 return FALSE;
187 static void set_dword_edit_limit(HWND hwndDlg, DWORD type)
189 if (isDecimal)
190 SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, EM_SETLIMITTEXT, type == REG_DWORD ? 10 : 20, 0);
191 else
192 SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, EM_SETLIMITTEXT, type == REG_DWORD ? 8 : 16, 0);
195 static void change_dword_base(HWND hwndDlg, BOOL toHex, DWORD type)
197 WCHAR buf[64];
198 unsigned int len;
199 UINT64 val;
201 len = GetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, buf, ARRAY_SIZE(buf));
202 if (!len) SetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, L"0");
204 if ((isDecimal && !toHex) || (!isDecimal && toHex))
205 return;
207 if (len)
209 swscanf(buf, toHex ? L"%I64u" : L"%I64x", &val);
210 swprintf(buf, ARRAY_SIZE(buf), toHex ? L"%I64x" : L"%I64u", val);
211 SetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, buf);
214 isDecimal = !toHex;
216 set_dword_edit_limit(hwndDlg, type);
219 static INT_PTR CALLBACK modify_dword_dlgproc(HWND hwndDlg, UINT msg, WPARAM wparam, LPARAM lparam)
221 static struct edit_params *params;
222 WCHAR buf[64];
223 int ret = 0;
225 switch (msg)
227 case WM_INITDIALOG:
228 params = (struct edit_params *)lparam;
229 SetWindowLongPtrW(hwndDlg, DWLP_USER, (ULONG_PTR)params);
230 set_dlgproc_value_name(hwndDlg, params);
231 SetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, params->data);
232 CheckRadioButton(hwndDlg, IDC_DWORD_HEX, IDC_DWORD_DEC, IDC_DWORD_HEX);
233 isDecimal = FALSE;
234 if (params->type == REG_QWORD && LoadStringW(GetModuleHandleW(0), IDS_EDIT_QWORD, buf, ARRAY_SIZE(buf)))
235 SetWindowTextW(hwndDlg, buf);
236 set_dword_edit_limit(hwndDlg, params->type);
237 return TRUE;
238 case WM_COMMAND:
239 switch (LOWORD(wparam))
241 case IDC_DWORD_HEX:
242 change_dword_base(hwndDlg, TRUE, params->type);
243 break;
244 case IDC_DWORD_DEC:
245 change_dword_base(hwndDlg, FALSE, params->type);
246 break;
247 case IDOK:
248 params = (struct edit_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
249 if (!SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, EM_LINELENGTH, 0, 0))
250 SetDlgItemTextW(hwndDlg, IDC_VALUE_DATA, L"0");
251 ret = update_registry_value(hwndDlg, params);
252 /* fall through */
253 case IDCANCEL:
254 EndDialog(hwndDlg, ret);
255 return TRUE;
258 return FALSE;
261 static INT_PTR CALLBACK modify_binary_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
263 struct edit_params *params;
264 int ret = 0;
266 switch(uMsg) {
267 case WM_INITDIALOG:
268 params = (struct edit_params *)lParam;
269 SetWindowLongPtrW(hwndDlg, DWLP_USER, (ULONG_PTR)params);
270 set_dlgproc_value_name(hwndDlg, params);
271 SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, HEM_SETDATA, (WPARAM)params->size, (LPARAM)params->data);
272 SendDlgItemMessageW(hwndDlg, IDC_VALUE_DATA, WM_SETFONT, (WPARAM) GetStockObject(ANSI_FIXED_FONT), TRUE);
273 return TRUE;
274 case WM_COMMAND:
275 switch (LOWORD(wParam)) {
276 case IDOK:
277 params = (struct edit_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
278 ret = update_registry_value(hwndDlg, params);
279 /* fall through */
280 case IDCANCEL:
281 EndDialog(hwndDlg, ret);
282 return TRUE;
285 return FALSE;
288 static BOOL read_value(HWND hwnd, struct edit_params *params)
290 LONG ret;
291 WCHAR *buf = NULL;
293 if ((ret = RegQueryValueExW(params->hkey, params->value_name, NULL, &params->type, NULL, &params->size)))
295 if (ret == ERROR_FILE_NOT_FOUND && !params->value_name)
297 params->type = REG_SZ;
298 params->size = sizeof(WCHAR);
299 params->data = malloc(params->size);
300 *(WCHAR *)params->data = 0;
301 return TRUE;
304 goto error;
307 buf = malloc(params->size + sizeof(WCHAR));
309 if (RegQueryValueExW(params->hkey, params->value_name, NULL, &params->type, (BYTE *)buf, &params->size))
310 goto error;
312 if (params->size % sizeof(WCHAR) == 0)
313 buf[params->size / sizeof(WCHAR)] = 0;
315 params->data = buf;
317 return TRUE;
319 error:
320 error_code_messagebox(hwnd, IDS_BAD_VALUE, params->value_name);
321 free(buf);
322 params->data = NULL;
323 return FALSE;
326 BOOL CreateKey(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPWSTR keyName)
328 BOOL result = FALSE;
329 LONG lRet = ERROR_SUCCESS;
330 HKEY retKey = NULL;
331 WCHAR newKey[MAX_NEW_KEY_LEN - 4];
332 int keyNum;
333 HKEY hKey;
335 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_CREATE_SUB_KEY, &hKey);
336 if (lRet) {
337 error_code_messagebox(hwnd, IDS_CREATE_KEY_FAILED);
338 goto done;
341 if (!LoadStringW(GetModuleHandleW(0), IDS_NEWKEY, newKey, ARRAY_SIZE(newKey))) goto done;
343 /* try to find a name for the key being created (maximum = 100 attempts) */
344 for (keyNum = 1; keyNum < 100; keyNum++) {
345 wsprintfW(keyName, newKey, keyNum);
346 lRet = RegOpenKeyW(hKey, keyName, &retKey);
347 if (lRet) break;
348 RegCloseKey(retKey);
350 if (lRet == ERROR_SUCCESS) goto done;
352 lRet = RegCreateKeyW(hKey, keyName, &retKey);
353 if (lRet) {
354 error_code_messagebox(hwnd, IDS_CREATE_KEY_FAILED);
355 goto done;
358 result = TRUE;
360 done:
361 RegCloseKey(retKey);
362 return result;
365 static void format_dlgproc_string(struct edit_params *params)
367 int i, j, count, len;
368 WCHAR *str, *buf;
370 if (params->type == REG_DWORD || params->type == REG_QWORD)
372 UINT64 value = *((UINT64 *)params->data);
374 params->data = realloc(params->data, 32 * sizeof(WCHAR));
375 swprintf(params->data, 32, params->type == REG_DWORD ? L"%lx" : L"%I64x", value);
376 return;
379 /* REG_MULTI_SZ */
380 len = params->size / sizeof(WCHAR);
381 str = params->data;
383 for (i = 0, count = 0; i < len; i++)
385 if (!str[i] && str[i + 1]) count++;
388 buf = malloc(params->size + (count * sizeof(WCHAR)));
390 for (i = 0, j = 0; i < len; i++)
392 if (!str[i] && str[i + 1])
394 buf[j++] = '\r';
395 buf[j++] = '\n';
397 else buf[j++] = str[i];
400 free(params->data);
401 str = NULL;
402 params->data = buf;
405 BOOL ModifyValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPCWSTR valueName)
407 struct edit_params params;
408 BOOL result = FALSE;
410 if (RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &params.hkey))
412 error_code_messagebox(hwnd, IDS_SET_VALUE_FAILED);
413 return FALSE;
416 params.value_name = valueName;
418 if (!read_value(hwnd, &params))
420 RegCloseKey(params.hkey);
421 return FALSE;
424 switch (params.type)
426 case REG_SZ:
427 case REG_EXPAND_SZ:
428 result = DialogBoxParamW(NULL, MAKEINTRESOURCEW(IDD_EDIT_STRING), hwnd,
429 modify_string_dlgproc, (LPARAM)&params);
430 break;
431 case REG_DWORD:
432 case REG_QWORD:
433 format_dlgproc_string(&params);
434 result = DialogBoxParamW(NULL, MAKEINTRESOURCEW(IDD_EDIT_DWORD), hwnd,
435 modify_dword_dlgproc, (LPARAM)&params);
436 break;
437 case REG_MULTI_SZ:
438 format_dlgproc_string(&params);
439 result = DialogBoxParamW(NULL, MAKEINTRESOURCEW(IDD_EDIT_MULTI_STRING), hwnd,
440 modify_string_dlgproc, (LPARAM)&params);
441 break;
442 default: /* hex data types */
443 result = DialogBoxParamW(NULL, MAKEINTRESOURCEW(IDD_EDIT_BINARY), hwnd,
444 modify_binary_dlgproc, (LPARAM)&params);
447 /* Update the listview item with the new data string */
448 if (result)
450 int index = SendMessageW(g_pChildWnd->hListWnd, LVM_GETNEXTITEM, -1,
451 MAKELPARAM(LVNI_FOCUSED | LVNI_SELECTED, 0));
453 format_value_data(g_pChildWnd->hListWnd, index, params.type, params.data, params.size);
456 free(params.data);
457 RegCloseKey(params.hkey);
458 return result;
461 BOOL DeleteKey(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath)
463 BOOL result = FALSE;
464 LONG lRet;
465 HKEY hKey;
467 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ|KEY_SET_VALUE, &hKey);
468 if (lRet) {
469 error_code_messagebox(hwnd, IDS_DELETE_KEY_FAILED);
470 return FALSE;
473 if (messagebox(hwnd, MB_YESNO | MB_ICONEXCLAMATION, IDS_DELETE_KEY_TITLE,
474 IDS_DELETE_KEY_TEXT) != IDYES)
475 goto done;
477 lRet = SHDeleteKeyW(hKeyRoot, keyPath);
478 if (lRet) {
479 error_code_messagebox(hwnd, IDS_BAD_KEY, keyPath);
480 goto done;
482 result = TRUE;
484 done:
485 RegCloseKey(hKey);
486 return result;
489 BOOL DeleteValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPCWSTR valueName)
491 BOOL result = FALSE;
492 LONG lRet;
493 HKEY hKey;
495 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
496 if (lRet) return FALSE;
498 lRet = RegDeleteValueW(hKey, valueName);
499 if (lRet && valueName) {
500 error_code_messagebox(hwnd, IDS_BAD_VALUE, valueName);
502 if (lRet) goto done;
503 result = TRUE;
505 done:
506 RegCloseKey(hKey);
507 return result;
510 BOOL CreateValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, DWORD valueType, LPWSTR valueName)
512 LONG lRet = ERROR_SUCCESS;
513 WCHAR newValue[256];
514 UINT64 value = 0;
515 DWORD size_bytes;
516 BOOL result = FALSE;
517 int valueNum, index;
518 HKEY hKey;
519 LVITEMW item;
521 lRet = RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
522 if (lRet) {
523 error_code_messagebox(hwnd, IDS_CREATE_VALUE_FAILED);
524 return FALSE;
527 if (!LoadStringW(GetModuleHandleW(0), IDS_NEWVALUE, newValue, ARRAY_SIZE(newValue))) goto done;
529 /* try to find a name for the value being created (maximum = 100 attempts) */
530 for (valueNum = 1; valueNum < 100; valueNum++) {
531 wsprintfW(valueName, newValue, valueNum);
532 lRet = RegQueryValueExW(hKey, valueName, 0, 0, 0, 0);
533 if (lRet == ERROR_FILE_NOT_FOUND) break;
535 if (lRet != ERROR_FILE_NOT_FOUND) {
536 error_code_messagebox(hwnd, IDS_CREATE_VALUE_FAILED);
537 goto done;
540 switch (valueType)
542 case REG_DWORD:
543 case REG_DWORD_BIG_ENDIAN:
544 size_bytes = sizeof(DWORD);
545 break;
546 case REG_QWORD:
547 size_bytes = sizeof(UINT64);
548 break;
549 case REG_BINARY:
550 size_bytes = 0;
551 break;
552 case REG_MULTI_SZ:
553 size_bytes = 2 * sizeof(WCHAR);
554 break;
555 default: /* REG_NONE, REG_SZ, REG_EXPAND_SZ */
556 size_bytes = sizeof(WCHAR);
559 lRet = RegSetValueExW(hKey, valueName, 0, valueType, (BYTE *)&value, size_bytes);
560 if (lRet) {
561 error_code_messagebox(hwnd, IDS_CREATE_VALUE_FAILED);
562 goto done;
565 /* Add the new item to the listview */
566 index = AddEntryToList(g_pChildWnd->hListWnd, valueName, valueType, (BYTE *)&value, size_bytes, -1);
567 item.state = LVIS_FOCUSED | LVIS_SELECTED;
568 item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
569 SendMessageW(g_pChildWnd->hListWnd, LVM_SETITEMSTATE, index, (LPARAM)&item);
571 result = TRUE;
573 done:
574 RegCloseKey(hKey);
575 return result;
578 BOOL RenameValue(HWND hwnd, HKEY hKeyRoot, LPCWSTR keyPath, LPCWSTR oldName, LPCWSTR newName)
580 struct edit_params params;
581 BOOL result = FALSE;
583 if (!oldName) return FALSE;
584 if (!newName) return FALSE;
586 if (RegOpenKeyExW(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &params.hkey))
588 error_code_messagebox(hwnd, IDS_RENAME_VALUE_FAILED);
589 return FALSE;
592 if (!RegQueryValueExW(params.hkey, newName, NULL, NULL, NULL, NULL))
594 error_code_messagebox(hwnd, IDS_VALUE_EXISTS, oldName);
595 goto done;
598 params.value_name = oldName;
599 if (!read_value(hwnd, &params)) goto done;
601 if (RegSetValueExW(params.hkey, newName, 0, params.type, (BYTE *)params.data, params.size))
603 error_code_messagebox(hwnd, IDS_RENAME_VALUE_FAILED);
604 goto done;
607 if (RegDeleteValueW(params.hkey, oldName))
609 RegDeleteValueW(params.hkey, newName);
610 error_code_messagebox(hwnd, IDS_RENAME_VALUE_FAILED);
611 goto done;
614 result = TRUE;
616 done:
617 free(params.data);
618 RegCloseKey(params.hkey);
619 return result;
622 BOOL RenameKey(HWND hwnd, HKEY hRootKey, LPCWSTR keyPath, LPCWSTR newName)
624 LPWSTR parentPath = 0;
625 LPCWSTR srcSubKey = 0;
626 HKEY parentKey = 0;
627 HKEY destKey = 0;
628 BOOL result = FALSE;
629 LONG lRet;
630 DWORD disposition;
632 if (!keyPath || !newName) return FALSE;
634 if (!wcsrchr(keyPath, '\\')) {
635 parentKey = hRootKey;
636 srcSubKey = keyPath;
637 } else {
638 LPWSTR srcSubKey_copy;
640 parentPath = malloc((lstrlenW(keyPath) + 1) * sizeof(WCHAR));
641 lstrcpyW(parentPath, keyPath);
642 srcSubKey_copy = wcsrchr(parentPath, '\\');
643 *srcSubKey_copy = 0;
644 srcSubKey = srcSubKey_copy + 1;
645 lRet = RegOpenKeyExW(hRootKey, parentPath, 0, KEY_READ | KEY_CREATE_SUB_KEY, &parentKey);
646 if (lRet) {
647 error_code_messagebox(hwnd, IDS_RENAME_KEY_FAILED);
648 goto done;
652 /* The following fails if the old name is the same as the new name. */
653 if (!lstrcmpW(srcSubKey, newName)) goto done;
655 lRet = RegCreateKeyExW(parentKey, newName, 0, NULL, REG_OPTION_NON_VOLATILE,
656 KEY_WRITE, NULL /* FIXME */, &destKey, &disposition);
657 if (disposition == REG_OPENED_EXISTING_KEY)
658 lRet = ERROR_FILE_EXISTS;
659 if (lRet) {
660 error_code_messagebox(hwnd, IDS_KEY_EXISTS, srcSubKey);
661 goto done;
664 /* FIXME: SHCopyKey does not copy the security attributes */
665 lRet = SHCopyKeyW(parentKey, srcSubKey, destKey, 0);
666 if (lRet) {
667 RegCloseKey(destKey);
668 RegDeleteKeyW(parentKey, newName);
669 error_code_messagebox(hwnd, IDS_RENAME_KEY_FAILED);
670 goto done;
673 lRet = SHDeleteKeyW(hRootKey, keyPath);
674 if (lRet) {
675 error_code_messagebox(hwnd, IDS_RENAME_KEY_FAILED);
676 goto done;
679 result = TRUE;
681 done:
682 RegCloseKey(destKey);
683 if (parentKey) {
684 RegCloseKey(parentKey);
685 free(parentPath);
687 return result;