[PATCH v2 14/15] msi: Write shortcut properties

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


Msi now evaluates the MsiShortcutProperty table and applies the properties
to the created shortcuts.
---
 dlls/msi/Makefile.in        |   2 +-
 dlls/msi/action.c           | 120 ++++++++++++++++++++++++++++++++++++++++++++
 dlls/msi/tests/Makefile.in  |   2 +-
 dlls/msi/tests/action.c     |  75 +++++++++++++++++++++++++--
 dlls/msi/tests/automation.c |   1 -
 5 files changed, 194 insertions(+), 6 deletions(-)

diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in
index 79704ad..8df4b26 100644
--- a/dlls/msi/Makefile.in
+++ b/dlls/msi/Makefile.in
@@ -1,7 +1,7 @@
 MODULE    = msi.dll
 IMPORTLIB = msi
 IMPORTS   = uuid urlmon wininet comctl32 shell32 shlwapi cabinet oleaut32 ole32 version user32 gdi32 advapi32
-DELAYIMPORTS = odbccp32 wintrust crypt32 imagehlp mspatcha
+DELAYIMPORTS = odbccp32 wintrust crypt32 imagehlp mspatcha propsys
 
 C_SRCS = \
 	action.c \
diff --git a/dlls/msi/action.c b/dlls/msi/action.c
index c0ab23d..88f0c34 100644
--- a/dlls/msi/action.c
+++ b/dlls/msi/action.c
@@ -39,6 +39,7 @@
 #include "imagehlp.h"
 #include "wine/unicode.h"
 #include "winver.h"
+#include "propvarutil.h"
 
 #define REG_PROGRESS_VALUE 13200
 #define COMPONENT_PROGRESS_VALUE 24000
@@ -3888,6 +3889,122 @@ WCHAR *msi_build_icon_path( MSIPACKAGE *package, const WCHAR *icon_name )
     return path;
 }
 
+struct AddShortcutProperties_closure
+{
+    MSIPACKAGE *package;
+    IShellLinkW *link;
+};
+static UINT ITERATE_AddShortcutProperties(MSIRECORD *row, LPVOID param)
+{
+    struct AddShortcutProperties_closure *closure = param;
+    IShellLinkW *link = closure->link;
+    MSIPACKAGE  *package = closure->package;
+    IPropertyStore *store = NULL;
+    IPropertyDescription *desc = NULL;
+    WCHAR *name = NULL, *value = NULL;
+    PROPVARIANT v;
+    PROPERTYKEY key;
+    HRESULT hr;
+
+    PropVariantInit(&v);
+
+    hr = IShellLinkW_QueryInterface(link, &IID_IPropertyStore, (void**)&store);
+    if (FAILED(hr))
+    {
+        ERR("QueryInterface(IID_IPropertyStore): %x\n", hr);
+        return ERROR_SUCCESS;
+    }
+
+    deformat_string(package, MSI_RecordGetString(row, 3), &name);
+    deformat_string(package, MSI_RecordGetString(row, 4), &value);
+
+    hr = PSGetPropertyDescriptionByName(name, &IID_IPropertyDescription, (void**)&desc);
+    if (FAILED(hr))
+    {
+        WARN("Unknown property %s, ignoring\n", debugstr_w(name));
+        goto out;
+    }
+
+    hr = InitPropVariantFromString(value, &v);
+    if (FAILED(hr))
+    {
+        ERR("InitPropVariantFromString hr=%x\n", hr);
+        goto out;
+    }
+
+    hr = IPropertyDescription_GetPropertyKey(desc, &key);
+    if (FAILED(hr))
+    {
+        ERR("IPropertyDescription::GetPropertyKey hr=%x\n", hr);
+        goto out;
+    }
+
+    hr = IPropertyDescription_CoerceToCanonicalValue(desc, &v);
+    if (FAILED(hr))
+    {
+        ERR("IPropertyDescription::CoerceToCanonicalValue hr=%x\n", hr);
+        goto out;
+    }
+
+    hr = IPropertyStore_SetValue(store, &key, &v);
+    if (FAILED(hr))
+    {
+        ERR("IPropertyStore::SetValue hr=%x\n", hr);
+    }
+
+out:
+    PropVariantClear(&v);
+    if (store) IPropertyStore_Release(store);
+    if (desc)  IPropertyDescription_Release(desc);
+
+    msi_free(name);
+    msi_free(value);
+
+    return ERROR_SUCCESS; /* fake it if necessary */
+}
+
+static UINT AddShortcutProperties(MSIPACKAGE *package, const WCHAR *shortcut, IShellLinkW *link)
+{
+    static const WCHAR queryTemplate[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+        '`','M','s','i','S','h','o','r','t','c','u','t','P','r','o','p','e','r','t','y','`',' ',
+        'W','H','E','R','E',' ','`','S','h','o','r','t','c','u','t','_','`',
+            ' ','=',' ','\'','%','s','\'', 0};
+    WCHAR *query;
+    MSIQUERY *view;
+    HRESULT res;
+    UINT rc;
+    struct AddShortcutProperties_closure closure = {
+        package, link
+    };
+
+    query = msi_alloc(sizeof(queryTemplate) + lstrlenW(shortcut)*sizeof(WCHAR));
+    if (!query)
+    {
+        ERR("No memory while querying shortcut properties\n");
+        return ERROR_SUCCESS; /* fake it */
+    }
+
+    sprintfW(query, queryTemplate, shortcut);
+
+    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
+    if (rc != ERROR_SUCCESS)
+    {
+        msi_free(query);
+        return ERROR_SUCCESS;
+    }
+
+    res = CoInitialize( NULL );
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_AddShortcutProperties, &closure);
+    msiobj_release(&view->hdr);
+
+    if (SUCCEEDED(res)) CoUninitialize();
+
+    msi_free(query);
+    return rc;
+}
+
 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
 {
     MSIPACKAGE *package = param;
@@ -3985,6 +4102,9 @@ static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
         full_path = msi_get_target_folder( package, wkdir );
         if (full_path) IShellLinkW_SetWorkingDirectory( sl, full_path );
     }
