[PATCH v2 13/15] propsys: Partially implement/stub IPropertyDescription

Jonas Kümmerlin rgcjonas at gmail.com
Sat Jul 18 11:26:52 CDT 2015


Propsys.dll now contains property description information for some builtin
windows properties and exposes this via IPropertyDescription. This allows
to resolve some property names. MSI will need this in the future.

There's still a long way to go, but a rough infrastructure is there for adding
more properties and other features as needed.
---
 dlls/propsys/Makefile.in             |   4 +-
 dlls/propsys/propdesc.c              | 537 +++++++++++++++++++++++++++++++++++
 dlls/propsys/propdesc_builtin_data.c |  85 ++++++
 dlls/propsys/propsys.spec            |   2 +-
 dlls/propsys/propsys_main.c          |   6 -
 dlls/propsys/propsys_private.h       |  23 ++
 dlls/propsys/tests/propsys.c         | 252 ++++++++++++++++
 include/propsys.idl                  |   1 +
 8 files changed, 902 insertions(+), 8 deletions(-)
 create mode 100644 dlls/propsys/propdesc.c
 create mode 100644 dlls/propsys/propdesc_builtin_data.c

diff --git a/dlls/propsys/Makefile.in b/dlls/propsys/Makefile.in
index a445922..9231f48 100644
--- a/dlls/propsys/Makefile.in
+++ b/dlls/propsys/Makefile.in
@@ -5,6 +5,8 @@ IMPORTS   = ole32 oleaut32 uuid
 C_SRCS = \
 	propstore.c \
 	propsys_main.c \
-	propvar.c
+	propvar.c \
+	propdesc.c \
+	propdesc_builtin_data.c
 
 IDL_SRCS = propsys_classes.idl
