mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 09:58:01 +00:00
6143a50ed2
All the fiddly business where you have to check that a thing exists, make sure of its type, find its size, allocate some memory, and then read it again properly (or, alternatively, loop round dealing with ERROR_MORE_DATA) just doesn't belong at every call site. It's crying out to be moved out into some separate utility functions that present a more ergonomic API, so that the code that decides _which_ Registry entries to read and what to do with them can concentrate on that. So I've written a fresh set of registry API wrappers in windows/utils, and simplified windows/storage.c as a result. The jump-list handling code in particular is almost legible now!
185 lines
5.3 KiB
C
185 lines
5.3 KiB
C
/*
|
|
* Implement convenience wrappers on the awkward low-level functions
|
|
* for accessing the Windows registry.
|
|
*/
|
|
|
|
#include "putty.h"
|
|
|
|
HKEY open_regkey_fn(bool create, HKEY hk, const char *path, ...)
|
|
{
|
|
HKEY toret = NULL;
|
|
bool hk_needs_close = false;
|
|
va_list ap;
|
|
va_start(ap, path);
|
|
|
|
for (; path; path = va_arg(ap, const char *)) {
|
|
HKEY hk_sub = NULL;
|
|
|
|
LONG status;
|
|
if (create)
|
|
status = RegCreateKeyEx(
|
|
hk, path, 0, NULL, REG_OPTION_NON_VOLATILE,
|
|
KEY_READ | KEY_WRITE, NULL, &hk_sub, NULL);
|
|
else
|
|
status = RegOpenKeyEx(
|
|
hk, path, 0, KEY_READ | KEY_WRITE, &hk_sub);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
goto out;
|
|
|
|
if (hk_needs_close)
|
|
RegCloseKey(hk);
|
|
hk = hk_sub;
|
|
hk_needs_close = true;
|
|
}
|
|
|
|
toret = hk;
|
|
hk = NULL;
|
|
hk_needs_close = false;
|
|
|
|
out:
|
|
va_end(ap);
|
|
if (hk_needs_close)
|
|
RegCloseKey(hk);
|
|
return toret;
|
|
}
|
|
|
|
void close_regkey(HKEY key)
|
|
{
|
|
RegCloseKey(key);
|
|
}
|
|
|
|
void del_regkey(HKEY key, const char *name)
|
|
{
|
|
RegDeleteKey(key, name);
|
|
}
|
|
|
|
char *enum_regkey(HKEY key, int index)
|
|
{
|
|
size_t regbuf_size = MAX_PATH + 1;
|
|
char *regbuf = snewn(regbuf_size, char);
|
|
|
|
while (1) {
|
|
LONG status = RegEnumKey(key, index, regbuf, regbuf_size);
|
|
if (status == ERROR_SUCCESS)
|
|
return regbuf;
|
|
if (status != ERROR_MORE_DATA) {
|
|
sfree(regbuf);
|
|
return NULL;
|
|
}
|
|
sgrowarray(regbuf, regbuf_size, regbuf_size);
|
|
}
|
|
}
|
|
|
|
bool get_reg_dword(HKEY key, const char *name, DWORD *out)
|
|
{
|
|
DWORD type, size;
|
|
size = sizeof(*out);
|
|
|
|
if (RegQueryValueEx(key, name, 0, &type,
|
|
(BYTE *)out, &size) != ERROR_SUCCESS ||
|
|
size != sizeof(*out) || type != REG_DWORD)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool put_reg_dword(HKEY key, const char *name, DWORD value)
|
|
{
|
|
return RegSetValueEx(key, name, 0, REG_DWORD, (CONST BYTE *) &value,
|
|
sizeof(value)) == ERROR_SUCCESS;
|
|
}
|
|
|
|
char *get_reg_sz(HKEY key, const char *name)
|
|
{
|
|
DWORD type, size;
|
|
|
|
if (RegQueryValueEx(key, name, 0, &type, NULL,
|
|
&size) != ERROR_SUCCESS || type != REG_SZ)
|
|
return NULL; /* not a string */
|
|
|
|
size_t allocsize = size+1; /* allow for an extra NUL if needed */
|
|
char *toret = snewn(allocsize, char);
|
|
if (RegQueryValueEx(key, name, 0, &type, (BYTE *)toret,
|
|
&size) != ERROR_SUCCESS || type != REG_SZ) {
|
|
sfree(toret);
|
|
return NULL;
|
|
}
|
|
assert(size < allocsize);
|
|
toret[size] = '\0'; /* add an extra NUL in case RegQueryValueEx
|
|
* didn't supply one */
|
|
|
|
return toret;
|
|
}
|
|
|
|
bool put_reg_sz(HKEY key, const char *name, const char *str)
|
|
{
|
|
/* You have to store the trailing NUL as well */
|
|
return RegSetValueEx(key, name, 0, REG_SZ, (CONST BYTE *)str,
|
|
1 + strlen(str)) == ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* REG_MULTI_SZ items are stored as a concatenation of NUL-terminated
|
|
* strings, terminated in turn with an empty string, i.e. a second
|
|
* consecutive NUL.
|
|
*
|
|
* We represent these in their storage format, as a strbuf - but
|
|
* *without* the second consecutive NUL.
|
|
*
|
|
* So you can build up a new MULTI_SZ value in a strbuf by calling
|
|
* put_asciz once per output string and then put_reg_multi_sz; and you
|
|
* can consume one by initialising a BinarySource to the result of
|
|
* get_reg_multi_sz, and then calling get_asciz on it and assuming
|
|
* that !get_err(src) means you have a real output string.
|
|
*
|
|
* Also, calling strbuf_to_str on one of these will give you back a
|
|
* bare 'char *' with the same double-NUL termination, to pass back to
|
|
* a caller.
|
|
*/
|
|
strbuf *get_reg_multi_sz(HKEY key, const char *name)
|
|
{
|
|
DWORD type, size;
|
|
|
|
if (RegQueryValueEx(key, name, 0, &type, NULL,
|
|
&size) != ERROR_SUCCESS || type != REG_MULTI_SZ)
|
|
return NULL; /* not a string */
|
|
|
|
strbuf *toret = strbuf_new();
|
|
void *ptr = strbuf_append(toret, (size_t)size + 2);
|
|
if (RegQueryValueEx(key, name, 0, &type, (BYTE *)ptr,
|
|
&size) != ERROR_SUCCESS || type != REG_MULTI_SZ) {
|
|
strbuf_free(toret);
|
|
return NULL;
|
|
}
|
|
strbuf_shrink_to(toret, size);
|
|
/* Ensure we end with exactly one \0 */
|
|
while (strbuf_chomp(toret, '\0'));
|
|
put_byte(toret, '\0');
|
|
return toret;
|
|
}
|
|
|
|
bool put_reg_multi_sz(HKEY key, const char *name, strbuf *str)
|
|
{
|
|
/*
|
|
* Of course, to write our string list into the registry, we _do_
|
|
* have to include both trailing NULs. But this is easy, because a
|
|
* strbuf is also designed to hold a single string and make it
|
|
* conveniently accessible in NUL-terminated form, so it stores a
|
|
* NUL in its buffer just beyond its formal length. So we just
|
|
* include that extra byte in the data we write.
|
|
*/
|
|
return RegSetValueEx(key, name, 0, REG_MULTI_SZ, (CONST BYTE *)str->s,
|
|
str->len + 1) == ERROR_SUCCESS;
|
|
}
|
|
|
|
char *get_reg_sz_simple(HKEY key, const char *name, const char *leaf)
|
|
{
|
|
HKEY subkey = open_regkey(false, key, name);
|
|
if (!subkey)
|
|
return NULL;
|
|
char *toret = get_reg_sz(subkey, leaf);
|
|
RegCloseKey(subkey);
|
|
return toret;
|
|
}
|