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>