diff --git a/dlls/propsys/propdesc.c b/dlls/propsys/propdesc.c
new file mode 100644
index 0000000..0658fa8
--- /dev/null
+++ b/dlls/propsys/propdesc.c
@@ -0,0 +1,537 @@
+/*
+ * propsys IPropertyDescription implementation
+ *
+ * Copyright 2015 Jonas Kümmerlin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "propsys.h"
+#include "propvarutil.h"
+
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+#include "propsys_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(propsys);
+
+typedef struct {
+    IPropertyDescription2 IPropertyDescription2_iface;
+    LONG ref;
+    const struct propdesc_builtin_record *record;
+} PropertyDescription;
+
+static inline PropertyDescription *impl_from_IPropertyDescription(IPropertyDescription2 *iface)
+{
+    return CONTAINING_RECORD(iface, PropertyDescription, IPropertyDescription2_iface);
+}
+
+static HRESULT WINAPI PropertyDescription_QueryInterface(IPropertyDescription2 *iface, REFIID iid, void **ppv)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
+
+    if (!ppv) return E_INVALIDARG;
+
+    if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IPropertyDescription, iid)
+        || IsEqualIID(&IID_IPropertyDescription2, iid))
+    {
+        *ppv = &This->IPropertyDescription2_iface;
+    }
+    else
+    {
+        FIXME("No interface for %s\n", debugstr_guid(iid));
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef((IUnknown*)*ppv);
+    return S_OK;
+}
+static ULONG WINAPI PropertyDescription_AddRef(IPropertyDescription2 *iface)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+    ULONG ref = InterlockedIncrement(&This->ref);
+
+    TRACE("(%p) refcount=%u\n", iface, ref);
+
+    return ref;
+}
+
+static ULONG WINAPI PropertyDescription_Release(IPropertyDescription2 *iface)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+    ULONG ref = InterlockedDecrement(&This->ref);
+
+    if (ref == 0)
+    {
+        HeapFree(GetProcessHeap(), 0, This);
+    }
+
+    TRACE("(%p) refcount=%u\n", iface, ref);
+
+    return ref;
+}
+
+static HRESULT WINAPI PropertyDescription_GetPropertyKey(IPropertyDescription2 *iface, PROPERTYKEY *pkey)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, pkey);
+
+    if (!pkey)
+        return E_POINTER;
+
+    *pkey = This->record->key;
+    return S_OK;
+}
+
+static inline HRESULT com_wcsdup(const WCHAR *str, WCHAR **out)
+{
+    DWORD len;
+
+    if (!out || !str)
+        return E_POINTER;
+
+    len = lstrlenW(str);
+    *out = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
+    if (!*out)
+        return E_OUTOFMEMORY;
+
+    memcpy(*out, str, (len+1) * sizeof(WCHAR));
+
+    return len ? S_OK : S_FALSE;
+}
+
+static HRESULT WINAPI PropertyDescription_GetCanonicalName(IPropertyDescription2 *iface, LPWSTR *ppszName)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, ppszName);
+
+    return com_wcsdup(This->record->canonicalName, ppszName);
+}
+
+static HRESULT WINAPI PropertyDescription_GetPropertyType(IPropertyDescription2 *iface, VARTYPE *pvartype)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, pvartype);
+
+    if (!pvartype)
+        return E_POINTER;
+
+    *pvartype = This->record->type;
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetDisplayName(IPropertyDescription2 *iface, LPWSTR *ppszName)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, ppszName);
+
+    return com_wcsdup(This->record->displayName, ppszName);
+}
+
+static HRESULT WINAPI PropertyDescription_GetEditInvitation(IPropertyDescription2 *iface, LPWSTR *ppszName)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, ppszName);
+
+    return com_wcsdup(This->record->editInvitation, ppszName);
+}
+
+static HRESULT WINAPI PropertyDescription_GetTypeFlags(IPropertyDescription2 *iface,
+            PROPDESC_TYPE_FLAGS mask, PROPDESC_TYPE_FLAGS *ppdtFlags)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %x %p\n", iface, mask, ppdtFlags);
+
+    if (!ppdtFlags)
+        return E_POINTER;
+
+    *ppdtFlags = This->record->typeFlags & mask;
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetViewFlags(IPropertyDescription2 *iface, PROPDESC_VIEW_FLAGS *ppdvFlags)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, ppdvFlags);
+
+    if (!ppdvFlags)
+        return E_POINTER;
+
+    *ppdvFlags = This->record->viewFlags;
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetDefaultColumnWidth(IPropertyDescription2 *iface, UINT *p)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, p);
+
+    if (!p)
+        return E_POINTER;
+
+    *p = This->record->displayWidth;
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetDisplayType(IPropertyDescription2 *iface, PROPDESC_DISPLAYTYPE *p)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, p);
+
+    if (!p)
+        return E_POINTER;
+
+    *p = This->record->displayType;
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetColumnState(IPropertyDescription2 *iface, SHCOLSTATEF *p)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, p);
+
+    if (!p)
+        return E_POINTER;
+
+    *p = This->record->columnState;
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetGroupingRange(IPropertyDescription2 *iface, PROPDESC_GROUPING_RANGE *p)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, p);
+
+    if (!p)
+        return E_POINTER;
+
+    *p = This->record->groupingRange;
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetRelativeDescriptionType(IPropertyDescription2 *iface, PROPDESC_RELATIVEDESCRIPTION_TYPE *prdt)
+{
+    FIXME("%p %p: stub\n", iface, prdt);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI PropertyDescription_GetRelativeDescription(IPropertyDescription2 *iface,
+        REFPROPVARIANT propvar1, REFPROPVARIANT propvar2, LPWSTR *ppszDesc1, LPWSTR *ppszDesc2)
+{
+    FIXME("%p %p %p %p %p: stub\n", iface, propvar1, propvar2, ppszDesc1, ppszDesc2);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI PropertyDescription_GetSortDescription(IPropertyDescription2 *iface, PROPDESC_SORTDESCRIPTION *p)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, p);
+
+    if (!p)
+        return E_POINTER;
+
+    *p = This->record->sortDescription;
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetSortDescriptionLabel(IPropertyDescription2 *iface, BOOL fDescending, LPWSTR *ppszDescription)
+{
+    FIXME("%p %d %p: stub\n", iface, fDescending, ppszDescription);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI PropertyDescription_GetAggregationType(IPropertyDescription2 *iface, PROPDESC_AGGREGATION_TYPE *p)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p\n", iface, p);
+
+    if (!p)
+        return E_POINTER;
+
+    *p = This->record->aggregationType;
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetConditionType(IPropertyDescription2 *iface,
+        PROPDESC_CONDITION_TYPE *pcontype, CONDITION_OPERATION *popDefault)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+
+    TRACE("%p %p %p\n", iface, pcontype, popDefault);
+
+    if (!pcontype || !popDefault)
+        return E_POINTER;
+
+    *pcontype = This->record->conditionType;
+    *popDefault = This->record->conditionOperation;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI PropertyDescription_GetEnumTypeList(IPropertyDescription2 *iface,
+        REFIID riid, void **ppv)
+{
+    FIXME("%p %p %p: stub\n", iface, riid, ppv);
+
+    return E_NOTIMPL;
+}
+
+static inline DWORD canonicalize_wstr(WCHAR *str, DWORD len)
+{
+    DWORD whitespaceFront = 0;
+    DWORD whitespaceBack  = 0;
+
+    /* count whitespace at the front */
+    while (whitespaceFront < len && isspaceW(str[whitespaceFront]))
+        ++whitespaceFront;
+
+    /* and remove it */
+    if (whitespaceFront)
+    {
+        len -= whitespaceFront;
+        memmove(str, &str[whitespaceFront], (len+1) * sizeof(WCHAR));
+    }
+
+    /* count whitespace at the back */
+    while (whitespaceBack < len && isspaceW(str[len - 1 - whitespaceBack]))
+        ++whitespaceBack;
+
+    /* and remove it */
+    str[len - whitespaceBack] = 0;
+    return len - whitespaceBack;
+}
+
+static HRESULT WINAPI PropertyDescription_CoerceToCanonicalValue(IPropertyDescription2 *iface, PROPVARIANT *propvar)
+{
+    PropertyDescription *This = impl_from_IPropertyDescription(iface);
+    HRESULT hr = S_OK;
+
+    FIXME("%p %p: semi-stub\n", iface, propvar);
+
+    if (!propvar)
+        return E_POINTER;
+
+    /* Step 1: Convert empty stuff to VT_EMPTY */
+    if (propvar->vt == VT_EMPTY || propvar->vt == VT_NULL ||
+        (propvar->vt == VT_LPWSTR && !propvar->u.pwszVal) ||
+        (propvar->vt == VT_BSTR && !propvar->u.bstrVal) ||
+        (propvar->vt == VT_LPSTR && !propvar->u.pszVal))
+        /* TODO: check for empty/whitespace-only strings */
+    {
+        PropVariantInit(propvar);
+
+        /* VT_EMPTY is always canonical, exit here */
+        return S_OK;
+    }
+
+    /* Step 2: Convert to the required type */
+    if (propvar->vt != This->record->type)
+    {
+        PROPVARIANT tmp;
+        PropVariantInit(&tmp);
+
+        hr = PropVariantChangeType(&tmp, propvar, 0, This->record->type);
+
+        PropVariantClear(propvar);
+        memcpy(propvar, &tmp, sizeof(PROPVARIANT));
+
+        if (FAILED(hr))
+            return hr;
+    }
+
+    /* Step 3: Type-specific canonicalizations */
+    if (propvar->vt == VT_LPWSTR)
+    {
+        DWORD oldlen = lstrlenW(propvar->u.pwszVal);
+        DWORD len = canonicalize_wstr(propvar->u.pwszVal, oldlen);
+
+        if (len == 0)
+            PropVariantClear(propvar);
+
+        if (len < oldlen)
+            hr = INPLACE_S_TRUNCATED;
+    }
+
+    /* TODO: Step4: Check property description type enumeration */
+
+    return hr;
+
+}
+
+static HRESULT WINAPI PropertyDescription_FormatForDisplay(IPropertyDescription2 *iface,
+        REFPROPVARIANT propvar, PROPDESC_FORMAT_FLAGS flags, LPWSTR *ppszDisplay)
+{
+    FIXME("%p %p %x %p: semi-stub\n", iface, propvar, flags, ppszDisplay);
+
+    return PropVariantToStringAlloc(propvar, ppszDisplay);
+}
+
+static HRESULT WINAPI PropertyDescription_IsValueCanonical(IPropertyDescription2 *iface,
+        REFPROPVARIANT propvar)
+{
+    PROPVARIANT canonical;
+    HRESULT hr;
+
+    FIXME("%p %p: semi-stub\n", iface, propvar);
+
+    if (!propvar)
+        return E_POINTER;
+
+    /* TODO: do it better */
+    hr = PropVariantCopy(&canonical, propvar);
+    if (FAILED(hr))
+        return S_OK; /* fake it */
+
+        /* CoerceToCanonicalValue will clear the variant if it fails */
+    hr = IPropertyDescription2_CoerceToCanonicalValue(iface, &canonical);
+    if (FAILED(hr))
+        return S_OK;
+
+    hr = canonical.vt == propvar->vt
+           ?  (PropVariantCompare(&canonical, propvar) ? S_FALSE : S_OK)
+           :  S_FALSE;
+    PropVariantClear(&canonical);
+
+    return hr;
+}
+
+static HRESULT WINAPI PropertyDescription_GetImageReferenceForValue(IPropertyDescription2 *iface,
+        REFPROPVARIANT propvar, LPWSTR *ppszImageRes)
+{
+    FIXME("%p %p %p: stub\n", iface, propvar, ppszImageRes);
+
+    return E_NOTIMPL;
+}
+
+static IPropertyDescription2Vtbl PropertyDescription_Vtbl = {
+    PropertyDescription_QueryInterface,
+    PropertyDescription_AddRef,
+    PropertyDescription_Release,
+    PropertyDescription_GetPropertyKey,
+    PropertyDescription_GetCanonicalName,
+    PropertyDescription_GetPropertyType,
+    PropertyDescription_GetDisplayName,
+    PropertyDescription_GetEditInvitation,
+    PropertyDescription_GetTypeFlags,
+    PropertyDescription_GetViewFlags,
+    PropertyDescription_GetDefaultColumnWidth,
+    PropertyDescription_GetDisplayType,
+    PropertyDescription_GetColumnState,
+    PropertyDescription_GetGroupingRange,
+    PropertyDescription_GetRelativeDescriptionType,
+    PropertyDescription_GetRelativeDescription,
+    PropertyDescription_GetSortDescription,
+    PropertyDescription_GetSortDescriptionLabel,
+    PropertyDescription_GetAggregationType,
+    PropertyDescription_GetConditionType,
+    PropertyDescription_GetEnumTypeList,
+    PropertyDescription_CoerceToCanonicalValue,
+    PropertyDescription_FormatForDisplay,
+    PropertyDescription_IsValueCanonical,
+    PropertyDescription_GetImageReferenceForValue
+};
+
+static HRESULT ConstructFromBuiltinData(const struct propdesc_builtin_record *record, REFIID iid, void **ppv)
+{
+    PropertyDescription *This;
+    HRESULT ret;
+
+    TRACE("(%p,%s,%p)\n", record, debugstr_guid(iid), ppv);
+
+    *ppv = NULL;
+
+    This = HeapAlloc(GetProcessHeap(), 0, sizeof(PropertyDescription));
+    if (!This) return E_OUTOFMEMORY;
+
+    This->IPropertyDescription2_iface.lpVtbl = &PropertyDescription_Vtbl;
+    This->ref = 1;
+    This->record = record;
+
+    ret = IPropertyDescription2_QueryInterface(&This->IPropertyDescription2_iface, iid, ppv);
+    IPropertyDescription2_Release(&This->IPropertyDescription2_iface);
+
+    return ret;
+}
+
+HRESULT WINAPI PSGetPropertyDescription(REFPROPERTYKEY propkey, REFIID riid, void **ppv)
+{
+    HRESULT hr = TYPE_E_ELEMENTNOTFOUND;
+    size_t i;
+
+    TRACE("%p, %p, %p\n", propkey, riid, ppv);
+
+    if (!propkey || !ppv || !riid)
+        return E_INVALIDARG;
+
+    for (i = 0; i < propdesc_builtin_count; ++i)
+    {
+        const struct propdesc_builtin_record *rec = &propdesc_builtin_data[i];
+
+        if (IsEqualGUID(&rec->key.fmtid, &propkey->fmtid) && propkey->pid == rec->key.pid)
+        {
+            hr = ConstructFromBuiltinData(rec, riid, ppv);
+            break;
+        }
+    }
+
+    return hr;
+}
+
+HRESULT WINAPI PSGetPropertyDescriptionByName(LPCWSTR name, REFIID riid, void **ppv)
+{
+    HRESULT hr = TYPE_E_ELEMENTNOTFOUND;
+    size_t i;
+
+    TRACE("%s, %p, %p\n", debugstr_w(name), riid, ppv);
+
+    if (!name || !ppv || !riid)
+        return E_INVALIDARG;
+
+    for (i = 0; i < propdesc_builtin_count; ++i)
+    {
+        const struct propdesc_builtin_record *rec = &propdesc_builtin_data[i];
+
+        if (!lstrcmpW(name, rec->canonicalName))
+        {
+            hr = ConstructFromBuiltinData(rec, riid, ppv);
+            break;
+        }
+    }
+
+    return hr;
+}
diff --git a/dlls/propsys/propdesc_builtin_data.c b/dlls/propsys/propdesc_builtin_data.c
new file mode 100644
index 0000000..9aa87b0
--- /dev/null
+++ b/dlls/propsys/propdesc_builtin_data.c
@@ -0,0 +1,85 @@
+/*
+ * Description data for builtin properties
+ *
+ * Copyright 2015 Jonas Kümmerlin
+ * FIXME: Is copyright applicable here?
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "propidl.h"
+#include "propsys.h"
+#include "propsys_private.h"
+
+#include "initguid.h"
+#include "propkey.h"
+
+struct propdesc_builtin_record propdesc_builtin_data[] = {
+    {
+        { { 0, 0, 0, { 0,0,0,0,0,0,0,0 } }, 0 }, /* key */
+        VT_NULL,
+        { 'S','y','s','t','e','m','.','N','u','l','l',0 }, /* canonical name */
+        { 0 }, /* display name */
+        { 0 }, /* edit invitation */
+        PDDT_STRING, /* display type */
+        20, /* display width */
+        PDAT_DEFAULT, /* aggregation type */
+        SHCOLSTATE_DEFAULT, /* column state */
+        PDCOT_NONE, /* condition type */
+        COP_IMPLICIT, /* condition operation */
+        PDGR_DISCRETE, /* grouping range */
+        PDSD_GENERAL, /* sort description */
+        PDTF_DEFAULT, /* type flags */
+        PDVF_DEFAULT /* view flags */
+    }, {
+        {{0xf29f85e0,0x4ff9,0x1068,{0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9}},2 }, /* key */
+        VT_LPWSTR,
+        { 'S','y','s','t','e','m','.','T','i','t','l','e',0 }, /* canonical name */
+        { 'T','i','t','l','e',0 }, /* display name */
+        { 'A','d','d',' ','a',' ','t','i','t','l','e',0 }, /* edit invitation */
+        PDDT_STRING, /* display type */
+        20, /* display width */
+        PDAT_DEFAULT, /* aggregation type */
+        SHCOLSTATE_TYPE_STR, /* column state */
+        PDCOT_STRING, /* condition type */
+        COP_WORD_EQUAL, /* condition operation */
+        PDGR_ALPHANUMERIC, /* grouping range */
+        PDSD_A_Z, /* sort description */
+        PDTF_ISSYSTEMPROPERTY|PDTF_CANBEPURGED|PDTF_ISVIEWABLE|PDTF_CANSTACKBY|PDTF_CANGROUPBY, /* type flags */
+        PDVF_DEFAULT /* view flags */
+    }, {
+        {{0x9f4c2855,0x9f79,0x4B39,{0xa8,0xd0,0xe1,0xd4,0x2d,0xe1,0xd5,0xf3}},5}, /* key */
+        VT_LPWSTR,
+        { 'S','y','s','t','e','m','.',
+          'A','p','p','U','s','e','r','M','o','d','e','l','.',
+          'I','D', 0 }, /* canonical name */
+        { 'A','p','p','p','U','s','e','r','M','o','d','e','l',0 }, /* display name */
+        { 0 }, /* edit invitation */
+        PDDT_STRING, /* display type */
+        20, /* display width */
+        PDAT_DEFAULT, /* aggregation type */
+        SHCOLSTATE_TYPE_STR, /* column state */
+        PDCOT_STRING, /* condition type */
+        COP_WORD_EQUAL, /* condition operation */
+        PDGR_DISCRETE, /* grouping range */
+        PDSD_LOWEST_HIGHEST, /* sort description */
+        PDTF_ISSYSTEMPROPERTY|PDTF_CANBEPURGED|PDTF_CANSTACKBY|PDTF_CANGROUPBY, /* type flags */
+        PDVF_DEFAULT /* view flags */
+    }
+
+    /*TODO: add more properties */
+};
+
+size_t propdesc_builtin_count = sizeof(propdesc_builtin_data)/sizeof(*propdesc_builtin_data);
diff --git a/dlls/propsys/propsys.spec b/dlls/propsys/propsys.spec
index 970b5c0..055a17a 100644
--- a/dlls/propsys/propsys.spec
+++ b/dlls/propsys/propsys.spec
@@ -80,7 +80,7 @@
 @ stub PSGetNameFromPropertyKey
 @ stub PSGetNamedPropertyFromPropertyStorage
 @ stdcall PSGetPropertyDescription(ptr ptr ptr)
