This has been implemented in a few different ways (see MR !7238 and MR !359), neither of which have associated tests.
This way of doing things sidesteps the need to update existing `VT_BLOB` properties by just storing/retrieving them in the same format we always have. If the registry data doesn't match a set of criteria, we treat it as `VT_BLOB` always.
-- v2: mmdevapi: Add support for storing VT_CLSID properties. mmdevapi: Add support for storing VT_BOOL properties.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/mmdevapi/tests/Makefile.in | 2 +- dlls/mmdevapi/tests/propstore.c | 175 +++++++++++++++++++++++++++++++- 2 files changed, 175 insertions(+), 2 deletions(-)
diff --git a/dlls/mmdevapi/tests/Makefile.in b/dlls/mmdevapi/tests/Makefile.in index d7408be1bbf..dc515833ce1 100644 --- a/dlls/mmdevapi/tests/Makefile.in +++ b/dlls/mmdevapi/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = mmdevapi.dll -IMPORTS = ole32 version user32 advapi32 winmm +IMPORTS = ole32 version user32 advapi32 winmm oleaut32 propsys
SOURCES = \ capture.c \ diff --git a/dlls/mmdevapi/tests/propstore.c b/dlls/mmdevapi/tests/propstore.c index b333a556567..88496375dd8 100644 --- a/dlls/mmdevapi/tests/propstore.c +++ b/dlls/mmdevapi/tests/propstore.c @@ -31,6 +31,7 @@ #include "ks.h" #include "ksmedia.h" #include "mmreg.h" +#include "propvarutil.h"
static BOOL (WINAPI *pIsWow64Process)(HANDLE, BOOL *);
@@ -133,19 +134,150 @@ static void test_getat(IPropertyStore *store) ok(found_desc, "DEVPKEY_Device_DeviceDesc not found\n"); }
+static const VARIANT_BOOL vt_bool_vals[] = { VARIANT_TRUE, VARIANT_FALSE }; +static const GUID vt_clsid_vals[] = { { 1 }, { 2 } }; +static const BYTE vt_blob_val[] = { 1, 2, 3, 4, 5, 6 }; +static const WCHAR vt_bstr_val[] = L"Test1"; + +static ULONG set_propvariant_for_vt(PROPVARIANT *pv, VARTYPE vt, const void *val, DWORD size) +{ + ULONG elems = 1; + + PropVariantInit(pv); + pv->vt = vt; + switch (vt) + { + case VT_BOOL: + pv->boolVal = ((const VARIANT_BOOL *)val)[0]; + break; + + case VT_BOOL | VT_VECTOR: + pv->cabool.cElems = size / sizeof(*pv->cabool.pElems); + pv->cabool.pElems = CoTaskMemAlloc(size); + memcpy(pv->cabool.pElems, val, size); + elems = pv->cabool.cElems; + break; + + case VT_CLSID: + pv->puuid = CoTaskMemAlloc(sizeof(*pv->puuid)); + *pv->puuid = ((const GUID *)val)[0]; + break; + + case VT_CLSID | VT_VECTOR: + pv->cauuid.cElems = size / sizeof(*pv->cauuid.pElems); + pv->cauuid.pElems = CoTaskMemAlloc(size); + memcpy(pv->cauuid.pElems, val, size); + elems = pv->cauuid.cElems; + break; + + case VT_BSTR: + pv->bstrVal = SysAllocString((const WCHAR *)val); + break; + + case VT_BLOB: + pv->blob.cbSize = size; + pv->blob.pBlobData = CoTaskMemAlloc(size); + memcpy(pv->blob.pBlobData, val, size); + break; + } + + return elems; +} + +static BOOL compare_propvariant(PROPVARIANT *pv, PROPVARIANT *pv2) +{ + unsigned int i; + + if (pv->vt != pv2->vt) + return FALSE; + + switch (pv->vt) + { + case VT_BSTR: + case VT_CLSID: + return !PropVariantCompareEx(pv, pv2, 0, 0); + + case VT_BOOL: + return pv->boolVal == pv2->boolVal; + + case VT_BOOL | VT_VECTOR: + if (pv->cabool.cElems != pv2->cabool.cElems) + return FALSE; + + for (i = 0; i < pv->cabool.cElems; i++) + { + if (pv->cabool.pElems[i] != pv2->cabool.pElems[i]) + return FALSE; + } + return TRUE; + + case VT_CLSID | VT_VECTOR: + if (pv->cauuid.cElems != pv2->cauuid.cElems) + return FALSE; + + for (i = 0; i < pv->cauuid.cElems; i++) + { + if (memcmp(&pv->cauuid.pElems[i], &pv2->cauuid.pElems[i], sizeof(*pv->cauuid.pElems))) + return FALSE; + } + return TRUE; + + case VT_BLOB: + if (pv->blob.cbSize != pv2->blob.cbSize) + return FALSE; + + return !memcmp(pv->blob.pBlobData, pv2->blob.pBlobData, pv->blob.cbSize); + } + + return FALSE; +} + +struct reg_serialized { + VARTYPE vt; + WORD unk; /* Seems like mostly uninitialized memory... */ + ULONG elems; + BYTE data[]; +}; + static void test_setvalue_on_wow64(IPropertyStore *store) { - PROPVARIANT pv; + static const struct + { + VARTYPE vt; + const void *data; + DWORD data_size; + + HRESULT expected_hr; + DWORD expected_reg_type; + DWORD expected_size; + BOOL todo_hr; + BOOL todo_data; + } propvar_tests[] = + { + { VT_BOOL, vt_bool_vals, sizeof(vt_bool_vals[0]), S_OK, REG_BINARY, 0xa, TRUE, TRUE }, + { VT_BOOL | VT_VECTOR, vt_bool_vals, sizeof(vt_bool_vals), S_OK, REG_BINARY, 0xc, TRUE, TRUE }, + { VT_CLSID, vt_clsid_vals, sizeof(vt_clsid_vals[0]), S_OK, REG_BINARY, 0x18, TRUE, TRUE }, + { VT_CLSID | VT_VECTOR, vt_clsid_vals, sizeof(vt_clsid_vals), S_OK, REG_BINARY, 0x28, TRUE, TRUE }, + { VT_BSTR, vt_bstr_val, sizeof(vt_bstr_val), S_OK, REG_BINARY, 0x14, TRUE, TRUE }, + { VT_BLOB, vt_blob_val, sizeof(vt_blob_val), S_OK, REG_BINARY, 0xe, .todo_data = TRUE }, + }; + PROPVARIANT pv, pv2; HRESULT hr; LONG ret; WCHAR *guidW; HKEY root, props, devkey; DWORD type, regval, size; + unsigned int i; + BYTE buf[256];
static const PROPERTYKEY PKEY_Bogus = { {0x1da5d803, 0xd492, 0x4edd, {0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x00}}, 0x7f }; + static const PROPERTYKEY PKEY_Bogus2 = { + {0x1da5d803, 0xd492, 0x4edd, {0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x00}}, 0x80 + }; static const WCHAR bogusW[] = L"{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F00},127"; + static const WCHAR bogus2W[] = L"{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F00},128";
PropVariantInit(&pv);
@@ -189,6 +321,47 @@ static void test_setvalue_on_wow64(IPropertyStore *store) ok(type == REG_DWORD, "Got wrong value type: %lu\n", type); ok(regval == 0xAB, "Got wrong value: 0x%lx\n", regval);
+ for (i = 0; i < ARRAY_SIZE(propvar_tests); i++) + { + struct reg_serialized *reg_val; + ULONG expected_elems; + + winetest_push_context("Test %u", i); + + expected_elems = set_propvariant_for_vt(&pv, propvar_tests[i].vt, propvar_tests[i].data, propvar_tests[i].data_size); + hr = IPropertyStore_SetValue(store, &PKEY_Bogus2, &pv); + todo_wine_if(propvar_tests[i].todo_hr) ok(hr == propvar_tests[i].expected_hr, "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) + { + winetest_pop_context(); + PropVariantClear(&pv); + continue; + } + + ret = RegQueryValueExW(props, bogus2W, NULL, &type, NULL, &size); + ok(ret == ERROR_SUCCESS, "Couldn't get bogus propertykey value: %lu.\n", ret); + ok(type == propvar_tests[i].expected_reg_type, "Unexpected registry value type %lu.\n", type); + todo_wine_if(propvar_tests[i].todo_data) ok(size >= propvar_tests[i].expected_size, "Unexpected registry value size 0x%lx.\n", size); + + ret = RegQueryValueExW(props, bogus2W, NULL, &type, buf, &size); + ok(ret == ERROR_SUCCESS, "Couldn't get bogus propertykey value: %lu.\n", ret); + reg_val = (struct reg_serialized *)buf; + todo_wine_if(propvar_tests[i].todo_data) + ok(reg_val->vt == propvar_tests[i].vt, "Unexpected vt: %#x.\n", reg_val->vt); + todo_wine_if(propvar_tests[i].todo_data) + ok(reg_val->elems == expected_elems, "Unexpected elems: %lu.\n", reg_val->elems); + todo_wine_if(propvar_tests[i].todo_data) + ok(!memcmp(reg_val->data, propvar_tests[i].data, propvar_tests[i].data_size), "Unexpected data.\n"); + + hr = IPropertyStore_GetValue(store, &PKEY_Bogus2, &pv2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(compare_propvariant(&pv, &pv2), "Propvariant mismatch.\n"); + + PropVariantClear(&pv); + PropVariantClear(&pv2); + winetest_pop_context(); + } + RegCloseKey(props); RegCloseKey(devkey); RegCloseKey(root);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/mmdevapi/devenum.c | 150 ++++++++++++++++++++++++++++++-- dlls/mmdevapi/tests/propstore.c | 4 +- 2 files changed, 147 insertions(+), 7 deletions(-)
diff --git a/dlls/mmdevapi/devenum.c b/dlls/mmdevapi/devenum.c index adce05da954..0f48fe161a6 100644 --- a/dlls/mmdevapi/devenum.c +++ b/dlls/mmdevapi/devenum.c @@ -44,6 +44,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
+#define WINE_REG_PROP_MAGIC 0xbeef +struct reg_prop_serialized { + VARTYPE vt; + WORD unk; /* Uninitialized memory on native, we store a magic value here. */ + ULONG elems; + BYTE data[]; +}; + static HKEY key_render; static HKEY key_capture;
@@ -260,6 +268,116 @@ static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *pr return S_OK; }
+static BOOL is_vector_vt(VARTYPE vt) +{ + return !!(vt & VT_VECTOR); +} + +static unsigned int get_vt_elem_size(VARTYPE vt) +{ + switch (vt & VT_TYPEMASK) + { + case VT_BOOL: + return sizeof(VARIANT_BOOL); + + default: + return 0; + } +} + +static BOOL is_valid_serialized_reg_prop(BYTE *data, DWORD data_size) +{ + struct reg_prop_serialized *reg_prop; + unsigned int elem_size; + + if (data_size <= sizeof(*reg_prop)) + return FALSE; + + reg_prop = (struct reg_prop_serialized *)data; + if (reg_prop->unk != WINE_REG_PROP_MAGIC) + return FALSE; + + if (((reg_prop->vt & VT_TYPEMASK) > VT_CLSID)) + return FALSE; + + if (!!(reg_prop->vt & ~VT_TYPEMASK) && !is_vector_vt(reg_prop->vt)) + return FALSE; + + if (!reg_prop->elems || ((reg_prop->elems > 1) && !is_vector_vt(reg_prop->vt))) + return FALSE; + + elem_size = get_vt_elem_size(reg_prop->vt); + if (elem_size && (((elem_size * reg_prop->elems) + sizeof(*reg_prop)) > data_size)) + return FALSE; + + return TRUE; +} + +static HRESULT deserialize_reg_prop(BYTE *data, DWORD data_size, PROPVARIANT *pv) +{ + struct reg_prop_serialized *reg_prop = (struct reg_prop_serialized *)data; + unsigned int elems_size; + HRESULT hr = S_OK; + + switch (reg_prop->vt) + { + case VT_BOOL: + pv->vt = reg_prop->vt; + pv->boolVal = ((VARIANT_BOOL *)reg_prop->data)[0]; + break; + + case VT_BOOL | VT_VECTOR: + pv->vt = reg_prop->vt; + pv->cabool.cElems = reg_prop->elems; + elems_size = sizeof(*pv->cabool.pElems) * reg_prop->elems; + pv->cabool.pElems = CoTaskMemAlloc(elems_size); + memcpy(pv->cabool.pElems, reg_prop->data, elems_size); + break; + + default: + ERR("Tried to deserialize unhandled vt %#x.\n", reg_prop->vt); + hr = E_NOTIMPL; + break; + } + + return hr; +} + +static HRESULT serialize_reg_prop(HKEY reg_key, const WCHAR *prop_id, PROPVARIANT *pv) +{ + const struct reg_prop_serialized reg_prop_init = { pv->vt, WINE_REG_PROP_MAGIC }; + struct reg_prop_serialized *reg_prop = NULL; + unsigned int size = sizeof(reg_prop_init); + unsigned int elems_size, elem_count = 1; + void *elems_val = NULL; + LONG ret; + + switch (pv->vt) + { + case VT_BOOL: + elems_size = sizeof(pv->boolVal); + elems_val = &pv->boolVal; + break; + + case VT_BOOL | VT_VECTOR: + elem_count = pv->cabool.cElems; + elems_size = elem_count * sizeof(*pv->cabool.pElems); + elems_val = pv->cabool.pElems; + break; + } + + size += elems_size; + if (!(reg_prop = malloc(size))) + return E_OUTOFMEMORY; + + *reg_prop = reg_prop_init; + reg_prop->elems = elem_count; + memcpy(reg_prop->data, elems_val, elems_size); + ret = RegSetValueExW(reg_key, prop_id, 0, REG_BINARY, (BYTE *)reg_prop, size); + free(reg_prop); + return !ret ? S_OK : E_FAIL; +} + static HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv) { WCHAR buffer[80]; @@ -304,13 +422,26 @@ static HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERT } case REG_BINARY: { - pv->vt = VT_BLOB; - pv->blob.cbSize = size; - pv->blob.pBlobData = CoTaskMemAlloc(size); - if (!pv->blob.pBlobData) + BYTE *data = CoTaskMemAlloc(size); + + if (!data) + { hr = E_OUTOFMEMORY; + break; + } + + RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, data, &size); + if (is_valid_serialized_reg_prop(data, size)) + { + hr = deserialize_reg_prop(data, size, pv); + CoTaskMemFree(data); + } else - RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->blob.pBlobData, &size); + { + pv->vt = VT_BLOB; + pv->blob.cbSize = size; + pv->blob.pBlobData = data; + } break; } default: @@ -343,6 +474,15 @@ static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERT ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->ulVal, sizeof(DWORD)); break; } + + case VT_BOOL: + case VT_BOOL | VT_VECTOR: + { + hr = serialize_reg_prop(regkey, buffer, (PROPVARIANT *)pv); + ret = 0; + break; + } + case VT_BLOB: { ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->blob.pBlobData, pv->blob.cbSize); diff --git a/dlls/mmdevapi/tests/propstore.c b/dlls/mmdevapi/tests/propstore.c index 88496375dd8..15d1f5b4866 100644 --- a/dlls/mmdevapi/tests/propstore.c +++ b/dlls/mmdevapi/tests/propstore.c @@ -254,8 +254,8 @@ static void test_setvalue_on_wow64(IPropertyStore *store) BOOL todo_data; } propvar_tests[] = { - { VT_BOOL, vt_bool_vals, sizeof(vt_bool_vals[0]), S_OK, REG_BINARY, 0xa, TRUE, TRUE }, - { VT_BOOL | VT_VECTOR, vt_bool_vals, sizeof(vt_bool_vals), S_OK, REG_BINARY, 0xc, TRUE, TRUE }, + { VT_BOOL, vt_bool_vals, sizeof(vt_bool_vals[0]), S_OK, REG_BINARY, 0xa }, + { VT_BOOL | VT_VECTOR, vt_bool_vals, sizeof(vt_bool_vals), S_OK, REG_BINARY, 0xc }, { VT_CLSID, vt_clsid_vals, sizeof(vt_clsid_vals[0]), S_OK, REG_BINARY, 0x18, TRUE, TRUE }, { VT_CLSID | VT_VECTOR, vt_clsid_vals, sizeof(vt_clsid_vals), S_OK, REG_BINARY, 0x28, TRUE, TRUE }, { VT_BSTR, vt_bstr_val, sizeof(vt_bstr_val), S_OK, REG_BINARY, 0x14, TRUE, TRUE },
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/mmdevapi/devenum.c | 36 ++++++++++++++++++++++++++++++++- dlls/mmdevapi/tests/propstore.c | 4 ++-- 2 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/dlls/mmdevapi/devenum.c b/dlls/mmdevapi/devenum.c index 0f48fe161a6..8cd148b0a3c 100644 --- a/dlls/mmdevapi/devenum.c +++ b/dlls/mmdevapi/devenum.c @@ -280,6 +280,9 @@ static unsigned int get_vt_elem_size(VARTYPE vt) case VT_BOOL: return sizeof(VARIANT_BOOL);
+ case VT_CLSID: + return sizeof(GUID); + default: return 0; } @@ -334,6 +337,20 @@ static HRESULT deserialize_reg_prop(BYTE *data, DWORD data_size, PROPVARIANT *pv memcpy(pv->cabool.pElems, reg_prop->data, elems_size); break;
+ case VT_CLSID: + pv->vt = reg_prop->vt; + pv->puuid = CoTaskMemAlloc(sizeof(*pv->puuid)); + *pv->puuid = ((GUID *)reg_prop->data)[0]; + break; + + case VT_CLSID | VT_VECTOR: + pv->vt = reg_prop->vt; + pv->cauuid.cElems = reg_prop->elems; + elems_size = sizeof(*pv->cauuid.pElems) * reg_prop->elems; + pv->cauuid.pElems = CoTaskMemAlloc(elems_size); + memcpy(pv->cauuid.pElems, reg_prop->data, elems_size); + break; + default: ERR("Tried to deserialize unhandled vt %#x.\n", reg_prop->vt); hr = E_NOTIMPL; @@ -348,7 +365,7 @@ static HRESULT serialize_reg_prop(HKEY reg_key, const WCHAR *prop_id, PROPVARIAN const struct reg_prop_serialized reg_prop_init = { pv->vt, WINE_REG_PROP_MAGIC }; struct reg_prop_serialized *reg_prop = NULL; unsigned int size = sizeof(reg_prop_init); - unsigned int elems_size, elem_count = 1; + unsigned int elems_size = 0, elem_count = 1; void *elems_val = NULL; LONG ret;
@@ -364,6 +381,21 @@ static HRESULT serialize_reg_prop(HKEY reg_key, const WCHAR *prop_id, PROPVARIAN elems_size = elem_count * sizeof(*pv->cabool.pElems); elems_val = pv->cabool.pElems; break; + + case VT_CLSID: + elems_size = sizeof(*pv->puuid); + elems_val = pv->puuid; + break; + + case VT_CLSID | VT_VECTOR: + elem_count = pv->cauuid.cElems; + elems_size = elem_count * sizeof(*pv->cauuid.pElems); + elems_val = pv->cauuid.pElems; + break; + + default: + assert(0); + break; }
size += elems_size; @@ -477,6 +509,8 @@ static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERT
case VT_BOOL: case VT_BOOL | VT_VECTOR: + case VT_CLSID: + case VT_CLSID | VT_VECTOR: { hr = serialize_reg_prop(regkey, buffer, (PROPVARIANT *)pv); ret = 0; diff --git a/dlls/mmdevapi/tests/propstore.c b/dlls/mmdevapi/tests/propstore.c index 15d1f5b4866..3c1e6c1512a 100644 --- a/dlls/mmdevapi/tests/propstore.c +++ b/dlls/mmdevapi/tests/propstore.c @@ -256,8 +256,8 @@ static void test_setvalue_on_wow64(IPropertyStore *store) { { VT_BOOL, vt_bool_vals, sizeof(vt_bool_vals[0]), S_OK, REG_BINARY, 0xa }, { VT_BOOL | VT_VECTOR, vt_bool_vals, sizeof(vt_bool_vals), S_OK, REG_BINARY, 0xc }, - { VT_CLSID, vt_clsid_vals, sizeof(vt_clsid_vals[0]), S_OK, REG_BINARY, 0x18, TRUE, TRUE }, - { VT_CLSID | VT_VECTOR, vt_clsid_vals, sizeof(vt_clsid_vals), S_OK, REG_BINARY, 0x28, TRUE, TRUE }, + { VT_CLSID, vt_clsid_vals, sizeof(vt_clsid_vals[0]), S_OK, REG_BINARY, 0x18 }, + { VT_CLSID | VT_VECTOR, vt_clsid_vals, sizeof(vt_clsid_vals), S_OK, REG_BINARY, 0x28 }, { VT_BSTR, vt_bstr_val, sizeof(vt_bstr_val), S_OK, REG_BINARY, 0x14, TRUE, TRUE }, { VT_BLOB, vt_blob_val, sizeof(vt_blob_val), S_OK, REG_BINARY, 0xe, .todo_data = TRUE }, };