winecfg.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  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. #define WIN32_LEAN_AND_MEAN
  30. #include <assert.h>
  31. #include <stdio.h>
  32. #include <limits.h>
  33. #include <windows.h>
  34. #include <winreg.h>
  35. #include <wine/debug.h>
  36. #include <wine/list.h>
  37. WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
  38. #include "winecfg.h"
  39. #include "resource.h"
  40. static const BOOL is_win64 = (sizeof(void *) > sizeof(int));
  41. HKEY config_key = NULL;
  42. HMENU hPopupMenus = 0;
  43. /* this is called from the WM_SHOWWINDOW handlers of each tab page.
  44. *
  45. * it's a nasty hack, necessary because the property sheet insists on resetting the window title
  46. * to the title of the tab, which is utterly useless. dropping the property sheet is on the todo list.
  47. */
  48. void set_window_title(HWND dialog)
  49. {
  50. WCHAR newtitle[256];
  51. /* update the window title */
  52. if (current_app)
  53. {
  54. WCHAR apptitle[256];
  55. LoadStringW(GetModuleHandleW(NULL), IDS_WINECFG_TITLE_APP, apptitle, ARRAY_SIZE(apptitle));
  56. swprintf(newtitle, ARRAY_SIZE(newtitle), apptitle, current_app);
  57. }
  58. else
  59. {
  60. LoadStringW(GetModuleHandleW(NULL), IDS_WINECFG_TITLE, newtitle, ARRAY_SIZE(newtitle));
  61. }
  62. WINE_TRACE("setting title to %s\n", wine_dbgstr_w (newtitle));
  63. SendMessageW (GetParent(dialog), PSM_SETTITLEW, 0, (LPARAM) newtitle);
  64. }
  65. WCHAR* load_string (UINT id)
  66. {
  67. WCHAR buf[1024];
  68. int len;
  69. WCHAR* newStr;
  70. LoadStringW(GetModuleHandleW(NULL), id, buf, ARRAY_SIZE(buf));
  71. len = lstrlenW (buf);
  72. newStr = HeapAlloc (GetProcessHeap(), 0, (len + 1) * sizeof (WCHAR));
  73. memcpy (newStr, buf, len * sizeof (WCHAR));
  74. newStr[len] = 0;
  75. return newStr;
  76. }
  77. /**
  78. * get_config_key: Retrieves a configuration value from the registry
  79. *
  80. * char *subkey : the name of the config section
  81. * char *name : the name of the config value
  82. * char *default : if the key isn't found, return this value instead
  83. *
  84. * Returns a buffer holding the value if successful, NULL if
  85. * not. Caller is responsible for releasing the result.
  86. *
  87. */
  88. static WCHAR *get_config_key (HKEY root, const WCHAR *subkey, const WCHAR *name, const WCHAR *def)
  89. {
  90. LPWSTR buffer = NULL;
  91. DWORD len;
  92. HKEY hSubKey = NULL;
  93. DWORD res;
  94. WINE_TRACE("subkey=%s, name=%s, def=%s\n", wine_dbgstr_w(subkey),
  95. wine_dbgstr_w(name), wine_dbgstr_w(def));
  96. res = RegOpenKeyExW(root, subkey, 0, MAXIMUM_ALLOWED, &hSubKey);
  97. if (res != ERROR_SUCCESS)
  98. {
  99. if (res == ERROR_FILE_NOT_FOUND)
  100. {
  101. WINE_TRACE("Section key not present - using default\n");
  102. return def ? strdupW(def) : NULL;
  103. }
  104. else
  105. {
  106. WINE_ERR("RegOpenKey failed on wine config key (res=%d)\n", res);
  107. }
  108. goto end;
  109. }
  110. res = RegQueryValueExW(hSubKey, name, NULL, NULL, NULL, &len);
  111. if (res == ERROR_FILE_NOT_FOUND)
  112. {
  113. WINE_TRACE("Value not present - using default\n");
  114. buffer = def ? strdupW(def) : NULL;
  115. goto end;
  116. } else if (res != ERROR_SUCCESS)
  117. {
  118. WINE_ERR("Couldn't query value's length (res=%d)\n", res);
  119. goto end;
  120. }
  121. buffer = HeapAlloc(GetProcessHeap(), 0, len + sizeof(WCHAR));
  122. RegQueryValueExW(hSubKey, name, NULL, NULL, (LPBYTE) buffer, &len);
  123. WINE_TRACE("buffer=%s\n", wine_dbgstr_w(buffer));
  124. end:
  125. RegCloseKey(hSubKey);
  126. return buffer;
  127. }
  128. /**
  129. * set_config_key: convenience wrapper to set a key/value pair
  130. *
  131. * const char *subKey : the name of the config section
  132. * const char *valueName : the name of the config value
  133. * const char *value : the value to set the configuration key to
  134. *
  135. * Returns 0 on success, non-zero otherwise
  136. *
  137. * If valueName or value is NULL, an empty section will be created
  138. */
  139. static int set_config_key(HKEY root, const WCHAR *subkey, REGSAM access, const WCHAR *name, const void *value, DWORD type)
  140. {
  141. DWORD res = 1;
  142. HKEY key = NULL;
  143. WINE_TRACE("subkey=%s: name=%s, value=%p, type=%d\n", wine_dbgstr_w(subkey),
  144. wine_dbgstr_w(name), value, type);
  145. assert( subkey != NULL );
  146. if (subkey[0])
  147. {
  148. res = RegCreateKeyExW( root, subkey, 0, NULL, REG_OPTION_NON_VOLATILE,
  149. access, NULL, &key, NULL );
  150. if (res != ERROR_SUCCESS) goto end;
  151. }
  152. else key = root;
  153. if (name == NULL || value == NULL) goto end;
  154. switch (type)
  155. {
  156. case REG_SZ: res = RegSetValueExW(key, name, 0, REG_SZ, value, (lstrlenW(value)+1)*sizeof(WCHAR)); break;
  157. case REG_DWORD: res = RegSetValueExW(key, name, 0, REG_DWORD, value, sizeof(DWORD)); break;
  158. }
  159. if (res != ERROR_SUCCESS) goto end;
  160. res = 0;
  161. end:
  162. if (key && key != root) RegCloseKey(key);
  163. if (res != 0)
  164. WINE_ERR("Unable to set configuration key %s in section %s, res=%d\n",
  165. wine_dbgstr_w(name), wine_dbgstr_w(subkey), res);
  166. return res;
  167. }
  168. /* ========================================================================= */
  169. /* This code exists for the following reasons:
  170. *
  171. * - It makes working with the registry easier
  172. * - By storing a mini cache of the registry, we can more easily implement
  173. * cancel/revert and apply. The 'settings list' is an overlay on top of
  174. * the actual registry data that we can write out at will.
  175. *
  176. * Rather than model a tree in memory, we simply store each absolute (rooted
  177. * at the config key) path.
  178. *
  179. */
  180. struct setting
  181. {
  182. struct list entry;
  183. HKEY root; /* the key on which path is rooted */
  184. WCHAR *path; /* path in the registry rooted at root */
  185. WCHAR *name; /* name of the registry value. if null, this means delete the key */
  186. WCHAR *value; /* contents of the registry value. if null, this means delete the value */
  187. DWORD type; /* type of registry value. REG_SZ or REG_DWORD for now */
  188. };
  189. static struct list settings = LIST_INIT(settings);
  190. static void free_setting(struct setting *setting)
  191. {
  192. assert( setting != NULL );
  193. assert( setting->path );
  194. WINE_TRACE("destroying %p: %s\n", setting,
  195. wine_dbgstr_w(setting->path));
  196. HeapFree(GetProcessHeap(), 0, setting->path);
  197. HeapFree(GetProcessHeap(), 0, setting->name);
  198. HeapFree(GetProcessHeap(), 0, setting->value);
  199. list_remove(&setting->entry);
  200. HeapFree(GetProcessHeap(), 0, setting);
  201. }
  202. /**
  203. * Returns the contents of the value at path. If not in the settings
  204. * list, it will be fetched from the registry - failing that, the
  205. * default will be used.
  206. *
  207. * If already in the list, the contents as given there will be
  208. * returned. You are expected to HeapFree the result.
  209. */
  210. WCHAR *get_reg_key(HKEY root, const WCHAR *path, const WCHAR *name, const WCHAR *def)
  211. {
  212. struct list *cursor;
  213. struct setting *s;
  214. WCHAR *val;
  215. WINE_TRACE("path=%s, name=%s, def=%s\n", wine_dbgstr_w(path),
  216. wine_dbgstr_w(name), wine_dbgstr_w(def));
  217. /* check if it's in the list */
  218. LIST_FOR_EACH( cursor, &settings )
  219. {
  220. s = LIST_ENTRY(cursor, struct setting, entry);
  221. if (root != s->root) continue;
  222. if (lstrcmpiW(path, s->path) != 0) continue;
  223. if (!s->name) continue;
  224. if (lstrcmpiW(name, s->name) != 0) continue;
  225. WINE_TRACE("found %s:%s in settings list, returning %s\n",
  226. wine_dbgstr_w(path), wine_dbgstr_w(name),
  227. wine_dbgstr_w(s->value));
  228. return s->value ? strdupW(s->value) : NULL;
  229. }
  230. /* no, so get from the registry */
  231. val = get_config_key(root, path, name, def);
  232. WINE_TRACE("returning %s\n", wine_dbgstr_w(val));
  233. return val;
  234. }
  235. /**
  236. * Used to set a registry key.
  237. *
  238. * path is rooted at the config key, ie use "Version" or
  239. * "AppDefaults\\fooapp.exe\\Version". You can use keypath()
  240. * to get such a string.
  241. *
  242. * name is the value name, or NULL to delete the path.
  243. *
  244. * value is what to set the value to, or NULL to delete it.
  245. *
  246. * type is REG_SZ or REG_DWORD.
  247. *
  248. * These values will be copied when necessary.
  249. */
  250. static void set_reg_key_ex(HKEY root, const WCHAR *path, const WCHAR *name, const void *value, DWORD type)
  251. {
  252. struct list *cursor;
  253. struct setting *s;
  254. assert( path != NULL );
  255. WINE_TRACE("path=%s, name=%s, value=%s\n", wine_dbgstr_w(path),
  256. wine_dbgstr_w(name), wine_dbgstr_w(value));
  257. /* firstly, see if we already set this setting */
  258. LIST_FOR_EACH( cursor, &settings )
  259. {
  260. struct setting *s = LIST_ENTRY(cursor, struct setting, entry);
  261. if (root != s->root) continue;
  262. if (lstrcmpiW(s->path, path) != 0) continue;
  263. if ((s->name && name) && lstrcmpiW(s->name, name) != 0) continue;
  264. /* are we attempting a double delete? */
  265. if (!s->name && !name) return;
  266. /* do we want to undelete this key? */
  267. if (!s->name && name) s->name = strdupW(name);
  268. /* yes, we have already set it, so just replace the content and return */
  269. HeapFree(GetProcessHeap(), 0, s->value);
  270. s->type = type;
  271. switch (type)
  272. {
  273. case REG_SZ:
  274. s->value = value ? strdupW(value) : NULL;
  275. break;
  276. case REG_DWORD:
  277. s->value = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
  278. memcpy( s->value, value, sizeof(DWORD) );
  279. break;
  280. }
  281. /* are we deleting this key? this won't remove any of the
  282. * children from the overlay so if the user adds it again in
  283. * that session it will appear to undelete the settings, but
  284. * in reality only the settings actually modified by the user
  285. * in that session will be restored. we might want to fix this
  286. * corner case in future by actually deleting all the children
  287. * here so that once it's gone, it's gone.
  288. */
  289. if (!name) s->name = NULL;
  290. return;
  291. }
  292. /* otherwise add a new setting for it */
  293. s = HeapAlloc(GetProcessHeap(), 0, sizeof(struct setting));
  294. s->root = root;
  295. s->path = strdupW(path);
  296. s->name = name ? strdupW(name) : NULL;
  297. s->type = type;
  298. switch (type)
  299. {
  300. case REG_SZ:
  301. s->value = value ? strdupW(value) : NULL;
  302. break;
  303. case REG_DWORD:
  304. s->value = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
  305. memcpy( s->value, value, sizeof(DWORD) );
  306. break;
  307. }
  308. list_add_tail(&settings, &s->entry);
  309. }
  310. void set_reg_key(HKEY root, const WCHAR *path, const WCHAR *name, const WCHAR *value)
  311. {
  312. set_reg_key_ex(root, path, name, value, REG_SZ);
  313. }
  314. void set_reg_key_dword(HKEY root, const WCHAR *path, const WCHAR *name, DWORD value)
  315. {
  316. set_reg_key_ex(root, path, name, &value, REG_DWORD);
  317. }
  318. /**
  319. * enumerates the value names at the given path, taking into account
  320. * the changes in the settings list.
  321. *
  322. * you are expected to HeapFree each element of the array, which is null
  323. * terminated, as well as the array itself.
  324. */
  325. WCHAR **enumerate_values(HKEY root, const WCHAR *path)
  326. {
  327. HKEY key;
  328. DWORD res, i = 0, valueslen = 0;
  329. WCHAR **values = NULL;
  330. struct list *cursor;
  331. res = RegOpenKeyExW(root, path, 0, MAXIMUM_ALLOWED, &key);
  332. if (res == ERROR_SUCCESS)
  333. {
  334. while (TRUE)
  335. {
  336. WCHAR name[1024];
  337. DWORD namesize = ARRAY_SIZE(name);
  338. BOOL removed = FALSE;
  339. /* find out the needed size, allocate a buffer, read the value */
  340. if ((res = RegEnumValueW(key, i, name, &namesize, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
  341. break;
  342. WINE_TRACE("name=%s\n", wine_dbgstr_w(name));
  343. /* check if this value name has been removed in the settings list */
  344. LIST_FOR_EACH( cursor, &settings )
  345. {
  346. struct setting *s = LIST_ENTRY(cursor, struct setting, entry);
  347. if (lstrcmpiW(s->path, path) != 0) continue;
  348. if (lstrcmpiW(s->name, name) != 0) continue;
  349. if (!s->value)
  350. {
  351. WINE_TRACE("this key has been removed, so skipping\n");
  352. removed = TRUE;
  353. break;
  354. }
  355. }
  356. if (removed) /* this value was deleted by the user, so don't include it */
  357. {
  358. i++;
  359. continue;
  360. }
  361. /* grow the array if necessary, add buffer to it, iterate */
  362. if (values) values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(WCHAR*) * (valueslen + 1));
  363. else values = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR*));
  364. values[valueslen++] = strdupW(name);
  365. WINE_TRACE("valueslen is now %d\n", valueslen);
  366. i++;
  367. }
  368. }
  369. else
  370. {
  371. WINE_WARN("failed opening registry key %s, res=0x%x\n",
  372. wine_dbgstr_w(path), res);
  373. }
  374. WINE_TRACE("adding settings in list but not registry\n");
  375. /* now we have to add the values that aren't in the registry but are in the settings list */
  376. LIST_FOR_EACH( cursor, &settings )
  377. {
  378. struct setting *setting = LIST_ENTRY(cursor, struct setting, entry);
  379. BOOL found = FALSE;
  380. if (lstrcmpiW(setting->path, path) != 0) continue;
  381. if (!setting->value) continue;
  382. for (i = 0; i < valueslen; i++)
  383. {
  384. if (lstrcmpiW(setting->name, values[i]) == 0)
  385. {
  386. found = TRUE;
  387. break;
  388. }
  389. }
  390. if (found) continue;
  391. WINE_TRACE("%s in list but not registry\n", wine_dbgstr_w(setting->name));
  392. /* otherwise it's been set by the user but isn't in the registry */
  393. if (values) values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(WCHAR*) * (valueslen + 1));
  394. else values = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR*));
  395. values[valueslen++] = strdupW(setting->name);
  396. }
  397. WINE_TRACE("adding null terminator\n");
  398. if (values)
  399. {
  400. values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(WCHAR*) * (valueslen + 1));
  401. values[valueslen] = NULL;
  402. }
  403. RegCloseKey(key);
  404. return values;
  405. }
  406. /**
  407. * returns true if the given key/value pair exists in the registry or
  408. * has been written to.
  409. */
  410. BOOL reg_key_exists(HKEY root, const WCHAR *path, const WCHAR *name)
  411. {
  412. WCHAR *val = get_reg_key(root, path, name, NULL);
  413. HeapFree(GetProcessHeap(), 0, val);
  414. return val != NULL;
  415. }
  416. static void process_setting(struct setting *s)
  417. {
  418. HKEY key;
  419. BOOL needs_wow64 = (is_win64 && s->root == HKEY_LOCAL_MACHINE && s->path &&
  420. !wcsnicmp(s->path, L"Software\\", wcslen(L"Software\\")));
  421. if (s->value)
  422. {
  423. WINE_TRACE("Setting %s:%s to '%s'\n", wine_dbgstr_w(s->path),
  424. wine_dbgstr_w(s->name), wine_dbgstr_w(s->value));
  425. set_config_key(s->root, s->path, MAXIMUM_ALLOWED, s->name, s->value, s->type);
  426. if (needs_wow64)
  427. {
  428. WINE_TRACE("Setting 32-bit %s:%s to '%s'\n", wine_dbgstr_w(s->path),
  429. wine_dbgstr_w(s->name), wine_dbgstr_w(s->value));
  430. set_config_key(s->root, s->path, MAXIMUM_ALLOWED | KEY_WOW64_32KEY, s->name, s->value, s->type);
  431. }
  432. }
  433. else
  434. {
  435. WINE_TRACE("Removing %s:%s\n", wine_dbgstr_w(s->path), wine_dbgstr_w(s->name));
  436. if (!RegOpenKeyExW( s->root, s->path, 0, MAXIMUM_ALLOWED, &key ))
  437. {
  438. /* NULL name means remove that path/section entirely */
  439. if (s->name) RegDeleteValueW( key, s->name );
  440. else
  441. {
  442. RegDeleteTreeW( key, NULL );
  443. RegDeleteKeyW( s->root, s->path );
  444. }
  445. RegCloseKey( key );
  446. }
  447. if (needs_wow64)
  448. {
  449. WINE_TRACE("Removing 32-bit %s:%s\n", wine_dbgstr_w(s->path), wine_dbgstr_w(s->name));
  450. if (!RegOpenKeyExW( s->root, s->path, 0, MAXIMUM_ALLOWED | KEY_WOW64_32KEY, &key ))
  451. {
  452. if (s->name) RegDeleteValueW( key, s->name );
  453. else
  454. {
  455. RegDeleteTreeW( key, NULL );
  456. RegDeleteKeyExW( s->root, s->path, KEY_WOW64_32KEY, 0 );
  457. }
  458. RegCloseKey( key );
  459. }
  460. }
  461. }
  462. }
  463. void apply(void)
  464. {
  465. if (list_empty(&settings)) return; /* we will be called for each page when the user clicks OK */
  466. WINE_TRACE("()\n");
  467. while (!list_empty(&settings))
  468. {
  469. struct setting *s = (struct setting *) list_head(&settings);
  470. process_setting(s);
  471. free_setting(s);
  472. }
  473. }
  474. /* ================================== utility functions ============================ */
  475. WCHAR* current_app = NULL; /* the app we are currently editing, or NULL if editing global */
  476. /* returns a registry key path suitable for passing to addTransaction */
  477. WCHAR *keypath(const WCHAR *section)
  478. {
  479. static WCHAR *result = NULL;
  480. HeapFree(GetProcessHeap(), 0, result);
  481. if (current_app)
  482. {
  483. DWORD len = sizeof(L"AppDefaults\\") + (lstrlenW(current_app) + lstrlenW(section) + 1) * sizeof(WCHAR);
  484. result = HeapAlloc(GetProcessHeap(), 0, len );
  485. lstrcpyW( result, L"AppDefaults\\" );
  486. lstrcatW( result, current_app );
  487. if (section[0])
  488. {
  489. len = lstrlenW(result);
  490. result[len++] = '\\';
  491. lstrcpyW( result + len, section );
  492. }
  493. }
  494. else
  495. {
  496. result = strdupW(section);
  497. }
  498. return result;
  499. }
  500. void PRINTERROR(void)
  501. {
  502. LPSTR msg;
  503. FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
  504. 0, GetLastError(), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
  505. (LPSTR)&msg, 0, NULL);
  506. /* eliminate trailing newline, is this a Wine bug? */
  507. *(strrchr(msg, '\r')) = '\0';
  508. WINE_TRACE("error: '%s'\n", msg);
  509. }
  510. BOOL initialize(HINSTANCE hInstance)
  511. {
  512. DWORD res = RegCreateKeyW(HKEY_CURRENT_USER, WINE_KEY_ROOT, &config_key);
  513. if (res != ERROR_SUCCESS) {
  514. WINE_ERR("RegOpenKey failed on wine config key (%d)\n", res);
  515. return TRUE;
  516. }
  517. return FALSE;
  518. }