-@ stub PSGetPropertyDescriptionByName
+@ stdcall PSGetPropertyDescriptionByName(ptr ptr ptr)
 @ stdcall PSGetPropertyDescriptionListFromString(ptr ptr ptr)
 @ stub PSGetPropertyFromPropertyStorage
 @ stub PSGetPropertyKeyFromName
diff --git a/dlls/propsys/propsys_main.c b/dlls/propsys/propsys_main.c
index 7631023..c5a8c27 100644
--- a/dlls/propsys/propsys_main.c
+++ b/dlls/propsys/propsys_main.c
@@ -256,12 +256,6 @@ HRESULT WINAPI PSUnregisterPropertySchema(PCWSTR path)
     return E_NOTIMPL;
 }
 
-HRESULT WINAPI PSGetPropertyDescription(REFPROPERTYKEY propkey, REFIID riid, void **ppv)
-{
-    FIXME("%p, %p, %p\n", propkey, riid, ppv);
-    return E_NOTIMPL;
-}
-
 HRESULT WINAPI PSGetPropertyDescriptionListFromString(LPCWSTR proplist, REFIID riid, void **ppv)
 {
     FIXME("%s, %p, %p\n", debugstr_w(proplist), riid, ppv);
diff --git a/dlls/propsys/propsys_private.h b/dlls/propsys/propsys_private.h
index 35bbb4d..329f165 100644
--- a/dlls/propsys/propsys_private.h
+++ b/dlls/propsys/propsys_private.h
@@ -19,3 +19,26 @@
  */
 
 HRESULT PropertyStore_CreateInstance(IUnknown *outer, REFIID riid, void **ppv) DECLSPEC_HIDDEN;
+
+struct propdesc_builtin_record
+{
+    PROPERTYKEY key;
+    VARTYPE type;
+    WCHAR canonicalName[32];  /* extend the string lengths as needed */
+    WCHAR displayName[32];
+    WCHAR editInvitation[32];
+    PROPDESC_DISPLAYTYPE displayType;
+    DWORD displayWidth;
+    PROPDESC_AGGREGATION_TYPE aggregationType;
+    SHCOLSTATEF columnState;
+    PROPDESC_CONDITION_TYPE conditionType;
+    CONDITION_OPERATION conditionOperation;
+    PROPDESC_GROUPING_RANGE groupingRange;
+    PROPDESC_SORTDESCRIPTION sortDescription;
+    PROPDESC_TYPE_FLAGS typeFlags;
+    PROPDESC_VIEW_FLAGS viewFlags;
+    /* TODO: add more info */
+};
+
+extern struct propdesc_builtin_record propdesc_builtin_data[];
+extern size_t propdesc_builtin_count;
diff --git a/dlls/propsys/tests/propsys.c b/dlls/propsys/tests/propsys.c
index 7be320d..098bd55 100644
--- a/dlls/propsys/tests/propsys.c
+++ b/dlls/propsys/tests/propsys.c
@@ -35,6 +35,7 @@
 #include "wine/test.h"
 
 #include "initguid.h"
+#include "propkey.h"
 DEFINE_GUID(dummy_guid, 0xdeadbeef, 0xdead, 0xbeef, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe);
 DEFINE_GUID(expect_guid, 0x12345678, 0x1234, 0x1234, 0x12, 0x34, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12);
 
@@ -430,6 +431,256 @@ static void test_PSPropertyKeyFromString(void)
     }
 }
 