+
+    AddShortcutProperties(package, MSI_RecordGetString(row, 1), sl);
+
     link_file = get_link_file(package, row);
 
     TRACE("Writing shortcut to %s\n", debugstr_w(link_file));
diff --git a/dlls/msi/tests/Makefile.in b/dlls/msi/tests/Makefile.in
index 66f8abb..6ea5f9a 100644
--- a/dlls/msi/tests/Makefile.in
+++ b/dlls/msi/tests/Makefile.in
@@ -1,5 +1,5 @@
 TESTDLL   = msi.dll
-IMPORTS   = cabinet msi shell32 ole32 oleaut32 user32 advapi32 version
+IMPORTS   = cabinet msi shell32 ole32 oleaut32 user32 advapi32 version uuid
 
 C_SRCS = \
 	action.c \
diff --git a/dlls/msi/tests/action.c b/dlls/msi/tests/action.c
index 60562f1..f1cafbd 100644
--- a/dlls/msi/tests/action.c
+++ b/dlls/msi/tests/action.c
@@ -23,6 +23,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#define COBJMACROS
+
 #include <windows.h>
 #include <msiquery.h>
 #include <msidefs.h>
@@ -30,9 +32,14 @@
 #include <fci.h>
 #include <srrestoreptapi.h>
 #include <wtypes.h>
+#include <shobjidl.h>
 #include <shellapi.h>
+#include <propsys.h>
 #include <winsvc.h>
 
+#include <initguid.h>
+#include <propkey.h>
+
 #include "wine/test.h"
 
 static UINT (WINAPI *pMsiQueryComponentStateA)
@@ -879,6 +886,12 @@ static const char crs_shortcut_dat[] =
     "Shortcut\tShortcut\n"
     "shortcut\tMSITESTDIR\tshortcut\tshortcut\t[MSITESTDIR]target.txt\t\t\t\t\t\t\t\n";
 
+static const char crs_shortcut_prop_dat[] =
+    "MsiShortcutProperty\tShortcut_\tPropertyKey\tPropVariantValue\n"
+    "s255\ts72\ts255\ts255\n"
+    "MsiShortcutProperty\tMsiShortcutProperty\n"
+    "shortcut_prop\tshortcut\tSystem.AppUserModel.ID\tWine.Test.Blub\n";
+
 static const char crs_install_exec_seq_dat[] =
     "Action\tCondition\tSequence\n"
     "s72\tS255\tI2\n"