+static void test_propdesc(void)
+{
+    HRESULT hr;
+    WCHAR *str;
+    IPropertyDescription *desc;
+    PROPDESC_AGGREGATION_TYPE aggtype;
+    SHCOLSTATEF colflags;
+    PROPDESC_CONDITION_TYPE condtype;
+    CONDITION_OPERATION condop;
+    PROPDESC_DISPLAYTYPE displaytype;
+    PROPDESC_GROUPING_RANGE groupingrange;
+    PROPDESC_SORTDESCRIPTION sortdesc;
+    PROPDESC_TYPE_FLAGS typeflags;
+    PROPDESC_VIEW_FLAGS viewflags;
+    DWORD dw;
+    PROPERTYKEY key;
+    VARTYPE type;
+    PROPVARIANT pv;
+    WCHAR system_title[] = { 'S','y','s','t','e','m','.','T','i','t','l','e',0 };
+    WCHAR system_appusermodel_id[] = {'S','y','s','t','e','m','.','A','p','p','U','s','e','r','M','o','d','e','l','.','I','D',0 };
+    WCHAR noncanonical_title[] = { '	',' ','H','e','l','l','o',' ','\n',0 };
+    WCHAR canonical_title[] = { 'H','e','l','l','o',0 };
+    WCHAR num_str[] = { '1','2','3','4','5',0 };
+    WCHAR whitespace_str[] = { ' ', '	', '\r', ' ', '\n', 0 };
+    WCHAR empty_str[] = { 0 };
+
+    CoInitialize(NULL);
+
+    hr = PSGetPropertyDescription(&PKEY_Title, &IID_IPropertyDescription, (void**)&desc);
+    ok(hr == S_OK, "Failed to retrieve description for PKEY_Title, hr=%08x\n", hr);
+
+    hr = IPropertyDescription_GetPropertyKey(desc, &key);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(IsEqualGUID(&key.fmtid, &PKEY_Title.fmtid) && key.pid == PKEY_Title.pid,
+        "Unexpected property key %s,%u\n", wine_dbgstr_guid(&key.fmtid), key.pid);
+
+    hr = IPropertyDescription_GetPropertyType(desc, &type);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(type == VT_LPWSTR, "Unexpected variant type %d\n", type);
+
+    hr = IPropertyDescription_GetCanonicalName(desc, &str);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(!lstrcmpW(system_title, str), "Unexpected canonical name %s\n", wine_dbgstr_w(str));
+    CoTaskMemFree(str);
+
+    hr = IPropertyDescription_GetAggregationType(desc, &aggtype);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(aggtype == PDAT_DEFAULT, "Unexpected aggregation type %d\n", aggtype);
+
+    hr = IPropertyDescription_GetColumnState(desc, &colflags);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(colflags == SHCOLSTATE_TYPE_STR, "Unexpected column state %d\n", colflags);
+
+    hr = IPropertyDescription_GetConditionType(desc, &condtype, &condop);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(condtype == PDCOT_STRING, "Unexpected condition type %d\n", condtype);
+    ok(condop == COP_WORD_EQUAL, "Unexpected condition operation %d\n", condop);
+
+    hr = IPropertyDescription_GetDefaultColumnWidth(desc, &dw);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(dw == 20, "Unexpected column width %u\n", dw);
+
+    hr = IPropertyDescription_GetDisplayName(desc, &str);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    /* might be localized, can't compare */
+    CoTaskMemFree(str);
+
+    hr = IPropertyDescription_GetDisplayType(desc, &displaytype);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(displaytype == PDDT_STRING, "Unexpected display type %d\n", displaytype);
+
+    hr = IPropertyDescription_GetEditInvitation(desc, &str);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    /* might be localized, can't compare */
+    CoTaskMemFree(str);
+
+    hr = IPropertyDescription_GetGroupingRange(desc, &groupingrange);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(groupingrange == PDGR_ALPHANUMERIC, "Unexpected grouping range %d\n", groupingrange);
+
+    hr = IPropertyDescription_GetSortDescription(desc, &sortdesc);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(sortdesc == PDSD_A_Z, "Unexpected sort description %d\n", sortdesc);
+
+    hr = IPropertyDescription_GetTypeFlags(desc, PDTF_MASK_ALL, &typeflags);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(typeflags == (PDTF_ISSYSTEMPROPERTY|PDTF_CANBEPURGED|PDTF_ISVIEWABLE|PDTF_CANSTACKBY|PDTF_CANGROUPBY),
+       "Unexpected type flags %x\n", typeflags);
+
+    hr = IPropertyDescription_GetViewFlags(desc, &viewflags);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(viewflags == PDVF_DEFAULT, "Unexpected view flags %d\n", viewflags);
+
+    /* test canonicalization */
+
+    /* noncanonical string */
+    hr = InitPropVariantFromString(noncanonical_title, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_FALSE, "Unexpected hr=%08x\n", hr);
+
+    hr = IPropertyDescription_CoerceToCanonicalValue(desc, &pv);
+    ok(hr == INPLACE_S_TRUNCATED, "Unexpected hr=%08x\n", hr);
+    ok(pv.vt == VT_LPWSTR, "Unexpected variant type %d\n", pv.vt);
+    ok(!lstrcmpW(pv.u.pwszVal, canonical_title), "Unexpected canonical title %s\n", wine_dbgstr_w(pv.u.pwszVal));
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+
+    PropVariantClear(&pv);
+
+    /* string consisting entirely of whitespace */
+    hr = InitPropVariantFromString(whitespace_str, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_FALSE, "Unexpected hr=%08x\n", hr);
+
+    hr = IPropertyDescription_CoerceToCanonicalValue(desc, &pv);
+    ok(hr == INPLACE_S_TRUNCATED, "Unexpected hr=%08x\n", hr);
+    ok(pv.vt == VT_EMPTY, "Unexpected variant type %d\n", pv.vt);
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+
+    PropVariantClear(&pv);
+
+    /* empty string */
+    hr = InitPropVariantFromString(empty_str, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_FALSE, "Unexpected hr=%08x\n", hr);
+
+    hr = IPropertyDescription_CoerceToCanonicalValue(desc, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(pv.vt == VT_EMPTY, "Unexpected variant type %d\n", pv.vt);
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+
+    PropVariantClear(&pv);
+
+    /* null string */
+    pv.vt = VT_LPWSTR;
+    pv.u.pwszVal = NULL;
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_FALSE, "Unexpected hr=%08x\n", hr);
+
+    hr = IPropertyDescription_CoerceToCanonicalValue(desc, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(pv.vt == VT_EMPTY, "Unexpected variant type %d\n", pv.vt);
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+
+    PropVariantClear(&pv);
+
+    /* integer value */
+    pv.vt = VT_I4;
+    pv.u.lVal = 12345;
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_FALSE, "Unexpected hr=%08x\n", hr);
+
+    hr = IPropertyDescription_CoerceToCanonicalValue(desc, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(pv.vt == VT_LPWSTR, "Unexpected variant type %d\n", pv.vt);
+    ok(!lstrcmpW(pv.u.pwszVal, num_str), "Unexpected canonical title %s\n", wine_dbgstr_w(pv.u.pwszVal));
+
+    hr = IPropertyDescription_IsValueCanonical(desc, &pv);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    PropVariantClear(&pv);
+
+    IPropertyDescription_Release(desc);
+
+    /* Test another property, retrieve it by name this time */
+    hr = PSGetPropertyDescriptionByName(system_appusermodel_id, &IID_IPropertyDescription, (void**)&desc);
+    ok(hr == S_OK, "Failed to retrieve description for 'System.AppUserModel.ID', hr=%08x\n", hr);
+
+    hr = IPropertyDescription_GetPropertyKey(desc, &key);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(IsEqualGUID(&key.fmtid, &PKEY_AppUserModel_ID.fmtid) && key.pid == PKEY_AppUserModel_ID.pid,
+        "Unexpected property key %s,%u\n", wine_dbgstr_guid(&key.fmtid), key.pid);
+
+    hr = IPropertyDescription_GetPropertyType(desc, &type);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(type == VT_LPWSTR, "Unexpected variant type %d\n", type);
+
+    hr = IPropertyDescription_GetCanonicalName(desc, &str);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(!lstrcmpW(system_appusermodel_id, str), "Unexpected canonical name %s\n", wine_dbgstr_w(str));
+    CoTaskMemFree(str);
+
+    hr = IPropertyDescription_GetAggregationType(desc, &aggtype);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(aggtype == PDAT_DEFAULT, "Unexpected aggregation type %d\n", aggtype);
+
+    hr = IPropertyDescription_GetColumnState(desc, &colflags);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(colflags == SHCOLSTATE_TYPE_STR, "Unexpected column state %d\n", colflags);
+
+    hr = IPropertyDescription_GetConditionType(desc, &condtype, &condop);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(condtype == PDCOT_STRING, "Unexpected condition type %d\n", condtype);
+    ok(condop == COP_WORD_EQUAL, "Unexpected condition operation %d\n", condop);
+
+    hr = IPropertyDescription_GetDefaultColumnWidth(desc, &dw);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(dw == 20, "Unexpected column width %u\n", dw);
+
+    hr = IPropertyDescription_GetDisplayName(desc, &str);
+    ok(hr == S_OK || broken(hr == E_FAIL) /* win7 */, "Unexpected hr=%08x\n", hr);
+    /* might be localized, can't compare */
+    if (SUCCEEDED(hr))
+        CoTaskMemFree(str);
+
+    hr = IPropertyDescription_GetDisplayType(desc, &displaytype);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(displaytype == PDDT_STRING, "Unexpected display type %d\n", displaytype);
+
+    hr = IPropertyDescription_GetEditInvitation(desc, &str);
+    ok(hr == S_FALSE, "Unexpected hr=%08x\n", hr);
+    /* might be localized, can't compare */
+    CoTaskMemFree(str);
+
+    hr = IPropertyDescription_GetGroupingRange(desc, &groupingrange);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(groupingrange == PDGR_DISCRETE, "Unexpected grouping range %d\n", groupingrange);
+
+    hr = IPropertyDescription_GetSortDescription(desc, &sortdesc);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(sortdesc == PDSD_LOWEST_HIGHEST /* win8 */ || sortdesc == PDSD_A_Z /* win7 */,
+       "Unexpected sort description %d\n", sortdesc);
+
+    hr = IPropertyDescription_GetTypeFlags(desc, PDTF_MASK_ALL, &typeflags);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(typeflags == (PDTF_ISSYSTEMPROPERTY|PDTF_CANBEPURGED|PDTF_CANSTACKBY|PDTF_CANGROUPBY),
+       "Unexpected type flags %x\n", typeflags);
+
+    hr = IPropertyDescription_GetViewFlags(desc, &viewflags);
+    ok(hr == S_OK, "Unexpected hr=%08x\n", hr);
+    ok(viewflags == PDVF_DEFAULT, "Unexpected view flags %d\n", viewflags);
+
+    IPropertyDescription_Release(desc);
+    CoUninitialize();
+}
+
 static void test_PSRefreshPropertySchema(void)
 {
     HRESULT ret;
@@ -1327,4 +1578,5 @@ START_TEST(propsys)
     test_stringify();
     test_intconversions();
     test_serialization();
+    test_propdesc();
 }
diff --git a/include/propsys.idl b/include/propsys.idl
index 04d3fbf..fddf49a 100644
--- a/include/propsys.idl
+++ b/include/propsys.idl
@@ -802,6 +802,7 @@ cpp_quote("#define PKEYSTR_MAX (GUIDSTRING_MAX + 1 + PKEY_PIDSTR_MAX)")
 cpp_quote("HRESULT WINAPI PSStringFromPropertyKey(REFPROPERTYKEY,LPWSTR,UINT);")
 cpp_quote("HRESULT WINAPI PSPropertyKeyFromString(LPCWSTR,PROPERTYKEY*);")
 cpp_quote("HRESULT WINAPI PSGetPropertyDescription(REFPROPERTYKEY,REFIID,void **);")
+cpp_quote("HRESULT WINAPI PSGetPropertyDescriptionByName(LPCWSTR,REFIID,void **);")
 cpp_quote("HRESULT WINAPI PSGetPropertyDescriptionListFromString(LPCWSTR,REFIID,void **);")
 cpp_quote("HRESULT WINAPI PSRefreshPropertySchema(void);")
 cpp_quote("HRESULT WINAPI PSRegisterPropertySchema(LPCWSTR);")
-- 
2.4.3




More information about the wine-devel mailing list