@@ -1882,6 +1895,7 @@ static const msi_table crs_tables[] =
     ADD_TABLE(crs_feature_comp),
     ADD_TABLE(crs_file),
     ADD_TABLE(crs_shortcut),
+    ADD_TABLE(crs_shortcut_prop),
     ADD_TABLE(crs_install_exec_seq),
     ADD_TABLE(media),
     ADD_TABLE(property)
@@ -2671,13 +2685,18 @@ static BOOL file_exists(LPCSTR file)
     return GetFileAttributesA(file) != INVALID_FILE_ATTRIBUTES;
 }
 
+static void pf_fullname(LPCSTR file, CHAR buffer[])
+{
+    lstrcpyA(buffer, PROG_FILES_DIR);
+    lstrcatA(buffer, "\\");
+    lstrcatA(buffer, file);
+}
+
 static BOOL pf_exists(LPCSTR file)
 {
     CHAR path[MAX_PATH];
 
-    lstrcpyA(path, PROG_FILES_DIR);
-    lstrcatA(path, "\\");
-    lstrcatA(path, file);
+    pf_fullname(file, path);
 
     return file_exists(path);
 }
@@ -5825,6 +5844,56 @@ static void test_create_remove_shortcut(void)
     ok(pf_exists("msitest\\target.txt"), "file not created\n");
     ok(pf_exists("msitest\\shortcut.lnk"), "file not created\n");
 
+    /* analyze whether the shortcut properties have been written properly */
+    {
+        CHAR lnkpath[MAX_PATH];
+        WCHAR lnkpathW[MAX_PATH];
+        IShellLinkW *link = NULL;
+        IPersistFile *file = NULL;
+        IPropertyStore *store = NULL;
+        HRESULT hr;
+
+        CoInitialize(NULL);
+
+        pf_fullname("msitest\\shortcut.lnk", lnkpath);
+        MultiByteToWideChar(CP_ACP, 0, lnkpath, -1, lnkpathW, MAX_PATH);
+
+        hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                              &IID_IShellLinkW, (void**)&link);
+        ok(hr == S_OK, "CoCreateInstance() failed, hr=%x\n", hr);
+
+        hr = IShellLinkW_QueryInterface(link, &IID_IPersistFile, (void**)&file);
+        ok(hr == S_OK, "QueryInterface, hr=%x\n", hr);
+
+        hr = IPersistFile_Load(file, lnkpathW, STGM_READ);
+        ok(hr == S_OK, "IPersistFile::Load hr=%x\n", hr);
+
+        hr = IShellLinkW_QueryInterface(link, &IID_IPropertyStore, (void**)&store);
+        if (SUCCEEDED(hr))
+        {
+            PROPVARIANT v;
+            WCHAR expected[] = {'W','i','n','e','.','T','e','s','t','.','B','l','u','b',0};
+
+            hr = IPropertyStore_GetValue(store, &PKEY_AppUserModel_ID, &v);
+            ok(hr == S_OK, "IPropertyStore::GetValue hr=%x\n", hr);
+
+            ok(v.vt == VT_LPWSTR, "Unexpected variant type %d\n", v.vt);
+            ok(!lstrcmpW(expected, U(v).pwszVal), "Unexpected value %s\n", wine_dbgstr_w(U(v).pwszVal));
+
+            PropVariantClear(&v);
+            IPropertyStore_Release(store);
+        }
+        else
+        {
+            win_skip("IPropertyStore not implemented for shell links\n");
+        }
+
+        IPersistFile_Release(file);
+        IShellLinkW_Release(link);
+
+        CoUninitialize();
+    }
+
     r = MsiInstallProductA(msifile, "REMOVE=ALL");
     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
 
diff --git a/dlls/msi/tests/automation.c b/dlls/msi/tests/automation.c
index 016b1a2..84a3ae4 100644
--- a/dlls/msi/tests/automation.c
+++ b/dlls/msi/tests/automation.c
@@ -23,7 +23,6 @@
 
 #include <stdio.h>
 
-#include <initguid.h>
 #include <windows.h>
 #include <msiquery.h>
 #include <msidefs.h>
-- 
2.4.3




More information about the wine-devel mailing list