From 4a02dc8ca500074a8d513b1a5e4f97e5a69efc7e Mon Sep 17 00:00:00 2001 From: Aleksey Bragin Date: Sun, 10 Feb 2008 13:22:36 +0000 Subject: [PATCH] Winesync to Wine-0.9.55. svn path=/trunk/; revision=32262 --- rostests/winetests/msi/automation.c | 2435 +++++++++++++++ rostests/winetests/msi/db.c | 4405 ++++++++++++++++++++++++++- rostests/winetests/msi/format.c | 1309 +++++++- rostests/winetests/msi/install.c | 3378 ++++++++++++++++++-- rostests/winetests/msi/msi.c | 1989 +++++++++++- rostests/winetests/msi/msi.rbuild | 44 +- rostests/winetests/msi/package.c | 3588 ++++++++++++++++++++-- rostests/winetests/msi/record.c | 27 +- rostests/winetests/msi/source.c | 725 +++++ rostests/winetests/msi/suminfo.c | 186 +- rostests/winetests/msi/testlist.c | 6 + 11 files changed, 17310 insertions(+), 782 deletions(-) create mode 100644 rostests/winetests/msi/automation.c create mode 100644 rostests/winetests/msi/source.c diff --git a/rostests/winetests/msi/automation.c b/rostests/winetests/msi/automation.c new file mode 100644 index 00000000000..90e4b1f62b0 --- /dev/null +++ b/rostests/winetests/msi/automation.c @@ -0,0 +1,2435 @@ +/* + * Copyright (C) 2007 Mike McCormack for CodeWeavers + * Copyright (C) 2007 Misha Koshelev + * + * A test program for Microsoft Installer OLE automation functionality. + * + * 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 + +#include + +#include +#include +#include +#include +#include + +#include "wine/test.h" + +static const char *msifile = "winetest.msi"; +static const WCHAR szMsifile[] = {'w','i','n','e','t','e','s','t','.','m','s','i',0}; +static const WCHAR szMSITEST[] = { 'M','S','I','T','E','S','T',0 }; +static const WCHAR szProductCode[] = { '{','F','1','C','3','A','F','5','0','-','8','B','5','6','-','4','A','6','9','-','A','0','0','C','-','0','0','7','7','3','F','E','4','2','F','3','0','}',0 }; +static const WCHAR szUpgradeCode[] = { '{','C','E','0','6','7','E','8','D','-','2','E','1','A','-','4','3','6','7','-','B','7','3','4','-','4','E','B','2','B','D','A','D','6','5','6','5','}',0 }; +static const WCHAR szProductInfoException[] = { 'P','r','o','d','u','c','t','I','n','f','o',',','P','r','o','d','u','c','t',',','A','t','t','r','i','b','u','t','e',0 }; +static const WCHAR WINE_INSTALLPROPERTY_PACKAGENAMEW[] = {'P','a','c','k','a','g','e','N','a','m','e',0}; +static const WCHAR WINE_INSTALLPROPERTY_PRODUCTNAMEW[] = {'P','r','o','d','u','c','t','N','a','m','e',0}; +static FILETIME systemtime; +static CHAR CURR_DIR[MAX_PATH]; +static EXCEPINFO excepinfo; + +/* + * OLE automation data + **/ +static const WCHAR szProgId[] = { 'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r','.','I','n','s','t','a','l','l','e','r',0 }; +static IDispatch *pInstaller; + +/* msi database data */ + +static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "Five\t{8CC92E9D-14B2-4CA4-B2AA-B11D02078087}\tNEWDIR\t2\t\tfive.txt\n" + "Four\t{FD37B4EA-7209-45C0-8917-535F35A2F080}\tCABOUTDIR\t2\t\tfour.txt\n" + "One\t{783B242E-E185-4A56-AF86-C09815EC053C}\tMSITESTDIR\t2\t\tone.txt\n" + "Three\t{010B6ADD-B27D-4EDD-9B3D-34C4F7D61684}\tCHANGEDDIR\t2\t\tthree.txt\n" + "Two\t{BF03D1A6-20DA-4A65-82F3-6CAC995915CE}\tFIRSTDIR\t2\t\ttwo.txt\n" + "dangler\t{6091DF25-EF96-45F1-B8E9-A9B1420C7A3C}\tTARGETDIR\t4\t\tregdata\n" + "component\t\tMSITESTDIR\t0\t1\tfile\n" + "service_comp\t\tMSITESTDIR\t0\t1\tservice_file"; + +static const CHAR directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n" + "s72\tS72\tl255\n" + "Directory\tDirectory\n" + "CABOUTDIR\tMSITESTDIR\tcabout\n" + "CHANGEDDIR\tMSITESTDIR\tchanged:second\n" + "FIRSTDIR\tMSITESTDIR\tfirst\n" + "MSITESTDIR\tProgramFilesFolder\tmsitest\n" + "NEWDIR\tCABOUTDIR\tnew\n" + "ProgramFilesFolder\tTARGETDIR\t.\n" + "TARGETDIR\t\tSourceDir"; + +static const CHAR feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n" + "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n" + "Feature\tFeature\n" + "Five\t\tFive\tThe Five Feature\t5\t3\tNEWDIR\t0\n" + "Four\t\tFour\tThe Four Feature\t4\t3\tCABOUTDIR\t0\n" + "One\t\tOne\tThe One Feature\t1\t3\tMSITESTDIR\t0\n" + "Three\tOne\tThree\tThe Three Feature\t3\t3\tCHANGEDDIR\t0\n" + "Two\tOne\tTwo\tThe Two Feature\t2\t3\tFIRSTDIR\t0\n" + "feature\t\t\t\t2\t1\tTARGETDIR\t0\n" + "service_feature\t\t\t\t2\t1\tTARGETDIR\t0"; + +static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n" + "s38\ts72\n" + "FeatureComponents\tFeature_\tComponent_\n" + "Five\tFive\n" + "Four\tFour\n" + "One\tOne\n" + "Three\tThree\n" + "Two\tTwo\n" + "feature\tcomponent\n" + "service_feature\tservice_comp\n"; + +static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" + "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" + "File\tFile\n" + "five.txt\tFive\tfive.txt\t1000\t\t\t0\t5\n" + "four.txt\tFour\tfour.txt\t1000\t\t\t0\t4\n" + "one.txt\tOne\tone.txt\t1000\t\t\t0\t1\n" + "three.txt\tThree\tthree.txt\t1000\t\t\t0\t3\n" + "two.txt\tTwo\ttwo.txt\t1000\t\t\t0\t2\n" + "file\tcomponent\tfilename\t100\t\t\t8192\t1\n" + "service_file\tservice_comp\tservice.exe\t100\t\t\t8192\t1"; + +static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n" + "s72\tS255\tI2\n" + "InstallExecuteSequence\tAction\n" + "AllocateRegistrySpace\tNOT Installed\t1550\n" + "CostFinalize\t\t1000\n" + "CostInitialize\t\t800\n" + "FileCost\t\t900\n" + "InstallFiles\t\t4000\n" + "InstallServices\t\t5000\n" + "RegisterProduct\t\t6100\n" + "PublishProduct\t\t6400\n" + "InstallFinalize\t\t6600\n" + "InstallInitialize\t\t1500\n" + "InstallValidate\t\t1400\n" + "LaunchConditions\t\t100\n" + "WriteRegistryValues\tSourceDir And SOURCEDIR\t5000"; + +static const CHAR media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t5\t\t\tDISK1\t\n"; + +static const CHAR property_dat[] = "Property\tValue\n" + "s72\tl0\n" + "Property\tProperty\n" + "DefaultUIFont\tDlgFont8\n" + "HASUIRUN\t0\n" + "INSTALLLEVEL\t3\n" + "InstallMode\tTypical\n" + "Manufacturer\tWine\n" + "PIDTemplate\t12345<###-%%%%%%%>@@@@@\n" + "ProductCode\t{F1C3AF50-8B56-4A69-A00C-00773FE42F30}\n" + "ProductID\tnone\n" + "ProductLanguage\t1033\n" + "ProductName\tMSITEST\n" + "ProductVersion\t1.1.1\n" + "PROMPTROLLBACKCOST\tP\n" + "Setup\tSetup\n" + "UpgradeCode\t{CE067E8D-2E1A-4367-B734-4EB2BDAD6565}"; + +static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n" + "s72\ti2\tl255\tL255\tL0\ts72\n" + "Registry\tRegistry\n" + "Apples\t2\tSOFTWARE\\Wine\\msitest\tName\timaname\tOne\n" + "Oranges\t2\tSOFTWARE\\Wine\\msitest\tnumber\t#314\tTwo\n" + "regdata\t2\tSOFTWARE\\Wine\\msitest\tblah\tbad\tdangler\n" + "OrderTest\t2\tSOFTWARE\\Wine\\msitest\tOrderTestName\tOrderTestValue\tcomponent"; + +static const CHAR service_install_dat[] = "ServiceInstall\tName\tDisplayName\tServiceType\tStartType\tErrorControl\t" + "LoadOrderGroup\tDependencies\tStartName\tPassword\tArguments\tComponent_\tDescription\n" + "s72\ts255\tL255\ti4\ti4\ti4\tS255\tS255\tS255\tS255\tS255\ts72\tL255\n" + "ServiceInstall\tServiceInstall\n" + "TestService\tTestService\tTestService\t2\t3\t0\t\t\tTestService\t\t\tservice_comp\t\t"; + +static const CHAR service_control_dat[] = "ServiceControl\tName\tEvent\tArguments\tWait\tComponent_\n" + "s72\tl255\ti2\tL255\tI2\ts72\n" + "ServiceControl\tServiceControl\n" + "ServiceControl\tTestService\t8\t\t0\tservice_comp"; + +typedef struct _msi_table +{ + const CHAR *filename; + const CHAR *data; + int size; +} msi_table; + +#define ADD_TABLE(x) {#x".idt", x##_dat, sizeof(x##_dat)} + +static const msi_table tables[] = +{ + ADD_TABLE(component), + ADD_TABLE(directory), + ADD_TABLE(feature), + ADD_TABLE(feature_comp), + ADD_TABLE(file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(media), + ADD_TABLE(property), + ADD_TABLE(registry), + ADD_TABLE(service_install), + ADD_TABLE(service_control) +}; + +typedef struct _msi_summary_info +{ + UINT property; + UINT datatype; + INT iValue; + FILETIME *pftValue; + const CHAR *szValue; +} msi_summary_info; + +#define ADD_INFO_I2(property, iValue) {property, VT_I2, iValue, NULL, NULL} +#define ADD_INFO_I4(property, iValue) {property, VT_I4, iValue, NULL, NULL} +#define ADD_INFO_LPSTR(property, szValue) {property, VT_LPSTR, 0, NULL, szValue} +#define ADD_INFO_FILETIME(property, pftValue) {property, VT_FILETIME, 0, pftValue, NULL} + +static const msi_summary_info summary_info[] = +{ + ADD_INFO_LPSTR(PID_TEMPLATE, ";1033"), + ADD_INFO_LPSTR(PID_REVNUMBER, "{004757CA-5092-49c2-AD20-28E1CE0DF5F2}"), + ADD_INFO_I4(PID_PAGECOUNT, 100), + ADD_INFO_I4(PID_WORDCOUNT, 0), + ADD_INFO_FILETIME(PID_CREATE_DTM, &systemtime), + ADD_INFO_FILETIME(PID_LASTPRINTED, &systemtime) +}; + +/* + * Database Helpers + */ + +static void write_file(const CHAR *filename, const char *data, int data_size) +{ + DWORD size; + + HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + WriteFile(hf, data, data_size, &size, NULL); + CloseHandle(hf); +} + +static void write_msi_summary_info(MSIHANDLE db, const msi_summary_info *info, int num_info) +{ + MSIHANDLE summary; + UINT r; + int j; + + r = MsiGetSummaryInformationA(db, NULL, num_info, &summary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + /* import summary information into the stream */ + for (j = 0; j < num_info; j++) + { + const msi_summary_info *entry = &info[j]; + + r = MsiSummaryInfoSetPropertyA(summary, entry->property, entry->datatype, + entry->iValue, entry->pftValue, entry->szValue); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + } + + /* write the summary changes back to the stream */ + r = MsiSummaryInfoPersist(summary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + MsiCloseHandle(summary); +} + +static void create_database(const CHAR *name, const msi_table *tables, int num_tables, + const msi_summary_info *info, int num_info) +{ + MSIHANDLE db; + UINT r; + int j; + + r = MsiOpenDatabaseA(name, MSIDBOPEN_CREATE, &db); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + /* import the tables into the database */ + for (j = 0; j < num_tables; j++) + { + const msi_table *table = &tables[j]; + + write_file(table->filename, table->data, (table->size - 1) * sizeof(char)); + + r = MsiDatabaseImportA(db, CURR_DIR, table->filename); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + DeleteFileA(table->filename); + } + + write_msi_summary_info(db, info, num_info); + + r = MsiDatabaseCommit(db); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + MsiCloseHandle(db); +} + +/* + * Installation helpers + */ + +static char PROG_FILES_DIR[MAX_PATH]; + +static BOOL get_program_files_dir(LPSTR buf) +{ + HKEY hkey; + DWORD type = REG_EXPAND_SZ, size; + + if (RegOpenKey(HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows\\CurrentVersion", &hkey)) + return FALSE; + + size = MAX_PATH; + if (RegQueryValueEx(hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size)) + return FALSE; + + RegCloseKey(hkey); + return TRUE; +} + +static void create_file(const CHAR *name, DWORD size) +{ + HANDLE file; + DWORD written, left; + + file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name); + WriteFile(file, name, strlen(name), &written, NULL); + WriteFile(file, "\n", strlen("\n"), &written, NULL); + + left = size - lstrlen(name) - 1; + + SetFilePointer(file, left, NULL, FILE_CURRENT); + SetEndOfFile(file); + + CloseHandle(file); +} + +static void create_test_files(void) +{ + CreateDirectoryA("msitest", NULL); + create_file("msitest\\one.txt", 100); + CreateDirectoryA("msitest\\first", NULL); + create_file("msitest\\first\\two.txt", 100); + CreateDirectoryA("msitest\\second", NULL); + create_file("msitest\\second\\three.txt", 100); + CreateDirectoryA("msitest\\cabout",NULL); + create_file("msitest\\cabout\\four.txt", 100); + CreateDirectoryA("msitest\\cabout\\new",NULL); + create_file("msitest\\cabout\\new\\five.txt", 100); + create_file("msitest\\filename", 100); + create_file("msitest\\service.exe", 100); +} + +static BOOL delete_pf(const CHAR *rel_path, BOOL is_file) +{ + CHAR path[MAX_PATH]; + + lstrcpyA(path, PROG_FILES_DIR); + lstrcatA(path, "\\"); + lstrcatA(path, rel_path); + + if (is_file) + return DeleteFileA(path); + else + return RemoveDirectoryA(path); +} + +static void delete_test_files(void) +{ + DeleteFileA(msifile); + DeleteFileA("msitest\\cabout\\new\\five.txt"); + DeleteFileA("msitest\\cabout\\four.txt"); + DeleteFileA("msitest\\second\\three.txt"); + DeleteFileA("msitest\\first\\two.txt"); + DeleteFileA("msitest\\one.txt"); + DeleteFileA("msitest\\service.exe"); + DeleteFileA("msitest\\filename"); + RemoveDirectoryA("msitest\\cabout\\new"); + RemoveDirectoryA("msitest\\cabout"); + RemoveDirectoryA("msitest\\second"); + RemoveDirectoryA("msitest\\first"); + RemoveDirectoryA("msitest"); +} + +static void check_service_is_installed(void) +{ + SC_HANDLE scm, service; + BOOL res; + + scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + ok(scm != NULL, "Failed to open the SC Manager\n"); + + service = OpenService(scm, "TestService", SC_MANAGER_ALL_ACCESS); + ok(service != NULL, "Failed to open TestService\n"); + + res = DeleteService(service); + ok(res, "Failed to delete TestService\n"); + + CloseServiceHandle(service); + CloseServiceHandle(scm); +} + +/* + * Automation helpers and tests + */ + +/* ok-like statement which takes two unicode strings or one unicode and one ANSI string as arguments */ +static CHAR string1[MAX_PATH], string2[MAX_PATH]; + +#define ok_w2(format, szString1, szString2) \ +\ + if (lstrcmpW(szString1, szString2) != 0) \ + { \ + WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \ + WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \ + ok(0, format, string1, string2); \ + } + +#define ok_aw(format, aString, wString) \ +\ + WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \ + if (lstrcmpA(string1, aString) != 0) \ + ok(0, format, string1, aString); \ + +#define ok_awplus(format, extra, aString, wString) \ +\ + WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \ + if (lstrcmpA(string1, aString) != 0) \ + ok(0, format, extra, string1, aString); \ + +/* exception checker */ +static WCHAR szSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0}; + +#define ok_exception(hr, szDescription) \ + if (hr == DISP_E_EXCEPTION) \ + { \ + /* Compare wtype, source, and destination */ \ + ok(excepinfo.wCode == 1000, "Exception info was %d, expected 1000\n", excepinfo.wCode); \ +\ + ok(excepinfo.bstrSource != NULL, "Exception source was NULL\n"); \ + if (excepinfo.bstrSource) \ + ok_w2("Exception source was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrSource, szSource); \ +\ + ok(excepinfo.bstrDescription != NULL, "Exception description was NULL\n"); \ + if (excepinfo.bstrDescription) \ + ok_w2("Exception description was \"%s\" but expected to be \"%s\"\n", excepinfo.bstrDescription, szDescription); \ +\ + SysFreeString(excepinfo.bstrSource); \ + SysFreeString(excepinfo.bstrDescription); \ + SysFreeString(excepinfo.bstrHelpFile); \ + } + +static DISPID get_dispid( IDispatch *disp, const char *name ) +{ + LPOLESTR str; + UINT len; + DISPID id = -1; + HRESULT r; + + len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0 ); + str = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) ); + if (str) + { + len = MultiByteToWideChar(CP_ACP, 0, name, -1, str, len ); + r = IDispatch_GetIDsOfNames( disp, &IID_NULL, &str, 1, 0, &id ); + HeapFree(GetProcessHeap(), 0, str); + if (r != S_OK) + return -1; + } + + return id; +} + +static void test_dispid(void) +{ + ok( get_dispid( pInstaller, "CreateRecord" ) == 1, "dispid wrong\n"); + ok( get_dispid( pInstaller, "OpenPackage" ) == 2, "dispid wrong\n"); + todo_wine ok( get_dispid( pInstaller, "OpenProduct" ) == 3, "dispid wrong\n"); + ok( get_dispid( pInstaller, "OpenDatabase" ) == 4, "dispid wrong\n"); + todo_wine { + ok( get_dispid( pInstaller, "SummaryInformation" ) == 5, "dispid wrong\n"); + ok( get_dispid( pInstaller, "UILevel" ) == 6, "dispid wrong\n"); + ok( get_dispid( pInstaller, "EnableLog" ) == 7, "dispid wrong\n"); + } + ok( get_dispid( pInstaller, "InstallProduct" ) == 8, "dispid wrong\n"); + ok( get_dispid( pInstaller, "Version" ) == 9, "dispid wrong\n"); + todo_wine { + ok( get_dispid( pInstaller, "LastErrorRecord" ) == 10, "dispid wrong\n"); + } + ok( get_dispid( pInstaller, "RegistryValue" ) == 11, "dispid wrong\n"); + todo_wine { + ok( get_dispid( pInstaller, "Environment" ) == 12, "dispid wrong\n"); + ok( get_dispid( pInstaller, "FileAttributes" ) == 13, "dispid wrong\n"); + + ok( get_dispid( pInstaller, "FileSize" ) == 15, "dispid wrong\n"); + ok( get_dispid( pInstaller, "FileVersion" ) == 16, "dispid wrong\n"); + } + ok( get_dispid( pInstaller, "ProductState" ) == 17, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ProductInfo" ) == 18, "dispid wrong\n"); + todo_wine { + ok( get_dispid( pInstaller, "ConfigureProduct" ) == 19, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ReinstallProduct" ) == 20 , "dispid wrong\n"); + ok( get_dispid( pInstaller, "CollectUserInfo" ) == 21, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ApplyPatch" ) == 22, "dispid wrong\n"); + ok( get_dispid( pInstaller, "FeatureParent" ) == 23, "dispid wrong\n"); + ok( get_dispid( pInstaller, "FeatureState" ) == 24, "dispid wrong\n"); + ok( get_dispid( pInstaller, "UseFeature" ) == 25, "dispid wrong\n"); + ok( get_dispid( pInstaller, "FeatureUsageCount" ) == 26, "dispid wrong\n"); + ok( get_dispid( pInstaller, "FeatureUsageDate" ) == 27, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ConfigureFeature" ) == 28, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ReinstallFeature" ) == 29, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ProvideComponent" ) == 30, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ComponentPath" ) == 31, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ProvideQualifiedComponent" ) == 32, "dispid wrong\n"); + ok( get_dispid( pInstaller, "QualifierDescription" ) == 33, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ComponentQualifiers" ) == 34, "dispid wrong\n"); + } + ok( get_dispid( pInstaller, "Products" ) == 35, "dispid wrong\n"); + todo_wine { + ok( get_dispid( pInstaller, "Features" ) == 36, "dispid wrong\n"); + ok( get_dispid( pInstaller, "Components" ) == 37, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ComponentClients" ) == 38, "dispid wrong\n"); + ok( get_dispid( pInstaller, "Patches" ) == 39, "dispid wrong\n"); + } + ok( get_dispid( pInstaller, "RelatedProducts" ) == 40, "dispid wrong\n"); + todo_wine { + ok( get_dispid( pInstaller, "PatchInfo" ) == 41, "dispid wrong\n"); + ok( get_dispid( pInstaller, "PatchTransforms" ) == 42, "dispid wrong\n"); + ok( get_dispid( pInstaller, "AddSource" ) == 43, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ClearSourceList" ) == 44, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ForceSourceListResolution" ) == 45, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ShortcutTarget" ) == 46, "dispid wrong\n"); + ok( get_dispid( pInstaller, "FileHash" ) == 47, "dispid wrong\n"); + ok( get_dispid( pInstaller, "FileSignatureInfo" ) == 48, "dispid wrong\n"); + ok( get_dispid( pInstaller, "RemovePatches" ) == 49, "dispid wrong\n"); + + ok( get_dispid( pInstaller, "ApplyMultiplePatches" ) == 51, "dispid wrong\n"); + ok( get_dispid( pInstaller, "ProductsEx" ) == 52, "dispid wrong\n"); + + ok( get_dispid( pInstaller, "PatchesEx" ) == 55, "dispid wrong\n"); + + ok( get_dispid( pInstaller, "ExtractPatchXMLData" ) == 57, "dispid wrong\n"); + } + + /* MSDN claims the following functions exist but IDispatch->GetIDsOfNames disagrees */ + if (0) + { + get_dispid( pInstaller, "ProductElevated" ); + get_dispid( pInstaller, "ProductInfoFromScript" ); + get_dispid( pInstaller, "ProvideAssembly" ); + get_dispid( pInstaller, "CreateAdvertiseScript" ); + get_dispid( pInstaller, "AdvertiseProduct" ); + get_dispid( pInstaller, "PatchFiles" ); + } +} + +/* Test basic IDispatch functions */ +static void test_dispatch(void) +{ + static WCHAR szOpenPackage[] = { 'O','p','e','n','P','a','c','k','a','g','e',0 }; + static WCHAR szOpenPackageException[] = {'O','p','e','n','P','a','c','k','a','g','e',',','P','a','c','k','a','g','e','P','a','t','h',',','O','p','t','i','o','n','s',0}; + static WCHAR szProductState[] = { 'P','r','o','d','u','c','t','S','t','a','t','e',0 }; + HRESULT hr; + DISPID dispid; + OLECHAR *name; + VARIANT varresult; + VARIANTARG vararg[2]; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + + /* Test getting ID of a function name that does not exist */ + name = (WCHAR *)szMsifile; + hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid); + ok(hr == DISP_E_UNKNOWNNAME, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr); + + /* Test invoking this function */ + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr); + + /* Test getting ID of a function name that does exist */ + name = (WCHAR *)szOpenPackage; + hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid); + ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr); + + /* Test invoking this function (without parameters passed) */ + if (0) /* All of these crash MSI on Windows XP */ + { + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, NULL, NULL); + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, NULL, &excepinfo, NULL); + VariantInit(&varresult); + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, NULL, &varresult, &excepinfo, NULL); + } + + /* Try with NULL params */ + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL); + todo_wine ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr); + + /* Try one empty parameter */ + dispparams.rgvarg = vararg; + dispparams.cArgs = 1; + VariantInit(&vararg[0]); + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL); + todo_wine ok(hr == DISP_E_TYPEMISMATCH, "IDispatch::Invoke returned 0x%08x\n", hr); + + /* Try one parameter, function requires two */ + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szMsifile); + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL); + VariantClear(&vararg[0]); + + ok(hr == DISP_E_EXCEPTION, "IDispatch::Invoke returned 0x%08x\n", hr); + ok_exception(hr, szOpenPackageException); + + /* Test invoking a method as a DISPATCH_PROPERTYGET or DISPATCH_PROPERTYPUT */ + VariantInit(&vararg[0]); + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr); + + VariantInit(&vararg[0]); + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr); + + /* Test invoking a read-only property as DISPATCH_PROPERTYPUT or as a DISPATCH_METHOD */ + name = (WCHAR *)szProductState; + hr = IDispatch_GetIDsOfNames(pInstaller, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid); + ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr); + + dispparams.rgvarg = NULL; + dispparams.cArgs = 0; + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr); + + dispparams.rgvarg = NULL; + dispparams.cArgs = 0; + hr = IDispatch_Invoke(pInstaller, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "IDispatch::Invoke returned 0x%08x\n", hr); +} + +/* invocation helper function */ +static int _invoke_todo_vtResult = 0; + +static HRESULT invoke(IDispatch *pDispatch, LPCSTR szName, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, VARTYPE vtResult) +{ + OLECHAR *name = NULL; + DISPID dispid; + HRESULT hr; + UINT i; + UINT len; + + memset(pVarResult, 0, sizeof(VARIANT)); + VariantInit(pVarResult); + + len = MultiByteToWideChar(CP_ACP, 0, szName, -1, NULL, 0 ); + name = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) ); + if (!name) return E_FAIL; + len = MultiByteToWideChar(CP_ACP, 0, szName, -1, name, len ); + hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid); + HeapFree(GetProcessHeap(), 0, name); + ok(hr == S_OK, "IDispatch::GetIDsOfNames returned 0x%08x\n", hr); + if (!hr == S_OK) return hr; + + memset(&excepinfo, 0, sizeof(excepinfo)); + hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_NEUTRAL, wFlags, pDispParams, pVarResult, &excepinfo, NULL); + + if (hr == S_OK) + { + if (_invoke_todo_vtResult) todo_wine + ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult); + else + ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult); + if (vtResult != VT_EMPTY) + { + hr = VariantChangeTypeEx(pVarResult, pVarResult, LOCALE_NEUTRAL, 0, vtResult); + ok(hr == S_OK, "VariantChangeTypeEx returned 0x%08x\n", hr); + } + } + + for (i=0; icArgs; i++) + VariantClear(&pDispParams->rgvarg[i]); + + return hr; +} + +/* Object_Property helper functions */ + +static HRESULT Installer_CreateRecord(int count, IDispatch **pRecord) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = count; + + hr = invoke(pInstaller, "CreateRecord", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH); + *pRecord = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT Installer_RegistryValue(HKEY hkey, LPCWSTR szKey, VARIANT vValue, VARIANT *pVarResult, VARTYPE vtExpect) +{ + VARIANTARG vararg[3]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + + VariantInit(&vararg[2]); + V_VT(&vararg[2]) = VT_I4; + V_I4(&vararg[2]) = (int)hkey; + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_BSTR; + V_BSTR(&vararg[1]) = SysAllocString(szKey); + VariantInit(&vararg[0]); + VariantCopy(&vararg[0], &vValue); + VariantClear(&vValue); + + return invoke(pInstaller, "RegistryValue", DISPATCH_METHOD, &dispparams, pVarResult, vtExpect); +} + +static HRESULT Installer_RegistryValueE(HKEY hkey, LPCWSTR szKey, BOOL *pBool) +{ + VARIANT varresult; + VARIANTARG vararg; + HRESULT hr; + + VariantInit(&vararg); + V_VT(&vararg) = VT_EMPTY; + hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BOOL); + *pBool = V_BOOL(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Installer_RegistryValueW(HKEY hkey, LPCWSTR szKey, LPCWSTR szValue, LPWSTR szString) +{ + VARIANT varresult; + VARIANTARG vararg; + HRESULT hr; + + VariantInit(&vararg); + V_VT(&vararg) = VT_BSTR; + V_BSTR(&vararg) = SysAllocString(szValue); + + hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, VT_BSTR); + if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult)); + VariantClear(&varresult); + return hr; +} + +static HRESULT Installer_RegistryValueI(HKEY hkey, LPCWSTR szKey, int iValue, LPWSTR szString, VARTYPE vtResult) +{ + VARIANT varresult; + VARIANTARG vararg; + HRESULT hr; + + VariantInit(&vararg); + V_VT(&vararg) = VT_I4; + V_I4(&vararg) = iValue; + + hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, vtResult); + if (vtResult == VT_BSTR) lstrcpyW(szString, V_BSTR(&varresult)); + VariantClear(&varresult); + return hr; +} + +static HRESULT Installer_OpenPackage(LPCWSTR szPackagePath, int options, IDispatch **pSession) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_BSTR; + V_BSTR(&vararg[1]) = SysAllocString(szPackagePath); + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = options; + + hr = invoke(pInstaller, "OpenPackage", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH); + *pSession = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT Installer_OpenDatabase(LPCWSTR szDatabasePath, int openmode, IDispatch **pDatabase) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_BSTR; + V_BSTR(&vararg[1]) = SysAllocString(szDatabasePath); + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = openmode; + + hr = invoke(pInstaller, "OpenDatabase", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH); + *pDatabase = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT Installer_InstallProduct(LPCWSTR szPackagePath, LPCWSTR szPropertyValues) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_BSTR; + V_BSTR(&vararg[1]) = SysAllocString(szPackagePath); + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szPropertyValues); + + return invoke(pInstaller, "InstallProduct", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT Installer_ProductState(LPCWSTR szProduct, int *pInstallState) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szProduct); + + hr = invoke(pInstaller, "ProductState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4); + *pInstallState = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Installer_ProductInfo(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szString) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_BSTR; + V_BSTR(&vararg[1]) = SysAllocString(szProduct); + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szAttribute); + + hr = invoke(pInstaller, "ProductInfo", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR); + if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult)); + VariantClear(&varresult); + return hr; +} + +static HRESULT Installer_Products(IDispatch **pStringList) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr; + + hr = invoke(pInstaller, "Products", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH); + *pStringList = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT Installer_RelatedProducts(LPCWSTR szProduct, IDispatch **pStringList) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szProduct); + + hr = invoke(pInstaller, "RelatedProducts", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH); + *pStringList = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT Installer_VersionGet(LPWSTR szVersion) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr; + + hr = invoke(pInstaller, "Version", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR); + if (V_BSTR(&varresult)) lstrcpyW(szVersion, V_BSTR(&varresult)); + VariantClear(&varresult); + return hr; +} + +static HRESULT Session_Installer(IDispatch *pSession, IDispatch **pInst) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr; + + hr = invoke(pSession, "Installer", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH); + *pInst = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT Session_PropertyGet(IDispatch *pSession, LPCWSTR szName, LPWSTR szReturn) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szName); + + hr = invoke(pSession, "Property", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR); + if (V_BSTR(&varresult)) lstrcpyW(szReturn, V_BSTR(&varresult)); + VariantClear(&varresult); + return hr; +} + +static HRESULT Session_PropertyPut(IDispatch *pSession, LPCWSTR szName, LPCWSTR szValue) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPID dispid = DISPID_PROPERTYPUT; + DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1}; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_BSTR; + V_BSTR(&vararg[1]) = SysAllocString(szName); + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szValue); + + return invoke(pSession, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT Session_LanguageGet(IDispatch *pSession, UINT *pLangId) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr; + + hr = invoke(pSession, "Language", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4); + *pLangId = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Session_ModeGet(IDispatch *pSession, int iFlag, BOOL *pMode) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = iFlag; + + hr = invoke(pSession, "Mode", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BOOL); + *pMode = V_BOOL(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Session_ModePut(IDispatch *pSession, int iFlag, BOOL bMode) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPID dispid = DISPID_PROPERTYPUT; + DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1}; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_I4; + V_I4(&vararg[1]) = iFlag; + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BOOL; + V_BOOL(&vararg[0]) = bMode; + + return invoke(pSession, "Mode", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT Session_Database(IDispatch *pSession, IDispatch **pDatabase) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr; + + hr = invoke(pSession, "Database", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH); + *pDatabase = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT Session_DoAction(IDispatch *pSession, LPCWSTR szAction, int *iReturn) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szAction); + + hr = invoke(pSession, "DoAction", DISPATCH_METHOD, &dispparams, &varresult, VT_I4); + *iReturn = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Session_EvaluateCondition(IDispatch *pSession, LPCWSTR szCondition, int *iReturn) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szCondition); + + hr = invoke(pSession, "EvaluateCondition", DISPATCH_METHOD, &dispparams, &varresult, VT_I4); + *iReturn = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Session_SetInstallLevel(IDispatch *pSession, long iInstallLevel) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = iInstallLevel; + + return invoke(pSession, "SetInstallLevel", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT Session_FeatureCurrentState(IDispatch *pSession, LPCWSTR szName, int *pState) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szName); + + hr = invoke(pSession, "FeatureCurrentState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4); + *pState = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Session_FeatureRequestStateGet(IDispatch *pSession, LPCWSTR szName, int *pState) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szName); + + hr = invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4); + *pState = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Session_FeatureRequestStatePut(IDispatch *pSession, LPCWSTR szName, int iState) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPID dispid = DISPID_PROPERTYPUT; + DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1}; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_BSTR; + V_BSTR(&vararg[1]) = SysAllocString(szName); + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = iState; + + return invoke(pSession, "FeatureRequestState", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT Database_OpenView(IDispatch *pDatabase, LPCWSTR szSql, IDispatch **pView) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szSql); + + hr = invoke(pDatabase, "OpenView", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH); + *pView = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT Database_SummaryInformation(IDispatch *pDatabase, int iUpdateCount, IDispatch **pSummaryInfo) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = iUpdateCount; + + hr = invoke(pDatabase, "SummaryInformation", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH); + *pSummaryInfo = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT View_Execute(IDispatch *pView, IDispatch *pRecord) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_DISPATCH; + V_DISPATCH(&vararg[0]) = pRecord; + + return invoke(pView, "Execute", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT View_Fetch(IDispatch *pView, IDispatch **ppRecord) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr = invoke(pView, "Fetch", DISPATCH_METHOD, &dispparams, &varresult, VT_DISPATCH); + *ppRecord = V_DISPATCH(&varresult); + return hr; +} + +static HRESULT View_Modify(IDispatch *pView, int iMode, IDispatch *pRecord) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_I4; + V_I4(&vararg[1]) = iMode; + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_DISPATCH; + V_DISPATCH(&vararg[0]) = pRecord; + if (pRecord) + IDispatch_AddRef(pRecord); /* VariantClear in invoke will call IDispatch_Release */ + + return invoke(pView, "Modify", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT View_Close(IDispatch *pView) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + return invoke(pView, "Close", DISPATCH_METHOD, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT Record_FieldCountGet(IDispatch *pRecord, int *pFieldCount) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr = invoke(pRecord, "FieldCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4); + *pFieldCount = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Record_StringDataGet(IDispatch *pRecord, int iField, LPWSTR szString) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = iField; + + hr = invoke(pRecord, "StringData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR); + if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult)); + VariantClear(&varresult); + return hr; +} + +static HRESULT Record_StringDataPut(IDispatch *pRecord, int iField, LPCWSTR szString) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPID dispid = DISPID_PROPERTYPUT; + DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1}; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_I4; + V_I4(&vararg[1]) = iField; + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = SysAllocString(szString); + + return invoke(pRecord, "StringData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT Record_IntegerDataGet(IDispatch *pRecord, int iField, int *pValue) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = iField; + + hr = invoke(pRecord, "IntegerData", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4); + *pValue = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT Record_IntegerDataPut(IDispatch *pRecord, int iField, int iValue) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPID dispid = DISPID_PROPERTYPUT; + DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1}; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_I4; + V_I4(&vararg[1]) = iField; + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = iValue; + + return invoke(pRecord, "IntegerData", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT StringList__NewEnum(IDispatch *pList, IUnknown **ppEnumVARIANT) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr = invoke(pList, "_NewEnum", DISPATCH_METHOD, &dispparams, &varresult, VT_UNKNOWN); + *ppEnumVARIANT = V_UNKNOWN(&varresult); + return hr; +} + +static HRESULT StringList_Item(IDispatch *pStringList, int iIndex, LPWSTR szString) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = iIndex; + + hr = invoke(pStringList, "Item", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR); + if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult)); + VariantClear(&varresult); + return hr; +} + +static HRESULT StringList_Count(IDispatch *pStringList, int *pCount) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr = invoke(pStringList, "Count", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4); + *pCount = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +static HRESULT SummaryInfo_PropertyGet(IDispatch *pSummaryInfo, int pid, VARIANT *pVarResult, VARTYPE vtExpect) +{ + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = pid; + return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYGET, &dispparams, pVarResult, vtExpect); +} + +static HRESULT SummaryInfo_PropertyPut(IDispatch *pSummaryInfo, int pid, VARIANT *pVariant) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPID dispid = DISPID_PROPERTYPUT; + DISPPARAMS dispparams = {vararg, &dispid, sizeof(vararg)/sizeof(VARIANTARG), 1}; + + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_I4; + V_I4(&vararg[1]) = pid; + VariantInit(&vararg[0]); + VariantCopyInd(vararg, pVariant); + + return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYPUT, &dispparams, &varresult, VT_EMPTY); +} + +static HRESULT SummaryInfo_PropertyCountGet(IDispatch *pSummaryInfo, int *pCount) +{ + VARIANT varresult; + DISPPARAMS dispparams = {NULL, NULL, 0, 0}; + HRESULT hr; + + hr = invoke(pSummaryInfo, "PropertyCount", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4); + *pCount = V_I4(&varresult); + VariantClear(&varresult); + return hr; +} + +/* Test the various objects */ + +#define TEST_SUMMARYINFO_PROPERTIES_MODIFIED 4 + +static void test_SummaryInfo(IDispatch *pSummaryInfo, const msi_summary_info *info, int num_info, BOOL readonly) +{ + static const WCHAR szPropertyException[] = { 'P','r','o','p','e','r','t','y',',','P','i','d',0 }; + static const WCHAR szTitle[] = { 'T','i','t','l','e',0 }; + VARIANT varresult, var; + SYSTEMTIME st; + HRESULT hr; + int j; + + /* SummaryInfo::PropertyCount */ + hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j); + ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr); + ok(j == num_info, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info); + + /* SummaryInfo::Property, get for properties we have set */ + for (j = 0; j < num_info; j++) + { + const msi_summary_info *entry = &info[j]; + + int vt = entry->datatype; + if (vt == VT_LPSTR) vt = VT_BSTR; + else if (vt == VT_FILETIME) vt = VT_DATE; + else if (vt == VT_I2) vt = VT_I4; + + hr = SummaryInfo_PropertyGet(pSummaryInfo, entry->property, &varresult, vt); + ok(hr == S_OK, "SummaryInfo_Property (pid %d) failed, hresult 0x%08x\n", entry->property, hr); + if (V_VT(&varresult) != vt) + skip("Skipping property tests due to type mismatch\n"); + else if (vt == VT_I4) + ok(V_I4(&varresult) == entry->iValue, "SummaryInfo_Property (pid %d) I4 result expected to be %d, but was %d\n", + entry->property, entry->iValue, V_I4(&varresult)); + else if (vt == VT_DATE) + { + FILETIME ft; + DATE d; + + FileTimeToLocalFileTime(entry->pftValue, &ft); + FileTimeToSystemTime(&ft, &st); + SystemTimeToVariantTime(&st, &d); + ok(d == V_DATE(&varresult), "SummaryInfo_Property (pid %d) DATE result expected to be %lf, but was %lf\n", entry->property, d, V_DATE(&varresult)); + } + else if (vt == VT_BSTR) + { + ok_awplus("SummaryInfo_Property (pid %d) BSTR result expected to be %s, but was %s\n", entry->property, entry->szValue, V_BSTR(&varresult)); + } + else + skip("SummaryInfo_Property (pid %d) unhandled result type %d\n", entry->property, vt); + + VariantClear(&varresult); + } + + /* SummaryInfo::Property, get; invalid arguments */ + + /* Invalid pids */ + hr = SummaryInfo_PropertyGet(pSummaryInfo, -1, &varresult, VT_EMPTY); + ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + ok_exception(hr, szPropertyException); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, 1000, &varresult, VT_EMPTY); + ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + ok_exception(hr, szPropertyException); + + /* Unsupported pids */ + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_DICTIONARY, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_THUMBNAIL, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + /* Pids we have not set, one for each type */ + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_EDITTIME, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + if (!readonly) + { + /* SummaryInfo::Property, put; one for each type */ + + /* VT_I2 */ + VariantInit(&var); + V_VT(&var) = VT_I2; + V_I2(&var) = 1; + hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CODEPAGE, &var); + ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_I4 /* NOT VT_I2 */); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + ok(V_I2(&var) == V_I2(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I2(&var), V_I2(&varresult)); + VariantClear(&varresult); + VariantClear(&var); + + /* VT_BSTR */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(szTitle); + hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_TITLE, &var); + ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, V_VT(&var)); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + ok_w2("SummaryInfo_PropertyGet expected %s, but returned %s\n", V_BSTR(&var), V_BSTR(&varresult)); + VariantClear(&varresult); + VariantClear(&var); + + /* VT_DATE */ + V_VT(&var) = VT_DATE; + FileTimeToSystemTime(&systemtime, &st); + SystemTimeToVariantTime(&st, &V_DATE(&var)); + hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_LASTSAVE_DTM, &var); + ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_LASTSAVE_DTM, &varresult, V_VT(&var)); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + /* FIXME: Off by one second */ + todo_wine ok(V_DATE(&var) == V_DATE(&varresult), "SummaryInfo_PropertyGet expected %lf, but returned %lf\n", V_DATE(&var), V_DATE(&varresult)); + VariantClear(&varresult); + VariantClear(&var); + + /* VT_I4 */ + V_VT(&var) = VT_I4; + V_I4(&var) = 1000; + hr = SummaryInfo_PropertyPut(pSummaryInfo, PID_CHARCOUNT, &var); + ok(hr == S_OK, "SummaryInfo_PropertyPut failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, V_VT(&var)); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + ok(V_I4(&var) == V_I4(&varresult), "SummaryInfo_PropertyGet expected %d, but returned %d\n", V_I4(&var), V_I4(&varresult)); + VariantClear(&varresult); + VariantClear(&var); + + /* SummaryInfo::PropertyCount */ + hr = SummaryInfo_PropertyCountGet(pSummaryInfo, &j); + ok(hr == S_OK, "SummaryInfo_PropertyCount failed, hresult 0x%08x\n", hr); + ok(j == num_info+4, "SummaryInfo_PropertyCount returned %d, expected %d\n", j, num_info); + } +} + +static void test_Database(IDispatch *pDatabase, BOOL readonly) +{ + static WCHAR szSql[] = { 'S','E','L','E','C','T',' ','`','F','e','a','t','u','r','e','`',' ','F','R','O','M',' ','`','F','e','a','t','u','r','e','`',' ','W','H','E','R','E',' ','`','F','e','a','t','u','r','e','_','P','a','r','e','n','t','`','=','\'','O','n','e','\'',0 }; + static WCHAR szThree[] = { 'T','h','r','e','e',0 }; + static WCHAR szTwo[] = { 'T','w','o',0 }; + static WCHAR szStringDataField[] = { 'S','t','r','i','n','g','D','a','t','a',',','F','i','e','l','d',0 }; + static WCHAR szModifyModeRecord[] = { 'M','o','d','i','f','y',',','M','o','d','e',',','R','e','c','o','r','d',0 }; + IDispatch *pView = NULL, *pSummaryInfo = NULL; + HRESULT hr; + + hr = Database_OpenView(pDatabase, szSql, &pView); + ok(hr == S_OK, "Database_OpenView failed, hresult 0x%08x\n", hr); + if (hr == S_OK) + { + IDispatch *pRecord = NULL; + WCHAR szString[MAX_PATH]; + + /* View::Execute */ + hr = View_Execute(pView, NULL); + ok(hr == S_OK, "View_Execute failed, hresult 0x%08x\n", hr); + + /* View::Fetch */ + hr = View_Fetch(pView, &pRecord); + ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr); + ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n"); + if (pRecord) + { + /* Record::StringDataGet */ + memset(szString, 0, sizeof(szString)); + hr = Record_StringDataGet(pRecord, 1, szString); + ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr); + ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree); + + /* Record::StringDataPut with correct index */ + hr = Record_StringDataPut(pRecord, 1, szTwo); + ok(hr == S_OK, "Record_StringDataPut failed, hresult 0x%08x\n", hr); + + /* Record::StringDataGet */ + memset(szString, 0, sizeof(szString)); + hr = Record_StringDataGet(pRecord, 1, szString); + ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr); + ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo); + + /* Record::StringDataPut with incorrect index */ + hr = Record_StringDataPut(pRecord, -1, szString); + ok(hr == DISP_E_EXCEPTION, "Record_StringDataPut failed, hresult 0x%08x\n", hr); + ok_exception(hr, szStringDataField); + + /* View::Modify with incorrect parameters */ + hr = View_Modify(pView, -5, NULL); + ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr); + ok_exception(hr, szModifyModeRecord); + + hr = View_Modify(pView, -5, pRecord); + ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr); + ok_exception(hr, szModifyModeRecord); + + hr = View_Modify(pView, MSIMODIFY_REFRESH, NULL); + ok(hr == DISP_E_EXCEPTION, "View_Modify failed, hresult 0x%08x\n", hr); + ok_exception(hr, szModifyModeRecord); + + /* View::Modify with MSIMODIFY_REFRESH should undo our changes */ + hr = View_Modify(pView, MSIMODIFY_REFRESH, pRecord); + /* Wine's MsiViewModify currently does not support MSIMODIFY_REFRESH */ + todo_wine ok(hr == S_OK, "View_Modify failed, hresult 0x%08x\n", hr); + + /* Record::StringDataGet, confirm that the record is back to its unmodified value */ + memset(szString, 0, sizeof(szString)); + hr = Record_StringDataGet(pRecord, 1, szString); + ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr); + todo_wine ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szThree); + + IDispatch_Release(pRecord); + } + + /* View::Fetch */ + hr = View_Fetch(pView, &pRecord); + ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr); + ok(pRecord != NULL, "View_Fetch should not have returned NULL record\n"); + if (pRecord) + { + /* Record::StringDataGet */ + memset(szString, 0, sizeof(szString)); + hr = Record_StringDataGet(pRecord, 1, szString); + ok(hr == S_OK, "Record_StringDataGet failed, hresult 0x%08x\n", hr); + ok_w2("Record_StringDataGet result was %s but expected %s\n", szString, szTwo); + + IDispatch_Release(pRecord); + } + + /* View::Fetch */ + hr = View_Fetch(pView, &pRecord); + ok(hr == S_OK, "View_Fetch failed, hresult 0x%08x\n", hr); + ok(pRecord == NULL, "View_Fetch should have returned NULL record\n"); + if (pRecord) + IDispatch_Release(pRecord); + + /* View::Close */ + hr = View_Close(pView); + ok(hr == S_OK, "View_Close failed, hresult 0x%08x\n", hr); + + IDispatch_Release(pView); + } + + /* Database::SummaryInformation */ + hr = Database_SummaryInformation(pDatabase, TEST_SUMMARYINFO_PROPERTIES_MODIFIED, &pSummaryInfo); + ok(hr == S_OK, "Database_SummaryInformation failed, hresult 0x%08x\n", hr); + ok(pSummaryInfo != NULL, "Database_SummaryInformation should not have returned NULL record\n"); + if (pSummaryInfo) + { + test_SummaryInfo(pSummaryInfo, summary_info, sizeof(summary_info)/sizeof(msi_summary_info), readonly); + IDispatch_Release(pSummaryInfo); + } +} + +static void test_Session(IDispatch *pSession) +{ + static WCHAR szProductName[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 }; + static WCHAR szOne[] = { 'O','n','e',0 }; + static WCHAR szOneStateFalse[] = { '!','O','n','e','>','0',0 }; + static WCHAR szOneStateTrue[] = { '!','O','n','e','=','-','1',0 }; + static WCHAR szOneActionFalse[] = { '$','O','n','e','=','-','1',0 }; + static WCHAR szOneActionTrue[] = { '$','O','n','e','>','0',0 }; + static WCHAR szCostInitialize[] = { 'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0 }; + static WCHAR szEmpty[] = { 0 }; + static WCHAR szEquals[] = { '=',0 }; + static WCHAR szPropertyName[] = { 'P','r','o','p','e','r','t','y',',','N','a','m','e',0 }; + WCHAR stringw[MAX_PATH]; + CHAR string[MAX_PATH]; + UINT len; + BOOL bool; + int myint; + IDispatch *pDatabase = NULL, *pInst = NULL; + HRESULT hr; + + /* Session::Installer */ + hr = Session_Installer(pSession, &pInst); + ok(hr == S_OK, "Session_Installer failed, hresult 0x%08x\n", hr); + ok(pInst != NULL, "Session_Installer returned NULL IDispatch pointer\n"); + ok(pInst == pInstaller, "Session_Installer does not match Installer instance from CoCreateInstance\n"); + + /* Session::Property, get */ + memset(stringw, 0, sizeof(stringw)); + hr = Session_PropertyGet(pSession, szProductName, stringw); + ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr); + if (lstrcmpW(stringw, szMSITEST) != 0) + { + len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL); + ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError()); + ok(0, "Property \"ProductName\" expected to be \"MSITEST\" but was \"%s\"\n", string); + } + + /* Session::Property, put */ + hr = Session_PropertyPut(pSession, szProductName, szProductName); + ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr); + memset(stringw, 0, sizeof(stringw)); + hr = Session_PropertyGet(pSession, szProductName, stringw); + ok(hr == S_OK, "Session_PropertyGet failed, hresult 0x%08x\n", hr); + if (lstrcmpW(stringw, szProductName) != 0) + { + len = WideCharToMultiByte(CP_ACP, 0, stringw, -1, string, MAX_PATH, NULL, NULL); + ok(len, "WideCharToMultiByteChar returned error %d\n", GetLastError()); + ok(0, "Property \"ProductName\" expected to be \"ProductName\" but was \"%s\"\n", string); + } + + /* Try putting a property using empty property identifier */ + hr = Session_PropertyPut(pSession, szEmpty, szProductName); + ok(hr == DISP_E_EXCEPTION, "Session_PropertyPut failed, hresult 0x%08x\n", hr); + ok_exception(hr, szPropertyName); + + /* Try putting a property using illegal property identifier */ + hr = Session_PropertyPut(pSession, szEquals, szProductName); + ok(hr == S_OK, "Session_PropertyPut failed, hresult 0x%08x\n", hr); + + /* Session::Language, get */ + hr = Session_LanguageGet(pSession, &len); + ok(hr == S_OK, "Session_LanguageGet failed, hresult 0x%08x\n", hr); + /* Not sure how to check the language is correct */ + + /* Session::Mode, get */ + hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool); + ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr); + todo_wine ok(!bool, "Reboot at end session mode is %d\n", bool); + + /* Session::Mode, put */ + hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, TRUE); + todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr); + hr = Session_ModeGet(pSession, MSIRUNMODE_REBOOTATEND, &bool); + ok(hr == S_OK, "Session_ModeGet failed, hresult 0x%08x\n", hr); + ok(bool, "Reboot at end session mode is %d, expected 1\n", bool); + hr = Session_ModePut(pSession, MSIRUNMODE_REBOOTATEND, FALSE); /* set it again so we don't reboot */ + todo_wine ok(hr == S_OK, "Session_ModePut failed, hresult 0x%08x\n", hr); + + /* Session::Database, get */ + hr = Session_Database(pSession, &pDatabase); + ok(hr == S_OK, "Session_Database failed, hresult 0x%08x\n", hr); + if (hr == S_OK) + { + test_Database(pDatabase, TRUE); + IDispatch_Release(pDatabase); + } + + /* Session::EvaluateCondition */ + hr = Session_EvaluateCondition(pSession, NULL, &myint); + ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr); + ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN); + + hr = Session_EvaluateCondition(pSession, szEmpty, &myint); + ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr); + ok(myint == MSICONDITION_NONE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN); + + hr = Session_EvaluateCondition(pSession, szEquals, &myint); + ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr); + ok(myint == MSICONDITION_ERROR, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN); + + /* Session::DoAction(CostInitialize) must occur before the next statements */ + hr = Session_DoAction(pSession, szCostInitialize, &myint); + ok(hr == S_OK, "Session_DoAction failed, hresult 0x%08x\n", hr); + ok(myint == IDOK, "DoAction(CostInitialize) returned %d, %d expected\n", myint, IDOK); + + /* Session::SetInstallLevel */ + hr = Session_SetInstallLevel(pSession, INSTALLLEVEL_MINIMUM); + ok(hr == S_OK, "Session_SetInstallLevel failed, hresult 0x%08x\n", hr); + + /* Session::FeatureCurrentState, get */ + hr = Session_FeatureCurrentState(pSession, szOne, &myint); + ok(hr == S_OK, "Session_FeatureCurrentState failed, hresult 0x%08x\n", hr); + ok(myint == INSTALLSTATE_UNKNOWN, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN); + + /* Session::EvaluateCondition */ + hr = Session_EvaluateCondition(pSession, szOneStateFalse, &myint); + ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr); + ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN); + + hr = Session_EvaluateCondition(pSession, szOneStateTrue, &myint); + ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr); + ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN); + + /* Session::FeatureRequestState, put */ + hr = Session_FeatureRequestStatePut(pSession, szOne, INSTALLSTATE_ADVERTISED); + ok(hr == S_OK, "Session_FeatureRequestStatePut failed, hresult 0x%08x\n", hr); + hr = Session_FeatureRequestStateGet(pSession, szOne, &myint); + ok(hr == S_OK, "Session_FeatureRequestStateGet failed, hresult 0x%08x\n", hr); + ok(myint == INSTALLSTATE_ADVERTISED, "Feature request state was %d but expected %d\n", myint, INSTALLSTATE_ADVERTISED); + + /* Session::EvaluateCondition */ + hr = Session_EvaluateCondition(pSession, szOneActionFalse, &myint); + ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr); + ok(myint == MSICONDITION_FALSE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN); + + hr = Session_EvaluateCondition(pSession, szOneActionTrue, &myint); + ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr); + ok(myint == MSICONDITION_TRUE, "Feature current state was %d but expected %d\n", myint, INSTALLSTATE_UNKNOWN); +} + +/* delete key and all its subkeys */ +static DWORD delete_key( HKEY hkey ) +{ + char name[MAX_PATH]; + DWORD ret; + + while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name)))) + { + HKEY tmp; + if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp ))) + { + ret = delete_key( tmp ); + RegCloseKey( tmp ); + } + if (ret) break; + } + if (ret != ERROR_NO_MORE_ITEMS) return ret; + RegDeleteKeyA( hkey, "" ); + return 0; +} + +static void test_Installer_RegistryValue(void) +{ + static const DWORD qw[2] = { 0x12345678, 0x87654321 }; + static const WCHAR szKey[] = { 'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','T','e','s','t',0 }; + static const WCHAR szOne[] = { 'O','n','e',0 }; + static const WCHAR szTwo[] = { 'T','w','o',0 }; + static const WCHAR szThree[] = { 'T','h','r','e','e',0 }; + static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 }; + static const WCHAR szFour[] = { 'F','o','u','r',0 }; + static const WCHAR szExpand[] = { '%','M','S','I','T','E','S','T','%',0 }; + static const WCHAR szFive[] = { 'F','i','v','e',0,'H','i',0,0 }; + static const WCHAR szFiveHi[] = { 'F','i','v','e','\n','H','i',0 }; + static const WCHAR szSix[] = { 'S','i','x',0 }; + static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 }; + static const WCHAR szSeven[] = { 'S','e','v','e','n',0 }; + static const WCHAR szEight[] = { 'E','i','g','h','t',0 }; + static const WCHAR szBlank[] = { 0 }; + VARIANT varresult; + VARIANTARG vararg; + WCHAR szString[MAX_PATH]; + HKEY hkey, hkey_sub; + HRESULT hr; + BOOL bRet; + + /* Delete keys */ + if (!RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey )) delete_key( hkey ); + + /* Does our key exist? Shouldn't; check with all three possible value parameter types */ + hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet); + ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr); + ok(!bRet, "Registry key expected to not exist, but Installer_RegistryValue claims it does\n"); + + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString); + ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr); + + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR); + ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr); + + /* Create key */ + ok(!RegCreateKeyW( HKEY_CURRENT_USER, szKey, &hkey ), "RegCreateKeyW failed\n"); + + ok(!RegSetValueExW(hkey,szOne,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)), + "RegSetValueExW failed\n"); + ok(!RegSetValueExW(hkey,szTwo,0,REG_DWORD, (const BYTE *)qw, 4), + "RegSetValueExW failed\n"); + ok(!RegSetValueExW(hkey,szThree,0,REG_BINARY, (const BYTE *)qw, 4), + "RegSetValueExW failed\n"); + ok(SetEnvironmentVariableA("MSITEST", "Four"), "SetEnvironmentVariableA failed %d\n", GetLastError()); + ok(!RegSetValueExW(hkey,szFour,0,REG_EXPAND_SZ, (const BYTE *)szExpand, sizeof(szExpand)), + "RegSetValueExW failed\n"); + ok(!RegSetValueExW(hkey,szFive,0,REG_MULTI_SZ, (const BYTE *)szFive, sizeof(szFive)), + "RegSetValueExW failed\n"); + ok(!RegSetValueExW(hkey,szSix,0,REG_QWORD, (const BYTE *)qw, 8), + "RegSetValueExW failed\n"); + ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, (const BYTE *)NULL, 0), + "RegSetValueExW failed\n"); + + ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)), + "RegSetValueExW failed\n"); + + ok(!RegCreateKeyW( hkey, szEight, &hkey_sub ), "RegCreateKeyW failed\n"); + + /* Does our key exist? It should, and make sure we retrieve the correct default value */ + bRet = FALSE; + hr = Installer_RegistryValueE(HKEY_CURRENT_USER, szKey, &bRet); + ok(hr == S_OK, "Installer_RegistryValueE failed, hresult 0x%08x\n", hr); + ok(bRet, "Registry key expected to exist, but Installer_RegistryValue claims it does not\n"); + + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, NULL, szString); + ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr); + ok_w2("Default registry value \"%s\" does not match expected \"%s\"\n", szString, szOne); + + /* Ask for the value of a nonexistent key */ + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szExpand, szString); + ok(hr == DISP_E_BADINDEX, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr); + + /* Get values of keys */ + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szOne, szString); + ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr); + ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szOne); + + VariantInit(&vararg); + V_VT(&vararg) = VT_BSTR; + V_BSTR(&vararg) = SysAllocString(szTwo); + hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_I4); + ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr); + ok(V_I4(&varresult) == 305419896, "Registry value %d does not match expected value\n", V_I4(&varresult)); + VariantClear(&varresult); + + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szThree, szString); + ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr); + ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_BINARY); + + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFour, szString); + ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr); + ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFour); + + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szFive, szString); + ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr); + ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szFiveHi); + + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueW(HKEY_CURRENT_USER, szKey, szSix, szString); + ok(hr == S_OK, "Installer_RegistryValueW failed, hresult 0x%08x\n", hr); + ok_w2("Registry value \"%s\" does not match expected \"%s\"\n", szString, szREG_); + + VariantInit(&vararg); + V_VT(&vararg) = VT_BSTR; + V_BSTR(&vararg) = SysAllocString(szSeven); + hr = Installer_RegistryValue(HKEY_CURRENT_USER, szKey, vararg, &varresult, VT_EMPTY); + ok(hr == S_OK, "Installer_RegistryValue failed, hresult 0x%08x\n", hr); + + /* Get string class name for the key */ + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 0, szString, VT_BSTR); + ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr); + ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szBlank); + + /* Get name of a value by positive number (RegEnumValue like), valid index */ + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 2, szString, VT_BSTR); + ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr); + /* RegEnumValue order seems different on wine */ + todo_wine ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szTwo); + + /* Get name of a value by positive number (RegEnumValue like), invalid index */ + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, 10, szString, VT_EMPTY); + ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr); + + /* Get name of a subkey by negative number (RegEnumValue like), valid index */ + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -1, szString, VT_BSTR); + ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr); + ok_w2("Registry name \"%s\" does not match expected \"%s\"\n", szString, szEight); + + /* Get name of a subkey by negative number (RegEnumValue like), invalid index */ + memset(szString, 0, sizeof(szString)); + hr = Installer_RegistryValueI(HKEY_CURRENT_USER, szKey, -10, szString, VT_EMPTY); + ok(hr == S_OK, "Installer_RegistryValueI failed, hresult 0x%08x\n", hr); + + /* clean up */ + delete_key(hkey); +} + +static void test_Installer_Products(BOOL bProductInstalled) +{ + WCHAR szString[MAX_PATH]; + HRESULT hr; + int idx; + IUnknown *pUnk = NULL; + IEnumVARIANT *pEnum = NULL; + VARIANT var; + ULONG celt; + int iCount, iValue; + IDispatch *pStringList = NULL; + BOOL bProductFound = FALSE; + + /* Installer::Products */ + hr = Installer_Products(&pStringList); + ok(hr == S_OK, "Installer_Products failed, hresult 0x%08x\n", hr); + if (hr == S_OK) + { + /* StringList::_NewEnum */ + hr = StringList__NewEnum(pStringList, &pUnk); + ok(hr == S_OK, "StringList_NewEnum failed, hresult 0x%08x\n", hr); + if (hr == S_OK) + { + hr = IUnknown_QueryInterface(pUnk, &IID_IEnumVARIANT, (void **)&pEnum); + ok (hr == S_OK, "IUnknown::QueryInterface returned 0x%08x\n", hr); + } + if (!pEnum) + skip("IEnumVARIANT tests\n"); + + /* StringList::Count */ + hr = StringList_Count(pStringList, &iCount); + ok(hr == S_OK, "StringList_Count failed, hresult 0x%08x\n", hr); + + for (idx=0; idx #include #include #include +#include + #include "wine/test.h" static const char *msifile = "winetest.msi"; +static const char *msifile2 = "winetst2.msi"; +static const char *mstfile = "winetst.mst"; static void test_msidatabase(void) { - MSIHANDLE hdb = 0; + MSIHANDLE hdb = 0, hdb2 = 0; UINT res; DeleteFile(msifile); + res = MsiOpenDatabase( msifile, msifile2, &hdb ); + ok( res == ERROR_OPEN_FAILED, "expected failure\n"); + + res = MsiOpenDatabase( msifile, (LPSTR) 0xff, &hdb ); + ok( res == ERROR_INVALID_PARAMETER, "expected failure\n"); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + /* create an empty database */ res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb ); ok( res == ERROR_SUCCESS , "Failed to create database\n" ); @@ -41,8 +57,83 @@ static void test_msidatabase(void) res = MsiDatabaseCommit( hdb ); ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n"); + res = MsiCloseHandle( hdb ); ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + res = MsiOpenDatabase( msifile, msifile2, &hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile2 ), "database should exist\n"); + + res = MsiDatabaseCommit( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + res = MsiCloseHandle( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = MsiOpenDatabase( msifile, msifile2, &hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiCloseHandle( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + ok( INVALID_FILE_ATTRIBUTES == GetFileAttributes( msifile2 ), "uncommitted database should not exist\n"); + + res = MsiOpenDatabase( msifile, msifile2, &hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = MsiDatabaseCommit( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + res = MsiCloseHandle( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile2 ), "committed database should exist\n"); + + res = MsiOpenDatabase( msifile, MSIDBOPEN_READONLY, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = MsiOpenDatabase( msifile, MSIDBOPEN_DIRECT, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = MsiOpenDatabase( msifile, MSIDBOPEN_TRANSACT, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n"); + + /* MSIDBOPEN_CREATE deletes the database if MsiCommitDatabase isn't called */ + res = MsiOpenDatabase( msifile, MSIDBOPEN_CREATE, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n"); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + ok( INVALID_FILE_ATTRIBUTES == GetFileAttributes( msifile ), "database should exist\n"); + + res = MsiOpenDatabase( msifile, MSIDBOPEN_CREATE, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiDatabaseCommit( hdb ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n"); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = DeleteFile( msifile2 ); + ok( res == TRUE, "Failed to delete database\n" ); res = DeleteFile( msifile ); ok( res == TRUE, "Failed to delete database\n" ); @@ -70,6 +161,126 @@ static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec) return ret; } +static UINT run_query( MSIHANDLE hdb, MSIHANDLE hrec, const char *query ) +{ + MSIHANDLE hview = 0; + UINT r; + + r = MsiDatabaseOpenView(hdb, query, &hview); + if( r != ERROR_SUCCESS ) + return r; + + r = MsiViewExecute(hview, hrec); + if( r == ERROR_SUCCESS ) + r = MsiViewClose(hview); + MsiCloseHandle(hview); + return r; +} + +static UINT create_component_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `Component` ( " + "`Component` CHAR(72) NOT NULL, " + "`ComponentId` CHAR(38), " + "`Directory_` CHAR(72) NOT NULL, " + "`Attributes` SHORT NOT NULL, " + "`Condition` CHAR(255), " + "`KeyPath` CHAR(72) " + "PRIMARY KEY `Component`)" ); +} + +static UINT create_feature_components_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `FeatureComponents` ( " + "`Feature_` CHAR(38) NOT NULL, " + "`Component_` CHAR(72) NOT NULL " + "PRIMARY KEY `Feature_`, `Component_` )" ); +} + +static UINT create_std_dlls_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `StdDlls` ( " + "`File` CHAR(255) NOT NULL, " + "`Binary_` CHAR(72) NOT NULL " + "PRIMARY KEY `File` )" ); +} + +static UINT create_binary_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `Binary` ( " + "`Name` CHAR(72) NOT NULL, " + "`Data` CHAR(72) NOT NULL " + "PRIMARY KEY `Name` )" ); +} + +static UINT add_component_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `Component` " + "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, 0, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + +static UINT add_feature_components_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `FeatureComponents` " + "(`Feature_`, `Component_`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, 0, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + +static UINT add_std_dlls_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `StdDlls` " + "(`File`, `Binary_`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, 0, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + +static UINT add_binary_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `Binary` " + "(`Name`, `Data`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, 0, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + static void test_msiinsert(void) { MSIHANDLE hdb = 0, hview = 0, hrec = 0; @@ -162,17 +373,12 @@ static void test_msiinsert(void) r = do_query(hdb, query, &hrec); ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n"); - todo_wine { /* now try a few bad INSERT xqueries */ query = "INSERT INTO `phone` ( `id`, `name`, `number` )" "VALUES(?, ?)"; r = MsiDatabaseOpenView(hdb, query, &hview); ok(r == ERROR_BAD_QUERY_SYNTAX, "MsiDatabaseOpenView failed\n"); - if (r == ERROR_SUCCESS) - r = MsiCloseHandle(hview); - } - /* construct a record to insert */ hrec = MsiCreateRecord(4); r = MsiRecordSetInteger(hrec, 1, 2); @@ -225,8 +431,6 @@ static void test_msidecomposedesc(void) HMODULE hmod; hmod = GetModuleHandle("msi.dll"); - if (!hmod) - return; pMsiDecomposeDescriptorA = (fnMsiDecomposeDescriptorA) GetProcAddress(hmod, "MsiDecomposeDescriptorA"); if (!pMsiDecomposeDescriptorA) @@ -450,6 +654,39 @@ static void test_msibadqueries(void) r = try_query( hdb, "CREATE TABLE `c` (`b` CHAR NOT NULL PRIMARY KEY `b`)"); ok(r == ERROR_SUCCESS , "query 8 failed\n"); + r = try_query( hdb, "select * from c"); + ok(r == ERROR_SUCCESS , "query failed\n"); + + r = try_query( hdb, "select * from c where b = 'x"); + todo_wine ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from 'c'"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from ''"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from c where b = x"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from c where b = \"x\""); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from c where b = 'x'"); + ok(r == ERROR_SUCCESS, "query failed\n"); + + r = try_query( hdb, "select * from c where b = '\"x'"); + ok(r == ERROR_SUCCESS, "query failed\n"); + + if (0) /* FIXME: this query causes trouble with other tests */ + { + r = try_query( hdb, "select * from c where b = '\\\'x'"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + } + + r = try_query( hdb, "select * from 'c'"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + r = MsiCloseHandle( hdb ); ok(r == ERROR_SUCCESS , "Failed to close database transact\n"); @@ -457,26 +694,11 @@ static void test_msibadqueries(void) ok(r == TRUE, "file didn't exist after commit\n"); } -static UINT run_query( MSIHANDLE hdb, MSIHANDLE hrec, const char *query ) -{ - MSIHANDLE hview = 0; - UINT r; - - r = MsiDatabaseOpenView(hdb, query, &hview); - if( r != ERROR_SUCCESS ) - return r; - - r = MsiViewExecute(hview, hrec); - if( r == ERROR_SUCCESS ) - r = MsiViewClose(hview); - MsiCloseHandle(hview); - return r; -} - static void test_viewmodify(void) { MSIHANDLE hdb = 0, hview = 0, hrec = 0; UINT r; + MSIDBERROR err; const char *query; char buffer[0x100]; DWORD sz; @@ -496,8 +718,8 @@ static void test_viewmodify(void) /* check what the error function reports without doing anything */ sz = 0; /* passing NULL as the 3rd param make function to crash on older platforms */ - r = MsiViewGetError( 0, NULL, &sz ); - ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n"); + err = MsiViewGetError( 0, NULL, &sz ); + ok(err == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n"); /* open a view */ query = "SELECT * FROM `phone`"; @@ -505,31 +727,31 @@ static void test_viewmodify(void) ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); /* see what happens with a good hview and bad args */ - r = MsiViewGetError( hview, NULL, NULL ); - ok(r == MSIDBERROR_INVALIDARG || r == MSIDBERROR_NOERROR, - "MsiViewGetError returns %u (expected -3)\n", r); - r = MsiViewGetError( hview, buffer, NULL ); - ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n"); + err = MsiViewGetError( hview, NULL, NULL ); + ok(err == MSIDBERROR_INVALIDARG || err == MSIDBERROR_NOERROR, + "MsiViewGetError returns %u (expected -3)\n", err); + err = MsiViewGetError( hview, buffer, NULL ); + ok(err == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n"); /* see what happens with a zero length buffer */ sz = 0; buffer[0] = 'x'; - r = MsiViewGetError( hview, buffer, &sz ); - ok(r == MSIDBERROR_MOREDATA, "MsiViewGetError return\n"); + err = MsiViewGetError( hview, buffer, &sz ); + ok(err == MSIDBERROR_MOREDATA, "MsiViewGetError return\n"); ok(buffer[0] == 'x', "buffer cleared\n"); ok(sz == 0, "size not zero\n"); /* ok this one is strange */ sz = 0; - r = MsiViewGetError( hview, NULL, &sz ); - ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n"); + err = MsiViewGetError( hview, NULL, &sz ); + ok(err == MSIDBERROR_NOERROR, "MsiViewGetError return\n"); ok(sz == 0, "size not zero\n"); /* see if it really has an error */ sz = sizeof buffer; buffer[0] = 'x'; - r = MsiViewGetError( hview, buffer, &sz ); - ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n"); + err = MsiViewGetError( hview, buffer, &sz ); + ok(err == MSIDBERROR_NOERROR, "MsiViewGetError return\n"); ok(buffer[0] == 0, "buffer not cleared\n"); ok(sz == 0, "size not zero\n"); @@ -553,12 +775,12 @@ static void test_viewmodify(void) /* insert a valid record */ hrec = MsiCreateRecord(3); - r = MsiRecordSetInteger(hrec, 2, 1); + r = MsiRecordSetInteger(hrec, 1, 1); ok(r == ERROR_SUCCESS, "failed to set integer\n"); r = MsiRecordSetString(hrec, 2, "bob"); - ok(r == ERROR_SUCCESS, "failed to set integer\n"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); r = MsiRecordSetString(hrec, 3, "7654321"); - ok(r == ERROR_SUCCESS, "failed to set integer\n"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); r = MsiViewExecute(hview, 0); ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); @@ -570,14 +792,192 @@ static void test_viewmodify(void) ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); /* should fail ... */ - todo_wine { r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec ); ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `phone`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "MsiRecordGetString failed\n"); + ok(!lstrcmp(buffer, "bob"), "Expected bob, got %s\n", buffer); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "MsiRecordGetString failed\n"); + ok(!lstrcmp(buffer, "7654321"), "Expected 7654321, got %s\n", buffer); + + /* update the view, non-primary key */ + r = MsiRecordSetString(hrec, 3, "3141592"); + ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_SUCCESS, "MsiViewModify failed\n"); + + /* do it again */ + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_SUCCESS, "MsiViewModify failed: %d\n", r); + + /* update the view, primary key */ + r = MsiRecordSetInteger(hrec, 1, 5); + ok(r == ERROR_SUCCESS, "MsiRecordSetInteger failed\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `phone`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "MsiRecordGetString failed\n"); + ok(!lstrcmp(buffer, "bob"), "Expected bob, got %s\n", buffer); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "MsiRecordGetString failed\n"); + ok(!lstrcmp(buffer, "3141592"), "Expected 3141592, got %s\n", buffer); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + /* use a record that doesn't come from a view fetch */ + hrec = MsiCreateRecord(3); + ok(hrec != 0, "MsiCreateRecord failed\n"); + + r = MsiRecordSetInteger(hrec, 1, 3); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "jane"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + r = MsiRecordSetString(hrec, 3, "112358"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + /* use a record that doesn't come from a view fetch, primary key matches */ + hrec = MsiCreateRecord(3); + ok(hrec != 0, "MsiCreateRecord failed\n"); + + r = MsiRecordSetInteger(hrec, 1, 1); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "jane"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + r = MsiRecordSetString(hrec, 3, "112358"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + todo_wine + { + ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); } r = MsiCloseHandle(hrec); ok(r == ERROR_SUCCESS, "failed to close record\n"); + hrec = MsiCreateRecord(3); + + r = MsiRecordSetInteger(hrec, 1, 2); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "nick"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + r = MsiRecordSetString(hrec, 3, "141421"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec ); + ok(r == ERROR_SUCCESS, "MsiViewModify failed\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `phone` WHERE `id` = 1"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + /* change the id to match the second row */ + r = MsiRecordSetInteger(hrec, 1, 2); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "jerry"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* broader search */ + query = "SELECT * FROM `phone` ORDER BY `id`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + /* change the id to match the second row */ + r = MsiRecordSetInteger(hrec, 1, 2); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "jerry"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); r = MsiViewClose(hview); ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); r = MsiCloseHandle(hview); @@ -861,7 +1261,6 @@ static void test_msiexport(void) GetCurrentDirectory(MAX_PATH, path); - todo_wine { r = MsiDatabaseExport(hdb, "phone", path, file); ok(r == ERROR_SUCCESS, "MsiDatabaseExport failed\n"); @@ -885,13 +1284,12 @@ static void test_msiexport(void) ok( length == strlen(expected), "length of data wrong\n"); ok( !lstrcmp(buffer, expected), "data doesn't match\n"); - } DeleteFile(msifile); } static void test_longstrings(void) { - const char insert_query[] = + const char insert_query[] = "INSERT INTO `strings` ( `id`, `val` ) VALUES('1', 'Z')"; char *str; MSIHANDLE hdb = 0, hview = 0, hrec = 0; @@ -905,7 +1303,7 @@ static void test_longstrings(void) ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); /* create a table */ - r = try_query( hdb, + r = try_query( hdb, "CREATE TABLE `strings` ( `id` INT, `val` CHAR(0) PRIMARY KEY `id`)"); ok(r == ERROR_SUCCESS, "query failed\n"); @@ -940,18 +1338,31 @@ static void test_longstrings(void) r = MsiRecordGetString(hrec, 2, NULL, &len); ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); - todo_wine { ok(len == STRING_LENGTH, "string length wrong\n"); - } MsiCloseHandle(hrec); MsiCloseHandle(hdb); DeleteFile(msifile); } +static void create_file(const CHAR *name) +{ + HANDLE file; + DWORD written; + + file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name); + WriteFile(file, name, strlen(name), &written, NULL); + WriteFile(file, "\n", strlen("\n"), &written, NULL); + CloseHandle(file); +} + static void test_streamtable(void) { - MSIHANDLE hdb = 0, rec; + MSIHANDLE hdb = 0, rec, view; + char file[MAX_PATH]; + char buf[MAX_PATH]; + DWORD size; UINT r; hdb = create_db(); @@ -962,14 +1373,25 @@ static void test_streamtable(void) "( `Property` CHAR(255), `Value` CHAR(1) PRIMARY KEY `Property`)" ); ok( r == ERROR_SUCCESS , "Failed to create table\n" ); + r = run_query( hdb, 0, + "INSERT INTO `Properties` " + "( `Value`, `Property` ) VALUES ( 'Prop', 'value' )" ); + ok( r == ERROR_SUCCESS, "Failed to add to table\n" ); + + r = MsiDatabaseCommit( hdb ); + ok( r == ERROR_SUCCESS , "Failed to commit database\n" ); + + MsiCloseHandle( hdb ); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb ); + ok( r == ERROR_SUCCESS , "Failed to open database\n" ); + /* check the column types */ rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_TYPES ); ok( rec, "failed to get column info record\n" ); - todo_wine { ok( check_record( rec, 1, "s62"), "wrong record type\n"); ok( check_record( rec, 2, "V0"), "wrong record type\n"); - } MsiCloseHandle( rec ); @@ -977,21 +1399,71 @@ static void test_streamtable(void) rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_NAMES ); ok( rec, "failed to get column info record\n" ); - todo_wine { ok( check_record( rec, 1, "Name"), "wrong record type\n"); ok( check_record( rec, 2, "Data"), "wrong record type\n"); - } MsiCloseHandle( rec ); + + /* insert a file into the _Streams table */ + create_file( "test.txt" ); + + rec = MsiCreateRecord( 2 ); + MsiRecordSetString( rec, 1, "data" ); + + r = MsiRecordSetStream( rec, 2, "test.txt" ); + ok( r == ERROR_SUCCESS, "Failed to add stream data to the record: %d\n", r); + + DeleteFile("test.txt"); + + r = MsiDatabaseOpenView( hdb, + "INSERT INTO `_Streams` ( `Name`, `Data` ) VALUES ( ?, ? )", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + + r = MsiViewExecute( view, rec ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + + MsiCloseHandle( rec ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, + "SELECT `Name`, `Data` FROM `_Streams`", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "Failed to fetch record: %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, file, &size ); + ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r); + ok( !lstrcmp(file, "data"), "Expected 'data', got %s\n", file); + + size = MAX_PATH; + memset(buf, 0, MAX_PATH); + r = MsiRecordReadStream( rec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r); + ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf); + + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiCloseHandle( view ); MsiCloseHandle( hdb ); DeleteFile(msifile); } static void test_where(void) { - MSIHANDLE hdb = 0, rec; + MSIHANDLE hdb = 0, rec, view; LPCSTR query; UINT r; + DWORD size; + CHAR buf[MAX_PATH]; + UINT count; hdb = create_db(); ok( hdb, "failed to create db\n"); @@ -1037,9 +1509,70 @@ static void test_where(void) ok( 2 == r, "field wrong\n"); r = MsiRecordGetInteger(rec, 2); ok( 1 == r, "field wrong\n"); + MsiCloseHandle( rec ); + + query = "SELECT `DiskId` FROM `Media` WHERE `LastSequence` >= 1 AND DiskId >= 0"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(view, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + r = MsiViewFetch(view, &rec); + ok( r == ERROR_SUCCESS, "failed to fetch view: %d\n", r ); + + count = MsiRecordGetFieldCount( rec ); + ok( count == 1, "Expected 1 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + ok( !lstrcmp( buf, "2" ), + "For (row %d, column 1) expected '%d', got %s\n", 0, 2, buf ); + MsiCloseHandle( rec ); + + r = MsiViewFetch(view, &rec); + ok( r == ERROR_SUCCESS, "failed to fetch view: %d\n", r ); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + ok( !lstrcmp( buf, "3" ), + "For (row %d, column 1) expected '%d', got %s\n", 1, 3, buf ); + MsiCloseHandle( rec ); + + r = MsiViewFetch(view, &rec); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(view); + MsiCloseHandle(view); MsiCloseHandle( rec ); + rec = 0; + query = "SELECT * FROM `Media` WHERE `DiskPrompt` IS NULL"; + r = do_query(hdb, query, &rec); + ok( r == ERROR_SUCCESS, "query failed: %d\n", r ); + MsiCloseHandle( rec ); + + rec = MsiCreateRecord(1); + MsiRecordSetString(rec, 1, ""); + + query = "SELECT * FROM `Media` WHERE `DiskPrompt` = ?"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(rec); + MsiViewClose(view); + MsiCloseHandle(view); + MsiCloseHandle( hdb ); DeleteFile(msifile); } @@ -1051,6 +1584,24 @@ static const CHAR test_data[] = "FirstPrimaryColumn\tSecondPrimaryColumn\tShortI "TestTable\tFirstPrimaryColumn\n" "stringage\t5\t2\t\t2147483640\t-2147483640\tanother string\tlocalizable\tduh\n"; +static const CHAR two_primary[] = "PrimaryOne\tPrimaryTwo\n" + "s255\ts255\n" + "TwoPrimary\tPrimaryOne\tPrimaryTwo\n" + "papaya\tleaf\n" + "papaya\tflower\n"; + +static const CHAR endlines1[] = "A\tB\tC\tD\tE\tF\r\n" + "s72\ts72\ts72\ts72\ts72\ts72\n" + "Table\tA\r\n" + "a\tb\tc\td\te\tf\n" + "g\th\ti\t\rj\tk\tl\r\n"; + +static const CHAR endlines2[] = "A\tB\tC\tD\tE\tF\r" + "s72\ts72\ts72\ts72\ts72\ts72\n" + "Table2\tA\r\n" + "a\tb\tc\td\te\tf\n" + "g\th\ti\tj\tk\tl\r\n"; + static void write_file(const CHAR *filename, const char *data, int data_size) { DWORD size; @@ -1086,13 +1637,130 @@ static void test_msiimport(void) ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); r = add_table_to_db(hdb, test_data); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_table_to_db(hdb, two_primary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_table_to_db(hdb, endlines1); todo_wine { ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); } + r = add_table_to_db(hdb, endlines2); + todo_wine + { + ok(r == ERROR_FUNCTION_FAILED, + "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + } + query = "SELECT * FROM `TestTable`"; r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); + count = MsiRecordGetFieldCount(rec); + ok(count == 9, "Expected 9, got %d\n", count); + ok(check_record(rec, 1, "FirstPrimaryColumn"), "Expected FirstPrimaryColumn\n"); + ok(check_record(rec, 2, "SecondPrimaryColumn"), "Expected SecondPrimaryColumn\n"); + ok(check_record(rec, 3, "ShortInt"), "Expected ShortInt\n"); + ok(check_record(rec, 4, "ShortIntNullable"), "Expected ShortIntNullalble\n"); + ok(check_record(rec, 5, "LongInt"), "Expected LongInt\n"); + ok(check_record(rec, 6, "LongIntNullable"), "Expected LongIntNullalble\n"); + ok(check_record(rec, 7, "String"), "Expected String\n"); + ok(check_record(rec, 8, "LocalizableString"), "Expected LocalizableString\n"); + ok(check_record(rec, 9, "LocalizableStringNullable"), "Expected LocalizableStringNullable\n"); + MsiCloseHandle(rec); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + count = MsiRecordGetFieldCount(rec); + ok(count == 9, "Expected 9, got %d\n", count); + ok(check_record(rec, 1, "s255"), "Expected s255\n"); + ok(check_record(rec, 2, "i2"), "Expected i2\n"); + ok(check_record(rec, 3, "i2"), "Expected i2\n"); + ok(check_record(rec, 4, "I2"), "Expected I2\n"); + ok(check_record(rec, 5, "i4"), "Expected i4\n"); + ok(check_record(rec, 6, "I4"), "Expected I4\n"); + ok(check_record(rec, 7, "S255"), "Expected S255\n"); + ok(check_record(rec, 8, "S0"), "Expected S0\n"); + ok(check_record(rec, 9, "s0"), "Expected s0\n"); + MsiCloseHandle(rec); + + query = "SELECT * FROM `TestTable`"; + r = do_query(hdb, query, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(check_record(rec, 1, "stringage"), "Expected 'stringage'\n"); + ok(check_record(rec, 7, "another string"), "Expected 'another string'\n"); + ok(check_record(rec, 8, "localizable"), "Expected 'localizable'\n"); + ok(check_record(rec, 9, "duh"), "Expected 'duh'\n"); + + i = MsiRecordGetInteger(rec, 2); + ok(i == 5, "Expected 5, got %d\n", i); + + i = MsiRecordGetInteger(rec, 3); + ok(i == 2, "Expected 2, got %d\n", i); + + i = MsiRecordGetInteger(rec, 4); + ok(i == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", i); + + i = MsiRecordGetInteger(rec, 5); + ok(i == 2147483640, "Expected 2147483640, got %d\n", i); + + i = MsiRecordGetInteger(rec, 6); + ok(i == -2147483640, "Expected -2147483640, got %d\n", i); + + MsiCloseHandle(rec); + MsiCloseHandle(view); + + query = "SELECT * FROM `TwoPrimary`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); + count = MsiRecordGetFieldCount(rec); + ok(count == 2, "Expected 2, got %d\n", count); + ok(check_record(rec, 1, "PrimaryOne"), "Expected PrimaryOne\n"); + ok(check_record(rec, 2, "PrimaryTwo"), "Expected PrimaryTwo\n"); + + MsiCloseHandle(rec); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + count = MsiRecordGetFieldCount(rec); + ok(count == 2, "Expected 2, got %d\n", count); + ok(check_record(rec, 1, "s255"), "Expected s255\n"); + ok(check_record(rec, 2, "s255"), "Expected s255\n"); + MsiCloseHandle(rec); + + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + ok(check_record(rec, 1, "papaya"), "Expected 'papaya'\n"); + ok(check_record(rec, 2, "leaf"), "Expected 'leaf'\n"); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + ok(check_record(rec, 1, "papaya"), "Expected 'papaya'\n"); + ok(check_record(rec, 2, "flower"), "Expected 'flower'\n"); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(view); + + query = "SELECT * FROM `Table`"; + r = MsiDatabaseOpenView(hdb, query, &view); todo_wine { ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); @@ -1102,77 +1770,82 @@ static void test_msiimport(void) count = MsiRecordGetFieldCount(rec); todo_wine { - ok(count == 9, "Expected 9, got %d\n", count); - ok(check_record(rec, 1, "FirstPrimaryColumn"), "Expected FirstPrimaryColumn\n"); - ok(check_record(rec, 2, "SecondPrimaryColumn"), "Expected SecondPrimaryColumn\n"); - ok(check_record(rec, 3, "ShortInt"), "Expected ShortInt\n"); - ok(check_record(rec, 4, "ShortIntNullable"), "Expected ShortIntNullalble\n"); - ok(check_record(rec, 5, "LongInt"), "Expected LongInt\n"); - ok(check_record(rec, 6, "LongIntNullable"), "Expected LongIntNullalble\n"); - ok(check_record(rec, 7, "String"), "Expected String\n"); - ok(check_record(rec, 8, "LocalizableString"), "Expected LocalizableString\n"); - ok(check_record(rec, 9, "LocalizableStringNullable"), "Expected LocalizableStringNullable\n"); + ok(count == 6, "Expected 6, got %d\n", count); + ok(check_record(rec, 1, "A"), "Expected A\n"); + ok(check_record(rec, 2, "B"), "Expected B\n"); + ok(check_record(rec, 3, "C"), "Expected C\n"); + ok(check_record(rec, 4, "D"), "Expected D\n"); + ok(check_record(rec, 5, "E"), "Expected E\n"); + ok(check_record(rec, 6, "F"), "Expected F\n"); } + MsiCloseHandle(rec); r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); count = MsiRecordGetFieldCount(rec); todo_wine { - ok(count == 9, "Expected 9, got %d\n", count); - ok(check_record(rec, 1, "s255"), "Expected s255\n"); - ok(check_record(rec, 2, "i2"), "Expected i2\n"); - ok(check_record(rec, 3, "i2"), "Expected i2\n"); - ok(check_record(rec, 4, "I2"), "Expected I2\n"); - ok(check_record(rec, 5, "i4"), "Expected i4\n"); - ok(check_record(rec, 6, "I4"), "Expected I4\n"); - ok(check_record(rec, 7, "S255"), "Expected S255\n"); - ok(check_record(rec, 8, "S0"), "Expected S0\n"); - ok(check_record(rec, 9, "s0"), "Expected s0\n"); + ok(count == 6, "Expected 6, got %d\n", count); + ok(check_record(rec, 1, "s72"), "Expected s72\n"); + ok(check_record(rec, 2, "s72"), "Expected s72\n"); + ok(check_record(rec, 3, "s72"), "Expected s72\n"); + ok(check_record(rec, 4, "s72"), "Expected s72\n"); + ok(check_record(rec, 5, "s72"), "Expected s72\n"); + ok(check_record(rec, 6, "s72"), "Expected s72\n"); } + MsiCloseHandle(rec); - query = "SELECT * FROM `TestTable`"; - r = do_query(hdb, query, &rec); + MsiViewClose(view); + MsiCloseHandle(view); + + query = "SELECT * FROM `Table`"; + r = MsiDatabaseOpenView(hdb, query, &view); todo_wine { ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); } + r = MsiViewExecute(view, 0); todo_wine { - ok(check_record(rec, 1, "stringage"), "Expected 'stringage'\n"); - ok(check_record(rec, 7, "another string"), "Expected 'another string'\n"); - ok(check_record(rec, 8, "localizable"), "Expected 'localizable'\n"); - ok(check_record(rec, 9, "duh"), "Expected 'duh'\n"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); } - i = MsiRecordGetInteger(rec, 2); + r = MsiViewFetch(view, &rec); todo_wine { - ok(i == 5, "Expected 5, got %d\n", i); - } - - i = MsiRecordGetInteger(rec, 3); - todo_wine - { - ok(i == 2, "Expected 2, got %d\n", i); - } - - i = MsiRecordGetInteger(rec, 4); - ok(i == 0x80000000, "Expected 0x80000000, got %d\n", i); - - i = MsiRecordGetInteger(rec, 5); - todo_wine - { - ok(i == 2147483640, "Expected 2147483640, got %d\n", i); - } - - i = MsiRecordGetInteger(rec, 6); - todo_wine - { - ok(i == -2147483640, "Expected -2147483640, got %d\n", i); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(check_record(rec, 1, "a"), "Expected 'a'\n"); + ok(check_record(rec, 2, "b"), "Expected 'b'\n"); + ok(check_record(rec, 3, "c"), "Expected 'c'\n"); + ok(check_record(rec, 4, "d"), "Expected 'd'\n"); + ok(check_record(rec, 5, "e"), "Expected 'e'\n"); + ok(check_record(rec, 6, "f"), "Expected 'f'\n"); } MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(check_record(rec, 1, "g"), "Expected 'g'\n"); + ok(check_record(rec, 2, "h"), "Expected 'h'\n"); + ok(check_record(rec, 3, "i"), "Expected 'i'\n"); + ok(check_record(rec, 4, "j"), "Expected 'j'\n"); + ok(check_record(rec, 5, "k"), "Expected 'k'\n"); + ok(check_record(rec, 6, "l"), "Expected 'l'\n"); + } + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + todo_wine + { + ok(r == ERROR_NO_MORE_ITEMS, + "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + } + + MsiViewClose(view); MsiCloseHandle(view); MsiCloseHandle(hdb); DeleteFileA(msifile); @@ -1205,6 +1878,13 @@ static void test_markers(void) r = run_query(hdb, rec, query); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + /* verify that we just created a table called '?', not 'Fable' */ + r = try_query(hdb, "SELECT * from `Fable`"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + r = try_query(hdb, "SELECT * from `?`"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + /* try table name as marker without backticks */ MsiRecordSetString(rec, 1, "Mable"); query = "CREATE TABLE ? ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)"; @@ -1216,28 +1896,25 @@ static void test_markers(void) query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)"; r = run_query(hdb, rec, query); ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); /* try column names as markers */ - MsiCloseHandle(rec); rec = MsiCreateRecord(2); MsiRecordSetString(rec, 1, "One"); MsiRecordSetString(rec, 2, "Two"); query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `One`)"; r = run_query(hdb, rec, query); ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); /* try names with backticks */ - MsiCloseHandle(rec); rec = MsiCreateRecord(3); MsiRecordSetString(rec, 1, "One"); MsiRecordSetString(rec, 2, "Two"); MsiRecordSetString(rec, 3, "One"); query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)"; r = run_query(hdb, rec, query); - todo_wine - { - ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); - } + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); /* try names with backticks, minus definitions */ query = "CREATE TABLE `Mable` ( `?`, `?` PRIMARY KEY `?`)"; @@ -1248,17 +1925,17 @@ static void test_markers(void) query = "CREATE TABLE `Mable` ( ? SHORT NOT NULL, ? CHAR(255) PRIMARY KEY ?)"; r = run_query(hdb, rec, query); ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); /* try one long marker */ - MsiCloseHandle(rec); rec = MsiCreateRecord(1); MsiRecordSetString(rec, 1, "`One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`"); query = "CREATE TABLE `Mable` ( ? )"; r = run_query(hdb, rec, query); ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); /* try all names as markers */ - MsiCloseHandle(rec); rec = MsiCreateRecord(4); MsiRecordSetString(rec, 1, "Mable"); MsiRecordSetString(rec, 2, "One"); @@ -1267,23 +1944,26 @@ static void test_markers(void) query = "CREATE TABLE `?` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)"; r = run_query(hdb, rec, query); ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); /* try a legit insert */ query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( 5, 'hello' )"; r = run_query(hdb, 0, query); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = try_query(hdb, "SELECT * from `Table`"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + /* try values as markers */ - MsiCloseHandle(rec); rec = MsiCreateRecord(2); MsiRecordSetInteger(rec, 1, 4); MsiRecordSetString(rec, 2, "hi"); query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )"; r = run_query(hdb, rec, query); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(rec); /* try column names and values as markers */ - MsiCloseHandle(rec); rec = MsiCreateRecord(4); MsiRecordSetString(rec, 1, "One"); MsiRecordSetString(rec, 2, "Two"); @@ -1292,39 +1972,36 @@ static void test_markers(void) query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( ?, '?' )"; r = run_query(hdb, rec, query); ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); /* try column names as markers */ - MsiCloseHandle(rec); rec = MsiCreateRecord(2); MsiRecordSetString(rec, 1, "One"); MsiRecordSetString(rec, 2, "Two"); query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( 3, 'yellow' )"; r = run_query(hdb, rec, query); ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); /* try table name as a marker */ - MsiCloseHandle(rec); rec = MsiCreateRecord(1); MsiRecordSetString(rec, 1, "Table"); query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( 2, 'green' )"; r = run_query(hdb, rec, query); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(rec); /* try table name and values as markers */ - MsiCloseHandle(rec); rec = MsiCreateRecord(3); MsiRecordSetString(rec, 1, "Table"); MsiRecordSetInteger(rec, 2, 10); MsiRecordSetString(rec, 3, "haha"); query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( ?, '?' )"; r = run_query(hdb, rec, query); - todo_wine - { - ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r); - } + ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + MsiCloseHandle(rec); /* try all markers */ - MsiCloseHandle(rec); rec = MsiCreateRecord(5); MsiRecordSetString(rec, 1, "Table"); MsiRecordSetString(rec, 1, "One"); @@ -1334,18 +2011,18 @@ static void test_markers(void) query = "INSERT INTO `?` ( `?`, `?` ) VALUES ( ?, '?' )"; r = run_query(hdb, rec, query); ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); /* insert an integer as a string */ - MsiCloseHandle(rec); rec = MsiCreateRecord(2); MsiRecordSetString(rec, 1, "11"); MsiRecordSetString(rec, 2, "hi"); query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )"; r = run_query(hdb, rec, query); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(rec); /* leave off the '' for the string */ - MsiCloseHandle(rec); rec = MsiCreateRecord(2); MsiRecordSetInteger(rec, 1, 12); MsiRecordSetString(rec, 2, "hi"); @@ -1358,6 +2035,3472 @@ static void test_markers(void) DeleteFileA(msifile); } +#define MY_NVIEWS 4000 /* Largest installer I've seen uses < 2k */ +static void test_handle_limit(void) +{ + int i; + MSIHANDLE hdb; + MSIHANDLE hviews[MY_NVIEWS]; + UINT r; + + /* create an empty db */ + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + memset(hviews, 0, sizeof(hviews)); + + for (i=0; i #include @@ -25,44 +28,24 @@ #include #include #include +#include #include "wine/test.h" -static const char *msifile = "winetest.msi"; -CHAR CURR_DIR[MAX_PATH]; -CHAR PROG_FILES_DIR[MAX_PATH]; +static UINT (WINAPI *pMsiQueryComponentStateA) + (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, LPCSTR, INSTALLSTATE*); +static UINT (WINAPI *pMsiSourceListGetInfoA) + (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, LPCSTR, LPSTR, LPDWORD); + +static const char *msifile = "msitest.msi"; +static const char *msifile2 = "winetest2.msi"; +static const char *mstfile = "winetest.mst"; +static CHAR CURR_DIR[MAX_PATH]; +static CHAR PROG_FILES_DIR[MAX_PATH]; +static CHAR COMMON_FILES_DIR[MAX_PATH]; /* msi database data */ -static const CHAR admin_exec_seq_dat[] = "Action\tCondition\tSequence\n" - "s72\tS255\tI2\n" - "AdminExecuteSequence\tAction\n" - "CostFinalize\t\t1000\n" - "CostInitialize\t\t800\n" - "FileCost\t\t900\n" - "InstallAdminPackage\t\t3900\n" - "InstallFiles\t\t4000\n" - "InstallFinalize\t\t6600\n" - "InstallInitialize\t\t1500\n" - "InstallValidate\t\t1400"; - -static const CHAR advt_exec_seq_dat[] = "Action\tCondition\tSequence\n" - "s72\tS255\tI2\n" - "AdvtExecuteSequence\tAction\n" - "CostFinalize\t\t1000\n" - "CostInitialize\t\t800\n" - "CreateShortcuts\t\t4500\n" - "InstallFinalize\t\t6600\n" - "InstallInitialize\t\t1500\n" - "InstallValidate\t\t1400\n" - "PublishComponents\t\t6200\n" - "PublishFeatures\t\t6300\n" - "PublishProduct\t\t6400\n" - "RegisterClassInfo\t\t4600\n" - "RegisterExtensionInfo\t\t4700\n" - "RegisterMIMEInfo\t\t4900\n" - "RegisterProgIdInfo\t\t4800"; - static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" "s72\tS38\ts72\ti2\tS255\tS72\n" "Component\tComponent\n" @@ -71,7 +54,9 @@ static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttribu "One\t{783B242E-E185-4A56-AF86-C09815EC053C}\tMSITESTDIR\t2\t\tone.txt\n" "Three\t{010B6ADD-B27D-4EDD-9B3D-34C4F7D61684}\tCHANGEDDIR\t2\t\tthree.txt\n" "Two\t{BF03D1A6-20DA-4A65-82F3-6CAC995915CE}\tFIRSTDIR\t2\t\ttwo.txt\n" - "dangler\t{6091DF25-EF96-45F1-B8E9-A9B1420C7A3C}\tTARGETDIR\t4\t\tregdata"; + "dangler\t{6091DF25-EF96-45F1-B8E9-A9B1420C7A3C}\tTARGETDIR\t4\t\tregdata\n" + "component\t\tMSITESTDIR\t0\t1\tfile\n" + "service_comp\t\tMSITESTDIR\t0\t1\tservice_file"; static const CHAR directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n" "s72\tS72\tl255\n" @@ -91,7 +76,9 @@ static const CHAR feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\t "Four\t\tFour\tThe Four Feature\t4\t3\tCABOUTDIR\t0\n" "One\t\tOne\tThe One Feature\t1\t3\tMSITESTDIR\t0\n" "Three\t\tThree\tThe Three Feature\t3\t3\tCHANGEDDIR\t0\n" - "Two\t\tTwo\tThe Two Feature\t2\t3\tFIRSTDIR\t0"; + "Two\t\tTwo\tThe Two Feature\t2\t3\tFIRSTDIR\t0\n" + "feature\t\t\t\t2\t1\tTARGETDIR\t0\n" + "service_feature\t\t\t\t2\t1\tTARGETDIR\t0"; static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n" "s38\ts72\n" @@ -100,7 +87,9 @@ static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n" "Four\tFour\n" "One\tOne\n" "Three\tThree\n" - "Two\tTwo"; + "Two\tTwo\n" + "feature\tcomponent\n" + "service_feature\tservice_comp\n"; static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" @@ -109,7 +98,9 @@ static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tL "four.txt\tFour\tfour.txt\t1000\t\t\t16384\t4\n" "one.txt\tOne\tone.txt\t1000\t\t\t0\t1\n" "three.txt\tThree\tthree.txt\t1000\t\t\t0\t3\n" - "two.txt\tTwo\ttwo.txt\t1000\t\t\t0\t2"; + "two.txt\tTwo\ttwo.txt\t1000\t\t\t0\t2\n" + "file\tcomponent\tfilename\t100\t\t\t8192\t1\n" + "service_file\tservice_comp\tservice.exe\t100\t\t\t8192\t1"; static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n" "s72\tS255\tI2\n" @@ -118,12 +109,16 @@ static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n" "CostFinalize\t\t1000\n" "CostInitialize\t\t800\n" "FileCost\t\t900\n" + "ResolveSource\t\t950\n" + "MoveFiles\t\t1700\n" "InstallFiles\t\t4000\n" + "DuplicateFiles\t\t4500\n" + "InstallServices\t\t5000\n" "InstallFinalize\t\t6600\n" "InstallInitialize\t\t1500\n" "InstallValidate\t\t1400\n" "LaunchConditions\t\t100\n" - "WriteRegistryValues\t\t5000"; + "WriteRegistryValues\tSourceDir And SOURCEDIR\t5000"; static const CHAR media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" "i2\ti4\tL64\tS255\tS32\tS72\n" @@ -135,25 +130,461 @@ static const CHAR property_dat[] = "Property\tValue\n" "s72\tl0\n" "Property\tProperty\n" "DefaultUIFont\tDlgFont8\n" + "HASUIRUN\t0\n" "INSTALLLEVEL\t3\n" "InstallMode\tTypical\n" "Manufacturer\tWine\n" "PIDTemplate\t12345<###-%%%%%%%>@@@@@\n" - "ProductCode\t{F1C3AF50-8B56-4A69-A00C-00773FE42F30}\n" + "ProductCode\t{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}\n" "ProductID\tnone\n" "ProductLanguage\t1033\n" "ProductName\tMSITEST\n" "ProductVersion\t1.1.1\n" "PROMPTROLLBACKCOST\tP\n" "Setup\tSetup\n" - "UpgradeCode\t{CE067E8D-2E1A-4367-B734-4EB2BDAD6565}"; + "UpgradeCode\t{4C0EAA15-0264-4E5A-8758-609EF142B92D}\n" + "AdminProperties\tPOSTADMIN\n" + "ROOTDRIVE\tC:\\\n"; static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n" "s72\ti2\tl255\tL255\tL0\ts72\n" "Registry\tRegistry\n" "Apples\t2\tSOFTWARE\\Wine\\msitest\tName\timaname\tOne\n" "Oranges\t2\tSOFTWARE\\Wine\\msitest\tnumber\t#314\tTwo\n" - "regdata\t2\tSOFTWARE\\Wine\\msitest\tblah\tbad\tdangler"; + "regdata\t2\tSOFTWARE\\Wine\\msitest\tblah\tbad\tdangler\n" + "OrderTest\t2\tSOFTWARE\\Wine\\msitest\tOrderTestName\tOrderTestValue\tcomponent"; + +static const CHAR service_install_dat[] = "ServiceInstall\tName\tDisplayName\tServiceType\tStartType\tErrorControl\t" + "LoadOrderGroup\tDependencies\tStartName\tPassword\tArguments\tComponent_\tDescription\n" + "s72\ts255\tL255\ti4\ti4\ti4\tS255\tS255\tS255\tS255\tS255\ts72\tL255\n" + "ServiceInstall\tServiceInstall\n" + "TestService\tTestService\tTestService\t2\t3\t0\t\t\tTestService\t\t\tservice_comp\t\t"; + +static const CHAR service_control_dat[] = "ServiceControl\tName\tEvent\tArguments\tWait\tComponent_\n" + "s72\tl255\ti2\tL255\tI2\ts72\n" + "ServiceControl\tServiceControl\n" + "ServiceControl\tTestService\t8\t\t0\tservice_comp"; + +/* tables for test_continuouscabs */ +static const CHAR cc_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "maximus\t\tMSITESTDIR\t0\t1\tmaximus\n" + "augustus\t\tMSITESTDIR\t0\t1\taugustus\n" + "caesar\t\tMSITESTDIR\t0\t1\tcaesar\n"; + +static const CHAR cc_feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n" + "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n" + "Feature\tFeature\n" + "feature\t\t\t\t2\t1\tTARGETDIR\t0"; + +static const CHAR cc_feature_comp_dat[] = "Feature_\tComponent_\n" + "s38\ts72\n" + "FeatureComponents\tFeature_\tComponent_\n" + "feature\tmaximus\n" + "feature\taugustus\n" + "feature\tcaesar"; + +static const CHAR cc_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" + "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" + "File\tFile\n" + "maximus\tmaximus\tmaximus\t500\t\t\t16384\t1\n" + "augustus\taugustus\taugustus\t50000\t\t\t16384\t2\n" + "caesar\tcaesar\tcaesar\t500\t\t\t16384\t12"; + +static const CHAR cc_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t10\t\ttest1.cab\tDISK1\t\n" + "2\t2\t\ttest2.cab\tDISK2\t\n" + "3\t12\t\ttest3.cab\tDISK3\t\n"; + +static const CHAR co_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" + "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" + "File\tFile\n" + "maximus\tmaximus\tmaximus\t500\t\t\t16384\t1\n" + "augustus\taugustus\taugustus\t50000\t\t\t16384\t2\n" + "caesar\tcaesar\tcaesar\t500\t\t\t16384\t3"; + +static const CHAR co_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t10\t\ttest1.cab\tDISK1\t\n" + "2\t2\t\ttest2.cab\tDISK2\t\n" + "3\t3\t\ttest3.cab\tDISK3\t\n"; + +static const CHAR co2_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t10\t\ttest1.cab\tDISK1\t\n" + "2\t12\t\ttest3.cab\tDISK3\t\n" + "3\t2\t\ttest2.cab\tDISK2\t\n"; + +static const CHAR mm_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" + "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" + "File\tFile\n" + "maximus\tmaximus\tmaximus\t500\t\t\t512\t1\n" + "augustus\taugustus\taugustus\t500\t\t\t512\t2\n" + "caesar\tcaesar\tcaesar\t500\t\t\t16384\t3"; + +static const CHAR mm_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t3\t\ttest1.cab\tDISK1\t\n"; + +static const CHAR ss_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t2\t\ttest1.cab\tDISK1\t\n" + "2\t2\t\ttest2.cab\tDISK2\t\n" + "3\t12\t\ttest3.cab\tDISK3\t\n"; + +/* tables for test_uiLevelFlags */ +static const CHAR ui_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "maximus\t\tMSITESTDIR\t0\tHASUIRUN=1\tmaximus\n" + "augustus\t\tMSITESTDIR\t0\t1\taugustus\n" + "caesar\t\tMSITESTDIR\t0\t1\tcaesar\n"; + +static const CHAR ui_install_ui_seq_dat[] = "Action\tCondition\tSequence\n" + "s72\tS255\tI2\n" + "InstallUISequence\tAction\n" + "SetUIProperty\t\t5\n" + "ExecuteAction\t\t1100\n"; + +static const CHAR ui_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComments\n" + "s72\ti2\tS64\tS0\tS255\n" + "CustomAction\tAction\n" + "SetUIProperty\t51\tHASUIRUN\t1\t\n"; + +static const CHAR rof_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "maximus\t\tMSITESTDIR\t0\t1\tmaximus\n"; + +static const CHAR rof_feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n" + "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n" + "Feature\tFeature\n" + "feature\t\tFeature\tFeature\t2\t1\tTARGETDIR\t0\n" + "montecristo\t\tFeature\tFeature\t2\t1\tTARGETDIR\t0"; + +static const CHAR rof_feature_comp_dat[] = "Feature_\tComponent_\n" + "s38\ts72\n" + "FeatureComponents\tFeature_\tComponent_\n" + "feature\tmaximus\n" + "montecristo\tmaximus"; + +static const CHAR rof_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" + "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" + "File\tFile\n" + "maximus\tmaximus\tmaximus\t500\t\t\t8192\t1"; + +static const CHAR rof_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t1\t\t\tDISK1\t\n"; + +static const CHAR sdp_install_exec_seq_dat[] = "Action\tCondition\tSequence\n" + "s72\tS255\tI2\n" + "InstallExecuteSequence\tAction\n" + "AllocateRegistrySpace\tNOT Installed\t1550\n" + "CostFinalize\t\t1000\n" + "CostInitialize\t\t800\n" + "FileCost\t\t900\n" + "InstallFiles\t\t4000\n" + "InstallFinalize\t\t6600\n" + "InstallInitialize\t\t1500\n" + "InstallValidate\t\t1400\n" + "LaunchConditions\t\t100\n" + "SetDirProperty\t\t950"; + +static const CHAR sdp_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComments\n" + "s72\ti2\tS64\tS0\tS255\n" + "CustomAction\tAction\n" + "SetDirProperty\t51\tMSITESTDIR\t[CommonFilesFolder]msitest\\\t\n"; + +static const CHAR cie_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "maximus\t\tMSITESTDIR\t0\t1\tmaximus\n" + "augustus\t\tMSITESTDIR\t0\t1\taugustus\n" + "caesar\t\tMSITESTDIR\t0\t1\tcaesar\n" + "gaius\t\tMSITESTDIR\t0\t1\tgaius\n"; + +static const CHAR cie_feature_comp_dat[] = "Feature_\tComponent_\n" + "s38\ts72\n" + "FeatureComponents\tFeature_\tComponent_\n" + "feature\tmaximus\n" + "feature\taugustus\n" + "feature\tcaesar\n" + "feature\tgaius"; + +static const CHAR cie_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" + "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" + "File\tFile\n" + "maximus\tmaximus\tmaximus\t500\t\t\t16384\t1\n" + "augustus\taugustus\taugustus\t50000\t\t\t16384\t2\n" + "caesar\tcaesar\tcaesar\t500\t\t\t16384\t12\n" + "gaius\tgaius\tgaius\t500\t\t\t8192\t11"; + +static const CHAR cie_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t1\t\ttest1.cab\tDISK1\t\n" + "2\t2\t\ttest2.cab\tDISK2\t\n" + "3\t12\t\ttest3.cab\tDISK3\t\n"; + +static const CHAR ci_install_exec_seq_dat[] = "Action\tCondition\tSequence\n" + "s72\tS255\tI2\n" + "InstallExecuteSequence\tAction\n" + "CostFinalize\t\t1000\n" + "CostInitialize\t\t800\n" + "FileCost\t\t900\n" + "InstallFiles\t\t4000\n" + "InstallServices\t\t5000\n" + "InstallFinalize\t\t6600\n" + "InstallInitialize\t\t1500\n" + "RunInstall\t\t1600\n" + "InstallValidate\t\t1400\n" + "LaunchConditions\t\t100"; + +static const CHAR ci_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComments\n" + "s72\ti2\tS64\tS0\tS255\n" + "CustomAction\tAction\n" + "RunInstall\t23\tmsitest\\concurrent.msi\tMYPROP=[UILevel]\t\n"; + +static const CHAR ci_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "maximus\t{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}\tMSITESTDIR\t0\tUILevel=5\tmaximus\n"; + +static const CHAR ci2_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "augustus\t\tMSITESTDIR\t0\tUILevel=3 AND MYPROP=5\taugustus\n"; + +static const CHAR ci2_feature_comp_dat[] = "Feature_\tComponent_\n" + "s38\ts72\n" + "FeatureComponents\tFeature_\tComponent_\n" + "feature\taugustus"; + +static const CHAR ci2_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" + "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" + "File\tFile\n" + "augustus\taugustus\taugustus\t500\t\t\t8192\t1"; + +static const CHAR spf_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComments\n" + "s72\ti2\tS64\tS0\tS255\n" + "CustomAction\tAction\n" + "SetFolderProp\t51\tMSITESTDIR\t[ProgramFilesFolder]\\msitest\\added\t\n"; + +static const CHAR spf_install_exec_seq_dat[] = "Action\tCondition\tSequence\n" + "s72\tS255\tI2\n" + "InstallExecuteSequence\tAction\n" + "CostFinalize\t\t1000\n" + "CostInitialize\t\t800\n" + "FileCost\t\t900\n" + "SetFolderProp\t\t950\n" + "InstallFiles\t\t4000\n" + "InstallServices\t\t5000\n" + "InstallFinalize\t\t6600\n" + "InstallInitialize\t\t1500\n" + "InstallValidate\t\t1400\n" + "LaunchConditions\t\t100"; + +static const CHAR spf_install_ui_seq_dat[] = "Action\tCondition\tSequence\n" + "s72\tS255\tI2\n" + "InstallUISequence\tAction\n" + "CostInitialize\t\t800\n" + "FileCost\t\t900\n" + "CostFinalize\t\t1000\n" + "ExecuteAction\t\t1100\n"; + +static const CHAR pp_install_exec_seq_dat[] = "Action\tCondition\tSequence\n" + "s72\tS255\tI2\n" + "InstallExecuteSequence\tAction\n" + "ValidateProductID\t\t700\n" + "CostInitialize\t\t800\n" + "FileCost\t\t900\n" + "CostFinalize\t\t1000\n" + "InstallValidate\t\t1400\n" + "InstallInitialize\t\t1500\n" + "ProcessComponents\tPROCESS_COMPONENTS=1 Or FULL=1\t1600\n" + "UnpublishFeatures\tUNPUBLISH_FEATURES=1 Or FULL=1\t1800\n" + "RemoveFiles\t\t3500\n" + "InstallFiles\t\t4000\n" + "RegisterUser\tREGISTER_USER=1 Or FULL=1\t6000\n" + "RegisterProduct\tREGISTER_PRODUCT=1 Or FULL=1\t6100\n" + "PublishFeatures\tPUBLISH_FEATURES=1 Or FULL=1\t6300\n" + "PublishProduct\tPUBLISH_PRODUCT=1 Or FULL=1\t6400\n" + "InstallFinalize\t\t6600"; + +static const CHAR tp_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "augustus\t\tMSITESTDIR\t0\tprop=\"val\"\taugustus\n"; + +static const CHAR cwd_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "augustus\t\tMSITESTDIR\t0\t\taugustus\n"; + +static const CHAR adm_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "augustus\t\tMSITESTDIR\t0\tPOSTADMIN=1\taugustus"; + +static const CHAR adm_custom_action_dat[] = "Action\tType\tSource\tTarget\tISComments\n" + "s72\ti2\tS64\tS0\tS255\n" + "CustomAction\tAction\n" + "SetPOSTADMIN\t51\tPOSTADMIN\t1\t\n"; + +static const CHAR adm_admin_exec_seq_dat[] = "Action\tCondition\tSequence\n" + "s72\tS255\tI2\n" + "AdminExecuteSequence\tAction\n" + "CostFinalize\t\t1000\n" + "CostInitialize\t\t800\n" + "FileCost\t\t900\n" + "SetPOSTADMIN\t\t950\n" + "InstallFiles\t\t4000\n" + "InstallFinalize\t\t6600\n" + "InstallInitialize\t\t1500\n" + "InstallValidate\t\t1400\n" + "LaunchConditions\t\t100"; + +static const CHAR amp_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "augustus\t\tMSITESTDIR\t0\tMYPROP=2718\taugustus\n"; + +static const CHAR rem_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "hydrogen\t{C844BD1E-1907-4C00-8BC9-150BD70DF0A1}\tMSITESTDIR\t0\t\thydrogen\n" + "helium\t{5AD3C142-CEF8-490D-B569-784D80670685}\tMSITESTDIR\t1\t\thelium\n" + "lithium\t\tMSITESTDIR\t2\t\tlithium\n"; + +static const CHAR rem_feature_comp_dat[] = "Feature_\tComponent_\n" + "s38\ts72\n" + "FeatureComponents\tFeature_\tComponent_\n" + "feature\thydrogen\n" + "feature\thelium\n" + "feature\tlithium"; + +static const CHAR rem_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" + "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" + "File\tFile\n" + "hydrogen\thydrogen\thydrogen\t0\t\t\t8192\t1\n" + "helium\thelium\thelium\t0\t\t\t8192\t1\n" + "lithium\tlithium\tlithium\t0\t\t\t8192\t1"; + +static const CHAR rem_install_exec_seq_dat[] = "Action\tCondition\tSequence\n" + "s72\tS255\tI2\n" + "InstallExecuteSequence\tAction\n" + "ValidateProductID\t\t700\n" + "CostInitialize\t\t800\n" + "FileCost\t\t900\n" + "CostFinalize\t\t1000\n" + "InstallValidate\t\t1400\n" + "InstallInitialize\t\t1500\n" + "ProcessComponents\t\t1600\n" + "UnpublishFeatures\t\t1800\n" + "RemoveFiles\t\t3500\n" + "InstallFiles\t\t4000\n" + "RegisterProduct\t\t6100\n" + "PublishFeatures\t\t6300\n" + "PublishProduct\t\t6400\n" + "InstallFinalize\t\t6600"; + +static const CHAR rem_remove_files_dat[] = "FileKey\tComponent_\tFileName\tDirProperty\tInstallMode\n" + "s72\ts72\tS255\ts72\tI2\n" + "RemoveFile\tFileKey\n" + "furlong\thydrogen\tfurlong\tMSITESTDIR\t1\n" + "firkin\thelium\tfirkin\tMSITESTDIR\t1\n" + "fortnight\tlithium\tfortnight\tMSITESTDIR\t1\n" + "becquerel\thydrogen\tbecquerel\tMSITESTDIR\t2\n" + "dioptre\thelium\tdioptre\tMSITESTDIR\t2\n" + "attoparsec\tlithium\tattoparsec\tMSITESTDIR\t2\n" + "storeys\thydrogen\tstoreys\tMSITESTDIR\t3\n" + "block\thelium\tblock\tMSITESTDIR\t3\n" + "siriometer\tlithium\tsiriometer\tMSITESTDIR\t3\n"; + +static const CHAR mov_move_file_dat[] = "FileKey\tComponent_\tSourceName\tDestName\tSourceFolder\tDestFolder\tOptions\n" + "s72\ts72\tS255\tS255\tS72\ts72\ti2\n" + "MoveFile\tFileKey\n" + "abkhazia\taugustus\tnonexistent\tdest\tSourceDir\tMSITESTDIR\t0\n" + "bahamas\taugustus\tnonexistent\tdest\tSourceDir\tMSITESTDIR\t1\n" + "cambodia\taugustus\tcameroon\tcanada\tSourceDir\tMSITESTDIR\t0\n" + "denmark\taugustus\tdjibouti\tdominica\tSourceDir\tMSITESTDIR\t1\n" + "ecuador\taugustus\tegypt\telsalvador\tNotAProp\tMSITESTDIR\t1\n" + "fiji\taugustus\tfinland\tfrance\tSourceDir\tNotAProp\t1\n" + "gabon\taugustus\tgambia\tgeorgia\tSOURCEFULL\tMSITESTDIR\t1\n" + "haiti\taugustus\thonduras\thungary\tSourceDir\tDESTFULL\t1\n" + "iceland\taugustus\tindia\tindonesia\tMSITESTDIR\tMSITESTDIR\t1\n" + "jamaica\taugustus\tjapan\tjordan\tFILEPATHBAD\tMSITESTDIR\t1\n" + "kazakhstan\taugustus\t\tkiribati\tFILEPATHGOOD\tMSITESTDIR\t1\n" + "laos\taugustus\tlatvia\tlebanon\tSourceDir\tMSITESTDIR\t1\n" + "namibia\taugustus\tnauru\tkiribati\tSourceDir\tMSITESTDIR\t1\n" + "wildcard\taugustus\tapp*\twildcard\tSourceDir\tMSITESTDIR\t1\n" + "single\taugustus\tf?o\tsingle\tSourceDir\tMSITESTDIR\t1\n" + "wildcardnodest\taugustus\tbudd*\t\tSourceDir\tMSITESTDIR\t1\n" + "singlenodest\taugustus\tb?r\t\tSourceDir\tMSITESTDIR\t1\n"; + +static const CHAR mc_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "maximus\t\tMSITESTDIR\t0\t1\tmaximus\n" + "augustus\t\tMSITESTDIR\t0\t1\taugustus\n" + "caesar\t\tMSITESTDIR\t0\t1\tcaesar\n" + "gaius\t\tMSITESTDIR\t0\tGAIUS=1\tgaius\n"; + +static const CHAR mc_file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" + "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" + "File\tFile\n" + "maximus\tmaximus\tmaximus\t500\t\t\t16384\t1\n" + "augustus\taugustus\taugustus\t500\t\t\t0\t2\n" + "caesar\tcaesar\tcaesar\t500\t\t\t16384\t3\n" + "gaius\tgaius\tgaius\t500\t\t\t16384\t4"; + +static const CHAR mc_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t1\t\ttest1.cab\tDISK1\t\n" + "2\t2\t\ttest2.cab\tDISK2\t\n" + "3\t3\t\ttest3.cab\tDISK3\t\n" + "4\t4\t\ttest3.cab\tDISK3\t\n"; + +static const CHAR mc_file_hash_dat[] = "File_\tOptions\tHashPart1\tHashPart2\tHashPart3\tHashPart4\n" + "s72\ti2\ti4\ti4\ti4\ti4\n" + "MsiFileHash\tFile_\n" + "caesar\t0\t850433704\t-241429251\t675791761\t-1221108824"; + +static const CHAR df_directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n" + "s72\tS72\tl255\n" + "Directory\tDirectory\n" + "THIS\tMSITESTDIR\tthis\n" + "DOESNOT\tTHIS\tdoesnot\n" + "NONEXISTENT\tDOESNOT\texist\n" + "MSITESTDIR\tProgramFilesFolder\tmsitest\n" + "ProgramFilesFolder\tTARGETDIR\t.\n" + "TARGETDIR\t\tSourceDir"; + +static const CHAR df_duplicate_file_dat[] = "FileKey\tComponent_\tFile_\tDestName\tDestFolder\n" + "s72\ts72\ts72\tS255\tS72\n" + "DuplicateFile\tFileKey\n" + "maximus\tmaximus\tmaximus\taugustus\t\n" + "caesar\tmaximus\tmaximus\t\tNONEXISTENT\n"; + +static const CHAR wrv_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "augustus\t\tMSITESTDIR\t0\t\taugustus\n"; + +static const CHAR wrv_registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n" + "s72\ti2\tl255\tL255\tL0\ts72\n" + "Registry\tRegistry\n" + "regdata\t2\tSOFTWARE\\Wine\\msitest\tValue\t[~]one[~]two[~]three\taugustus"; typedef struct _msi_table { @@ -166,8 +597,6 @@ typedef struct _msi_table static const msi_table tables[] = { - ADD_TABLE(admin_exec_seq), - ADD_TABLE(advt_exec_seq), ADD_TABLE(component), ADD_TABLE(directory), ADD_TABLE(feature), @@ -176,44 +605,294 @@ static const msi_table tables[] = ADD_TABLE(install_exec_seq), ADD_TABLE(media), ADD_TABLE(property), - ADD_TABLE(registry) + ADD_TABLE(registry), + ADD_TABLE(service_install), + ADD_TABLE(service_control) +}; + +static const msi_table cc_tables[] = +{ + ADD_TABLE(cc_component), + ADD_TABLE(directory), + ADD_TABLE(cc_feature), + ADD_TABLE(cc_feature_comp), + ADD_TABLE(cc_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(cc_media), + ADD_TABLE(property), +}; + +static const msi_table co_tables[] = +{ + ADD_TABLE(cc_component), + ADD_TABLE(directory), + ADD_TABLE(cc_feature), + ADD_TABLE(cc_feature_comp), + ADD_TABLE(co_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(co_media), + ADD_TABLE(property), +}; + +static const msi_table co2_tables[] = +{ + ADD_TABLE(cc_component), + ADD_TABLE(directory), + ADD_TABLE(cc_feature), + ADD_TABLE(cc_feature_comp), + ADD_TABLE(cc_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(co2_media), + ADD_TABLE(property), +}; + +static const msi_table mm_tables[] = +{ + ADD_TABLE(cc_component), + ADD_TABLE(directory), + ADD_TABLE(cc_feature), + ADD_TABLE(cc_feature_comp), + ADD_TABLE(mm_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(mm_media), + ADD_TABLE(property), +}; + +static const msi_table ss_tables[] = +{ + ADD_TABLE(cc_component), + ADD_TABLE(directory), + ADD_TABLE(cc_feature), + ADD_TABLE(cc_feature_comp), + ADD_TABLE(cc_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(ss_media), + ADD_TABLE(property), +}; + +static const msi_table ui_tables[] = +{ + ADD_TABLE(ui_component), + ADD_TABLE(directory), + ADD_TABLE(cc_feature), + ADD_TABLE(cc_feature_comp), + ADD_TABLE(cc_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(ui_install_ui_seq), + ADD_TABLE(ui_custom_action), + ADD_TABLE(cc_media), + ADD_TABLE(property), +}; + +static const msi_table rof_tables[] = +{ + ADD_TABLE(rof_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(rof_feature_comp), + ADD_TABLE(rof_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), +}; + +static const msi_table sdp_tables[] = +{ + ADD_TABLE(rof_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(rof_feature_comp), + ADD_TABLE(rof_file), + ADD_TABLE(sdp_install_exec_seq), + ADD_TABLE(sdp_custom_action), + ADD_TABLE(rof_media), + ADD_TABLE(property), +}; + +static const msi_table cie_tables[] = +{ + ADD_TABLE(cie_component), + ADD_TABLE(directory), + ADD_TABLE(cc_feature), + ADD_TABLE(cie_feature_comp), + ADD_TABLE(cie_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(cie_media), + ADD_TABLE(property), +}; + +static const msi_table ci_tables[] = +{ + ADD_TABLE(ci_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(rof_feature_comp), + ADD_TABLE(rof_file), + ADD_TABLE(ci_install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), + ADD_TABLE(ci_custom_action), +}; + +static const msi_table ci2_tables[] = +{ + ADD_TABLE(ci2_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(ci2_feature_comp), + ADD_TABLE(ci2_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), +}; + +static const msi_table spf_tables[] = +{ + ADD_TABLE(ci_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(rof_feature_comp), + ADD_TABLE(rof_file), + ADD_TABLE(spf_install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), + ADD_TABLE(spf_custom_action), + ADD_TABLE(spf_install_ui_seq), +}; + +static const msi_table pp_tables[] = +{ + ADD_TABLE(ci_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(rof_feature_comp), + ADD_TABLE(rof_file), + ADD_TABLE(pp_install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), +}; + +static const msi_table tp_tables[] = +{ + ADD_TABLE(tp_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(ci2_feature_comp), + ADD_TABLE(ci2_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), +}; + +static const msi_table cwd_tables[] = +{ + ADD_TABLE(cwd_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(ci2_feature_comp), + ADD_TABLE(ci2_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), +}; + +static const msi_table adm_tables[] = +{ + ADD_TABLE(adm_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(ci2_feature_comp), + ADD_TABLE(ci2_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), + ADD_TABLE(adm_custom_action), + ADD_TABLE(adm_admin_exec_seq), +}; + +static const msi_table amp_tables[] = +{ + ADD_TABLE(amp_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(ci2_feature_comp), + ADD_TABLE(ci2_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), +}; + +static const msi_table rem_tables[] = +{ + ADD_TABLE(rem_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(rem_feature_comp), + ADD_TABLE(rem_file), + ADD_TABLE(rem_install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), + ADD_TABLE(rem_remove_files), +}; + +static const msi_table mov_tables[] = +{ + ADD_TABLE(cwd_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(ci2_feature_comp), + ADD_TABLE(ci2_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), + ADD_TABLE(mov_move_file), +}; + +static const msi_table mc_tables[] = +{ + ADD_TABLE(mc_component), + ADD_TABLE(directory), + ADD_TABLE(cc_feature), + ADD_TABLE(cie_feature_comp), + ADD_TABLE(mc_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(mc_media), + ADD_TABLE(property), + ADD_TABLE(mc_file_hash), +}; + +static const msi_table df_tables[] = +{ + ADD_TABLE(rof_component), + ADD_TABLE(df_directory), + ADD_TABLE(rof_feature), + ADD_TABLE(rof_feature_comp), + ADD_TABLE(rof_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), + ADD_TABLE(df_duplicate_file), +}; + +static const msi_table wrv_tables[] = +{ + ADD_TABLE(wrv_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(ci2_feature_comp), + ADD_TABLE(ci2_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), + ADD_TABLE(wrv_registry), }; /* cabinet definitions */ /* make the max size large so there is only one cab file */ -#define MEDIA_SIZE 999999999 +#define MEDIA_SIZE 0x7FFFFFFF #define FOLDER_THRESHOLD 900000 -/* The following defintions were copied from dlls/cabinet/cabinet.h - * because they are undocumented in windows. - */ - -/* EXTRACTdest flags */ -#define EXTRACT_FILLFILELIST 0x00000001 -#define EXTRACT_EXTRACTFILES 0x00000002 - -struct ExtractFileList { - LPSTR filename; - struct ExtractFileList *next; - BOOL unknown; /* always 1L */ -}; - -/* the first parameter of the function extract */ -typedef struct { - long result1; /* 0x000 */ - long unknown1[3]; /* 0x004 */ - struct ExtractFileList *filelist; /* 0x010 */ - long filecount; /* 0x014 */ - long flags; /* 0x018 */ - char directory[0x104]; /* 0x01c */ - char lastfile[0x20c]; /* 0x120 */ -} EXTRACTDEST; - -/* cabinet function pointers */ -HMODULE hCabinet; -static HRESULT (WINAPI *pExtract)(EXTRACTDEST*, LPCSTR); - /* the FCI callbacks */ static void *mem_alloc(ULONG cb) @@ -228,6 +907,7 @@ static void mem_free(void *memory) static BOOL get_next_cabinet(PCCAB pccab, ULONG cbPrevCab, void *pv) { + sprintf(pccab->szCab, pv, pccab->iCab); return TRUE; } @@ -248,9 +928,10 @@ static INT_PTR fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv) DWORD dwAccess = 0; DWORD dwShareMode = 0; DWORD dwCreateDisposition = OPEN_EXISTING; - + dwAccess = GENERIC_READ | GENERIC_WRITE; - dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */ + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES) dwCreateDisposition = OPEN_EXISTING; @@ -270,7 +951,7 @@ static UINT fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv) HANDLE handle = (HANDLE)hf; DWORD dwRead; BOOL res; - + res = ReadFile(handle, memory, cb, &dwRead, NULL); ok(res, "Failed to ReadFile\n"); @@ -301,7 +982,7 @@ static long fci_seek(INT_PTR hf, long dist, int seektype, int *err, void *pv) { HANDLE handle = (HANDLE)hf; DWORD ret; - + ret = SetFilePointer(handle, dist, NULL, seektype); ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n"); @@ -316,6 +997,21 @@ static int fci_delete(char *pszFile, int *err, void *pv) return 0; } +static void init_functionpointers(void) +{ + HMODULE hmsi = GetModuleHandleA("msi.dll"); + +#define GET_PROC(func) \ + p ## func = (void*)GetProcAddress(hmsi, #func); \ + if(!p ## func) \ + trace("GetProcAddress(%s) failed\n", #func); + + GET_PROC(MsiQueryComponentStateA); + GET_PROC(MsiSourceListGetInfoA); + +#undef GET_PROC +} + static BOOL check_record(MSIHANDLE rec, UINT field, LPCSTR val) { CHAR buffer[0x20]; @@ -362,7 +1058,7 @@ static INT_PTR get_open_info(char *pszName, USHORT *pdate, USHORT *ptime, res = GetFileInformationByHandle(handle, &finfo); ok(res, "Expected GetFileInformationByHandle to succeed\n"); - + FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime); FileTimeToDosDateTime(&filetime, pdate, ptime); @@ -372,42 +1068,43 @@ static INT_PTR get_open_info(char *pszName, USHORT *pdate, USHORT *ptime, return (INT_PTR)handle; } -static void add_file(HFCI hfci, char *file) +static BOOL add_file(HFCI hfci, const char *file, TCOMP compress) { char path[MAX_PATH]; - BOOL res; + char filename[MAX_PATH]; lstrcpyA(path, CURR_DIR); lstrcatA(path, "\\"); lstrcatA(path, file); - res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress, - get_open_info, tcompTYPE_MSZIP); - ok(res, "Expected FCIAddFile to succeed\n"); + lstrcpyA(filename, file); + + return FCIAddFile(hfci, path, filename, FALSE, get_next_cabinet, + progress, get_open_info, compress); } -static void set_cab_parameters(PCCAB pCabParams, const CHAR *name) +static void set_cab_parameters(PCCAB pCabParams, const CHAR *name, DWORD max_size) { ZeroMemory(pCabParams, sizeof(CCAB)); - pCabParams->cb = MEDIA_SIZE; + pCabParams->cb = max_size; pCabParams->cbFolderThresh = FOLDER_THRESHOLD; pCabParams->setID = 0xbeef; + pCabParams->iCab = 1; lstrcpyA(pCabParams->szCabPath, CURR_DIR); lstrcatA(pCabParams->szCabPath, "\\"); lstrcpyA(pCabParams->szCab, name); } -static void create_cab_file(const CHAR *name) +static void create_cab_file(const CHAR *name, DWORD max_size, const CHAR *files) { CCAB cabParams; + LPCSTR ptr; HFCI hfci; ERF erf; - static CHAR four_txt[] = "four.txt", - five_txt[] = "five.txt"; BOOL res; - set_cab_parameters(&cabParams, name); + set_cab_parameters(&cabParams, name, max_size); hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open, fci_read, fci_write, fci_close, fci_seek, fci_delete, @@ -415,8 +1112,13 @@ static void create_cab_file(const CHAR *name) ok(hfci != NULL, "Failed to create an FCI context\n"); - add_file(hfci, four_txt); - add_file(hfci, five_txt); + ptr = files; + while (*ptr) + { + res = add_file(hfci, ptr, tcompTYPE_MSZIP); + ok(res, "Failed to add file: %s\n", ptr); + ptr += lstrlen(ptr) + 1; + } res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress); ok(res, "Failed to flush the cabinet\n"); @@ -425,73 +1127,66 @@ static void create_cab_file(const CHAR *name) ok(res, "Failed to destroy the cabinet\n"); } -static BOOL init_function_pointers(void) -{ - hCabinet = LoadLibraryA("cabinet.dll"); - if (!hCabinet) - return FALSE; - - pExtract = (void *)GetProcAddress(hCabinet, "Extract"); - if (!pExtract) - return FALSE; - - return TRUE; -} - -static BOOL get_program_files_dir(LPSTR buf) +static BOOL get_program_files_dir(LPSTR buf, LPSTR buf2) { HKEY hkey; - CHAR temp[MAX_PATH]; - DWORD type = REG_EXPAND_SZ, size; + DWORD type, size; if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey)) return FALSE; size = MAX_PATH; - if (RegQueryValueEx(hkey, "ProgramFilesPath", 0, &type, (LPBYTE)temp, &size)) + if (RegQueryValueExA(hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size)) { + RegCloseKey(hkey); return FALSE; + } - ExpandEnvironmentStrings(temp, buf, MAX_PATH); + size = MAX_PATH; + if (RegQueryValueExA(hkey, "CommonFilesDir", 0, &type, (LPBYTE)buf2, &size)) { + RegCloseKey(hkey); + return FALSE; + } RegCloseKey(hkey); return TRUE; } -static void create_file(const CHAR *name) +static void create_file_data(LPCSTR name, LPCSTR data, DWORD size) { HANDLE file; DWORD written; file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name); - WriteFile(file, name, strlen(name), &written, NULL); - WriteFile(file, "\n", strlen("\n"), &written, NULL); + WriteFile(file, data, strlen(data), &written, NULL); + + if (size) + { + SetFilePointer(file, size, NULL, FILE_BEGIN); + SetEndOfFile(file); + } + CloseHandle(file); } +#define create_file(name, size) create_file_data(name, name, size) + static void create_test_files(void) { - int len; - - GetCurrentDirectoryA(MAX_PATH, CURR_DIR); - len = lstrlenA(CURR_DIR); - - if(len && (CURR_DIR[len-1] == '\\')) - CURR_DIR[len - 1] = 0; - - get_program_files_dir(PROG_FILES_DIR); - CreateDirectoryA("msitest", NULL); - create_file("msitest\\one.txt"); + create_file("msitest\\one.txt", 100); CreateDirectoryA("msitest\\first", NULL); - create_file("msitest\\first\\two.txt"); + create_file("msitest\\first\\two.txt", 100); CreateDirectoryA("msitest\\second", NULL); - create_file("msitest\\second\\three.txt"); + create_file("msitest\\second\\three.txt", 100); - create_file("four.txt"); - create_file("five.txt"); - create_cab_file("msitest.cab"); + create_file("four.txt", 100); + create_file("five.txt", 100); + create_cab_file("msitest.cab", MEDIA_SIZE, "four.txt\0five.txt\0"); + + create_file("msitest\\filename", 100); + create_file("msitest\\service.exe", 100); DeleteFileA("four.txt"); DeleteFileA("five.txt"); @@ -511,6 +1206,20 @@ static BOOL delete_pf(const CHAR *rel_path, BOOL is_file) return RemoveDirectoryA(path); } +static BOOL delete_cf(const CHAR *rel_path, BOOL is_file) +{ + CHAR path[MAX_PATH]; + + lstrcpyA(path, COMMON_FILES_DIR); + lstrcatA(path, "\\"); + lstrcatA(path, rel_path); + + if (is_file) + return DeleteFileA(path); + else + return RemoveDirectoryA(path); +} + static void delete_test_files(void) { DeleteFileA("msitest.msi"); @@ -518,6 +1227,8 @@ static void delete_test_files(void) DeleteFileA("msitest\\second\\three.txt"); DeleteFileA("msitest\\first\\two.txt"); DeleteFileA("msitest\\one.txt"); + DeleteFileA("msitest\\service.exe"); + DeleteFileA("msitest\\filename"); RemoveDirectoryA("msitest\\second"); RemoveDirectoryA("msitest\\first"); RemoveDirectoryA("msitest"); @@ -539,7 +1250,7 @@ static void write_msi_summary_info(MSIHANDLE db) MSIHANDLE summary; UINT r; - r = MsiGetSummaryInformationA(db, NULL, 4, &summary); + r = MsiGetSummaryInformationA(db, NULL, 5, &summary); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); r = MsiSummaryInfoSetPropertyA(summary, PID_TEMPLATE, VT_LPSTR, 0, NULL, ";1033"); @@ -555,6 +1266,9 @@ static void write_msi_summary_info(MSIHANDLE db) r = MsiSummaryInfoSetPropertyA(summary, PID_WORDCOUNT, VT_I4, 0, NULL, NULL); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + r = MsiSummaryInfoSetPropertyA(summary, PID_TITLE, VT_LPSTR, 0, NULL, "MSITEST"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + /* write the summary changes back to the stream */ r = MsiSummaryInfoPersist(summary); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); @@ -579,10 +1293,7 @@ static void create_database(const CHAR *name, const msi_table *tables, int num_t write_file(table->filename, table->data, (table->size - 1) * sizeof(char)); r = MsiDatabaseImportA(db, CURR_DIR, table->filename); - todo_wine - { - ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - } + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); DeleteFileA(table->filename); } @@ -595,6 +1306,24 @@ static void create_database(const CHAR *name, const msi_table *tables, int num_t MsiCloseHandle(db); } +static void check_service_is_installed(void) +{ + SC_HANDLE scm, service; + BOOL res; + + scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + ok(scm != NULL, "Failed to open the SC Manager\n"); + + service = OpenService(scm, "TestService", SC_MANAGER_ALL_ACCESS); + ok(service != NULL, "Failed to open TestService\n"); + + res = DeleteService(service); + ok(res, "Failed to delete TestService\n"); + + CloseServiceHandle(service); + CloseServiceHandle(scm); +} + static void test_MsiInstallProduct(void) { UINT r; @@ -603,67 +1332,67 @@ static void test_MsiInstallProduct(void) HKEY hkey; DWORD num, size, type; - r = MsiInstallProductA(msifile, NULL); - todo_wine - { - ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - } + create_test_files(); + create_database(msifile, tables, sizeof(tables) / sizeof(msi_table)); - todo_wine - { - ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n"); - ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n"); - ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n"); - ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n"); - ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n"); - ok(delete_pf("msitest\\changed", FALSE), "File not installed\n"); - ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n"); - ok(delete_pf("msitest\\first", FALSE), "File not installed\n"); - ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n"); - ok(delete_pf("msitest", FALSE), "File not installed\n"); - } + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n"); + ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n"); + ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\changed", FALSE), "File not installed\n"); + ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\first", FALSE), "File not installed\n"); + ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\filename", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\service.exe", TRUE), "File not installed\n"); + ok(delete_pf("msitest", FALSE), "File not installed\n"); res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey); - todo_wine - { - ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res); - } + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); size = MAX_PATH; type = REG_SZ; res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size); - todo_wine - { - ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res); - ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path); - } + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path); size = MAX_PATH; type = REG_SZ; res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size); - todo_wine - { - ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %ld\n", res); - } + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); size = sizeof(num); type = REG_DWORD; res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size); - todo_wine - { - ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res); - ok(num == 314, "Expected 314, got %ld\n", num); - } + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(num == 314, "Expected 314, got %d\n", num); + + size = MAX_PATH; + type = REG_SZ; + res = RegQueryValueExA(hkey, "OrderTestName", NULL, &type, (LPBYTE)path, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(path, "OrderTestValue"), "Expected imaname, got %s\n", path); + + check_service_is_installed(); RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest"); + + delete_test_files(); } static void test_MsiSetComponentState(void) { + INSTALLSTATE installed, action; MSIHANDLE package; char path[MAX_PATH]; UINT r; + create_database(msifile, tables, sizeof(tables) / sizeof(msi_table)); + CoInitialize(NULL); lstrcpy(path, CURR_DIR); @@ -682,14 +1411,18 @@ static void test_MsiSetComponentState(void) r = MsiDoAction(package, "CostFinalize"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + r = MsiGetComponentState(package, "dangler", &installed, &action); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(installed == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", installed); + ok(action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + r = MsiSetComponentState(package, "dangler", INSTALLSTATE_SOURCE); - todo_wine - { - ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - } + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); MsiCloseHandle(package); CoUninitialize(); + + DeleteFileA(msifile); } static void test_packagecoltypes(void) @@ -699,6 +1432,8 @@ static void test_packagecoltypes(void) LPCSTR query; UINT r, count; + create_database(msifile, tables, sizeof(tables) / sizeof(msi_table)); + CoInitialize(NULL); lstrcpy(path, CURR_DIR); @@ -710,54 +1445,2289 @@ static void test_packagecoltypes(void) query = "SELECT * FROM `Media`"; r = MsiDatabaseOpenView( hdb, query, &view ); - todo_wine - { - ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); - } + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); r = MsiViewGetColumnInfo( view, MSICOLINFO_NAMES, &rec ); count = MsiRecordGetFieldCount( rec ); - todo_wine - { - ok(r == ERROR_SUCCESS, "MsiViewGetColumnInfo failed\n"); - ok(count == 6, "Expected 6, got %d\n", count); - ok(check_record(rec, 1, "DiskId"), "wrong column label\n"); - ok(check_record(rec, 2, "LastSequence"), "wrong column label\n"); - ok(check_record(rec, 3, "DiskPrompt"), "wrong column label\n"); - ok(check_record(rec, 4, "Cabinet"), "wrong column label\n"); - ok(check_record(rec, 5, "VolumeLabel"), "wrong column label\n"); - ok(check_record(rec, 6, "Source"), "wrong column label\n"); - } + ok(r == ERROR_SUCCESS, "MsiViewGetColumnInfo failed\n"); + ok(count == 6, "Expected 6, got %d\n", count); + ok(check_record(rec, 1, "DiskId"), "wrong column label\n"); + ok(check_record(rec, 2, "LastSequence"), "wrong column label\n"); + ok(check_record(rec, 3, "DiskPrompt"), "wrong column label\n"); + ok(check_record(rec, 4, "Cabinet"), "wrong column label\n"); + ok(check_record(rec, 5, "VolumeLabel"), "wrong column label\n"); + ok(check_record(rec, 6, "Source"), "wrong column label\n"); + MsiCloseHandle(rec); r = MsiViewGetColumnInfo( view, MSICOLINFO_TYPES, &rec ); count = MsiRecordGetFieldCount( rec ); - todo_wine - { - ok(r == ERROR_SUCCESS, "MsiViewGetColumnInfo failed\n"); - ok(count == 6, "Expected 6, got %d\n", count); - ok(check_record(rec, 1, "i2"), "wrong column label\n"); - ok(check_record(rec, 2, "i4"), "wrong column label\n"); - ok(check_record(rec, 3, "L64"), "wrong column label\n"); - ok(check_record(rec, 4, "S255"), "wrong column label\n"); - ok(check_record(rec, 5, "S32"), "wrong column label\n"); - ok(check_record(rec, 6, "S72"), "wrong column label\n"); - } + ok(r == ERROR_SUCCESS, "MsiViewGetColumnInfo failed\n"); + ok(count == 6, "Expected 6, got %d\n", count); + ok(check_record(rec, 1, "i2"), "wrong column label\n"); + ok(check_record(rec, 2, "i4"), "wrong column label\n"); + ok(check_record(rec, 3, "L64"), "wrong column label\n"); + ok(check_record(rec, 4, "S255"), "wrong column label\n"); + ok(check_record(rec, 5, "S32"), "wrong column label\n"); + ok(check_record(rec, 6, "S72"), "wrong column label\n"); + MsiCloseHandle(rec); + MsiCloseHandle(view); MsiCloseHandle(hdb); DeleteFile(msifile); } -START_TEST(install) +static void create_cc_test_files(void) { - if (!init_function_pointers()) + CCAB cabParams; + HFCI hfci; + ERF erf; + static CHAR cab_context[] = "test%d.cab"; + BOOL res; + + create_file("maximus", 500); + create_file("augustus", 50000); + create_file("caesar", 500); + + set_cab_parameters(&cabParams, "test1.cab", 200); + + hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open, + fci_read, fci_write, fci_close, fci_seek, fci_delete, + get_temp_file, &cabParams, cab_context); + ok(hfci != NULL, "Failed to create an FCI context\n"); + + res = add_file(hfci, "maximus", tcompTYPE_MSZIP); + ok(res, "Failed to add file maximus\n"); + + res = add_file(hfci, "augustus", tcompTYPE_MSZIP); + ok(res, "Failed to add file augustus\n"); + + res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress); + ok(res, "Failed to flush the cabinet\n"); + + res = FCIDestroy(hfci); + ok(res, "Failed to destroy the cabinet\n"); + + create_cab_file("test3.cab", MEDIA_SIZE, "caesar\0"); + + DeleteFile("maximus"); + DeleteFile("augustus"); + DeleteFile("caesar"); +} + +static void delete_cab_files(void) +{ + SHFILEOPSTRUCT shfl; + CHAR path[MAX_PATH+10]; + + lstrcpyA(path, CURR_DIR); + lstrcatA(path, "\\*.cab"); + path[strlen(path) + 1] = '\0'; + + shfl.hwnd = NULL; + shfl.wFunc = FO_DELETE; + shfl.pFrom = path; + shfl.pTo = NULL; + shfl.fFlags = FOF_FILESONLY | FOF_NOCONFIRMATION | FOF_NORECURSION | FOF_SILENT; + + SHFileOperation(&shfl); +} + +static void test_continuouscabs(void) +{ + UINT r; + + create_cc_test_files(); + create_database(msifile, cc_tables, sizeof(cc_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + r = MsiInstallProductA(msifile, NULL); + ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n"); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n"); + } + ok(delete_pf("msitest", FALSE), "File not installed\n"); + + delete_cab_files(); + DeleteFile(msifile); +} + +static void test_caborder(void) +{ + UINT r; + + create_file("imperator", 100); + create_file("maximus", 500); + create_file("augustus", 50000); + create_file("caesar", 500); + + create_database(msifile, cc_tables, sizeof(cc_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + create_cab_file("test1.cab", MEDIA_SIZE, "maximus\0"); + create_cab_file("test2.cab", MEDIA_SIZE, "augustus\0"); + create_cab_file("test3.cab", MEDIA_SIZE, "caesar\0"); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r); + ok(!delete_pf("msitest\\augustus", TRUE), "File is installed\n"); + ok(!delete_pf("msitest\\caesar", TRUE), "File is installed\n"); + todo_wine + { + ok(!delete_pf("msitest\\maximus", TRUE), "File is installed\n"); + ok(!delete_pf("msitest", FALSE), "File is installed\n"); + } + + delete_cab_files(); + + create_cab_file("test1.cab", MEDIA_SIZE, "imperator\0"); + create_cab_file("test2.cab", MEDIA_SIZE, "maximus\0augustus\0"); + create_cab_file("test3.cab", MEDIA_SIZE, "caesar\0"); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r); + ok(!delete_pf("msitest\\maximus", TRUE), "File is installed\n"); + ok(!delete_pf("msitest\\augustus", TRUE), "File is installed\n"); + ok(!delete_pf("msitest\\caesar", TRUE), "File is installed\n"); + todo_wine + { + ok(!delete_pf("msitest", FALSE), "File is installed\n"); + } + + delete_cab_files(); + DeleteFile(msifile); + + create_cc_test_files(); + create_database(msifile, co_tables, sizeof(co_tables) / sizeof(msi_table)); + + r = MsiInstallProductA(msifile, NULL); + ok(!delete_pf("msitest\\augustus", TRUE), "File is installed\n"); + ok(!delete_pf("msitest\\caesar", TRUE), "File is installed\n"); + ok(!delete_pf("msitest", FALSE), "File is installed\n"); + todo_wine + { + ok(!delete_pf("msitest\\maximus", TRUE), "File is installed\n"); + ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r); + } + + delete_cab_files(); + DeleteFile(msifile); + + create_cc_test_files(); + create_database(msifile, co2_tables, sizeof(co2_tables) / sizeof(msi_table)); + + r = MsiInstallProductA(msifile, NULL); + ok(!delete_pf("msitest\\augustus", TRUE), "File is installed\n"); + ok(!delete_pf("msitest\\caesar", TRUE), "File is installed\n"); + todo_wine + { + ok(r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %u\n", r); + ok(!delete_pf("msitest\\maximus", TRUE), "File is installed\n"); + ok(!delete_pf("msitest", FALSE), "File is installed\n"); + } + + delete_cab_files(); + DeleteFile("imperator"); + DeleteFile("maximus"); + DeleteFile("augustus"); + DeleteFile("caesar"); + DeleteFile(msifile); +} + +static void test_mixedmedia(void) +{ + UINT r; + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\maximus", 500); + create_file("msitest\\augustus", 500); + create_file("caesar", 500); + + create_database(msifile, mm_tables, sizeof(mm_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + create_cab_file("test1.cab", MEDIA_SIZE, "caesar\0"); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n"); + ok(delete_pf("msitest", FALSE), "File not installed\n"); + + /* Delete the files in the temp (current) folder */ + DeleteFile("msitest\\maximus"); + DeleteFile("msitest\\augustus"); + RemoveDirectory("msitest"); + DeleteFile("caesar"); + DeleteFile("test1.cab"); + DeleteFile(msifile); +} + +static void test_samesequence(void) +{ + UINT r; + + create_cc_test_files(); + create_database(msifile, ss_tables, sizeof(ss_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + r = MsiInstallProductA(msifile, NULL); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n"); + } + ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n"); + ok(delete_pf("msitest", FALSE), "File not installed\n"); + + delete_cab_files(); + DeleteFile(msifile); +} + +static void test_uiLevelFlags(void) +{ + UINT r; + + create_cc_test_files(); + create_database(msifile, ui_tables, sizeof(ui_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY, NULL); + + r = MsiInstallProductA(msifile, NULL); + ok(!delete_pf("msitest\\maximus", TRUE), "UI install occurred, but execute-only was requested.\n"); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n"); + } + ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n"); + ok(delete_pf("msitest", FALSE), "File not installed\n"); + + delete_cab_files(); + DeleteFile(msifile); +} + +static BOOL file_matches(LPSTR path) +{ + CHAR buf[MAX_PATH]; + HANDLE file; + DWORD size; + + file = CreateFile(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + + ZeroMemory(buf, MAX_PATH); + ReadFile(file, buf, 15, &size, NULL); + CloseHandle(file); + + return !lstrcmp(buf, "msitest\\maximus"); +} + +static void test_readonlyfile(void) +{ + UINT r; + DWORD size; + HANDLE file; + CHAR path[MAX_PATH]; + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\maximus", 500); + create_database(msifile, rof_tables, sizeof(rof_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + lstrcpy(path, PROG_FILES_DIR); + lstrcat(path, "\\msitest"); + CreateDirectory(path, NULL); + + lstrcat(path, "\\maximus"); + file = CreateFile(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_NEW, FILE_ATTRIBUTE_READONLY, NULL); + + WriteFile(file, "readonlyfile", 20, &size, NULL); + CloseHandle(file); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(file_matches(path), "Expected file to be overwritten\n"); + ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n"); + ok(delete_pf("msitest", FALSE), "File not installed\n"); + + /* Delete the files in the temp (current) folder */ + DeleteFile("msitest\\maximus"); + RemoveDirectory("msitest"); + DeleteFile(msifile); +} + +static void test_setdirproperty(void) +{ + UINT r; + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\maximus", 500); + create_database(msifile, sdp_tables, sizeof(sdp_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_cf("msitest\\maximus", TRUE), "File not installed\n"); + ok(delete_cf("msitest", FALSE), "File not installed\n"); + + /* Delete the files in the temp (current) folder */ + DeleteFile(msifile); + DeleteFile("msitest\\maximus"); + RemoveDirectory("msitest"); +} + +static void test_cabisextracted(void) +{ + UINT r; + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\gaius", 500); + create_file("maximus", 500); + create_file("augustus", 500); + create_file("caesar", 500); + + create_cab_file("test1.cab", MEDIA_SIZE, "maximus\0"); + create_cab_file("test2.cab", MEDIA_SIZE, "augustus\0"); + create_cab_file("test3.cab", MEDIA_SIZE, "caesar\0"); + + create_database(msifile, cie_tables, sizeof(cie_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\gaius", TRUE), "File not installed\n"); + ok(delete_pf("msitest", FALSE), "File not installed\n"); + + /* Delete the files in the temp (current) folder */ + delete_cab_files(); + DeleteFile(msifile); + DeleteFile("maximus"); + DeleteFile("augustus"); + DeleteFile("caesar"); + DeleteFile("msitest\\gaius"); + RemoveDirectory("msitest"); +} + +static void test_concurrentinstall(void) +{ + UINT r; + CHAR path[MAX_PATH]; + + CreateDirectoryA("msitest", NULL); + CreateDirectoryA("msitest\\msitest", NULL); + create_file("msitest\\maximus", 500); + create_file("msitest\\msitest\\augustus", 500); + + create_database(msifile, ci_tables, sizeof(ci_tables) / sizeof(msi_table)); + + lstrcpyA(path, CURR_DIR); + lstrcatA(path, "\\msitest\\concurrent.msi"); + create_database(path, ci2_tables, sizeof(ci2_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n"); + ok(delete_pf("msitest", FALSE), "File not installed\n"); + + /* Delete the files in the temp (current) folder */ + DeleteFile(msifile); + DeleteFile(path); + DeleteFile("msitest\\msitest\\augustus"); + DeleteFile("msitest\\maximus"); + RemoveDirectory("msitest\\msitest"); + RemoveDirectory("msitest"); +} + +static void test_setpropertyfolder(void) +{ + UINT r; + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\maximus", 500); + + create_database(msifile, spf_tables, sizeof(spf_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_pf("msitest\\added\\maximus", TRUE), "File not installed\n"); + ok(delete_pf("msitest\\added", FALSE), "File not installed\n"); + ok(delete_pf("msitest", FALSE), "File not installed\n"); + + /* Delete the files in the temp (current) folder */ + DeleteFile(msifile); + DeleteFile("msitest\\maximus"); + RemoveDirectory("msitest"); +} + +static BOOL file_exists(LPCSTR file) +{ + return GetFileAttributes(file) != INVALID_FILE_ATTRIBUTES; +} + +static BOOL pf_exists(LPCSTR file) +{ + CHAR path[MAX_PATH]; + + lstrcpyA(path, PROG_FILES_DIR); + lstrcatA(path, "\\"); + lstrcatA(path, file); + + return file_exists(path); +} + +static void delete_pfmsitest_files(void) +{ + SHFILEOPSTRUCT shfl; + CHAR path[MAX_PATH+11]; + + lstrcpyA(path, PROG_FILES_DIR); + lstrcatA(path, "\\msitest\\*"); + path[strlen(path) + 1] = '\0'; + + shfl.hwnd = NULL; + shfl.wFunc = FO_DELETE; + shfl.pFrom = path; + shfl.pTo = NULL; + shfl.fFlags = FOF_FILESONLY | FOF_NOCONFIRMATION | FOF_NORECURSION | FOF_SILENT; + + SHFileOperation(&shfl); + + lstrcpyA(path, PROG_FILES_DIR); + lstrcatA(path, "\\msitest"); + RemoveDirectoryA(path); +} + +static void check_reg_str(HKEY prodkey, LPCSTR name, LPCSTR expected, BOOL bcase, DWORD line) +{ + char val[MAX_PATH]; + DWORD size, type; + LONG res; + + size = MAX_PATH; + val[0] = '\0'; + res = RegQueryValueExA(prodkey, name, NULL, &type, (LPBYTE)val, &size); + + if (res != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) + { + ok_(__FILE__, line)(FALSE, "Key doesn't exist or wrong type\n"); + return; + } + + if (!expected) + ok_(__FILE__, line)(lstrlenA(val) == 0, "Expected empty string, got %s\n", val); + else + { + if (bcase) + ok_(__FILE__, line)(!lstrcmpA(val, expected), "Expected %s, got %s\n", expected, val); + else + ok_(__FILE__, line)(!lstrcmpiA(val, expected), "Expected %s, got %s\n", expected, val); + } +} + +static void check_reg_dword(HKEY prodkey, LPCSTR name, DWORD expected, DWORD line) +{ + DWORD val, size, type; + LONG res; + + size = sizeof(DWORD); + res = RegQueryValueExA(prodkey, name, NULL, &type, (LPBYTE)&val, &size); + + if (res != ERROR_SUCCESS || type != REG_DWORD) + { + ok_(__FILE__, line)(FALSE, "Key doesn't exist or wrong type\n"); + return; + } + + ok_(__FILE__, line)(val == expected, "Expected %d, got %d\n", expected, val); +} + +#define CHECK_REG_STR(prodkey, name, expected) \ + check_reg_str(prodkey, name, expected, TRUE, __LINE__); + +#define CHECK_REG_ISTR(prodkey, name, expected) \ + check_reg_str(prodkey, name, expected, FALSE, __LINE__); + +#define CHECK_REG_DWORD(prodkey, name, expected) \ + check_reg_dword(prodkey, name, expected, __LINE__); + +static void get_date_str(LPSTR date) +{ + SYSTEMTIME systime; + + static const char date_fmt[] = "%d%02d%02d"; + GetLocalTime(&systime); + sprintf(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay); +} + +static void test_publish(void) +{ + UINT r; + LONG res; + HKEY uninstall, prodkey; + INSTALLSTATE state; + CHAR prodcode[] = "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"; + char date[MAX_PATH]; + char temp[MAX_PATH]; + + static const CHAR subkey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + + get_date_str(date); + GetTempPath(MAX_PATH, temp); + + res = RegOpenKeyA(HKEY_LOCAL_MACHINE, subkey, &uninstall); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\maximus", 500); + + create_database(msifile, pp_tables, sizeof(pp_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + /* nothing published */ + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + /* install again */ + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + /* try to uninstall */ + r = MsiInstallProductA(msifile, "REMOVE=ALL"); + todo_wine + { + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + } + ok(pf_exists("msitest\\maximus"), "File deleted\n"); + ok(pf_exists("msitest"), "File deleted\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + /* PublishProduct */ + r = MsiInstallProductA(msifile, "PUBLISH_PRODUCT=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + /* try to uninstall after PublishProduct */ + r = MsiInstallProductA(msifile, "REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File deleted\n"); + ok(pf_exists("msitest"), "File deleted\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + /* RegisterProduct */ + r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + todo_wine + { + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* complete uninstall */ + r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL"); + todo_wine + { + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + } + ok(pf_exists("msitest\\maximus"), "File deleted\n"); + ok(pf_exists("msitest"), "File deleted\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + todo_wine + { + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + } + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + todo_wine + { + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + } + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + todo_wine + { + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* PublishProduct and RegisterProduct */ + r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + todo_wine + { + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* try it again */ + r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + todo_wine + { + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + } + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + todo_wine + { + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + } + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + todo_wine + { + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + } + + /* uninstall has a problem with this */ + r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL"); + todo_wine + { + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + } + ok(pf_exists("msitest\\maximus"), "File deleted\n"); + ok(pf_exists("msitest"), "File deleted\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + todo_wine + { + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + } + + /* PublishProduct and RegisterProduct and ProcessComponents */ + r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1 PROCESS_COMPONENTS=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + todo_wine + { + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* complete uninstall */ + r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File deleted\n"); + ok(pf_exists("msitest"), "File deleted\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + todo_wine + { + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + } + + /* PublishProduct, RegisterProduct, ProcessComponents, PublishFeatures */ + r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1 PUBLISH_PRODUCT=1 PROCESS_COMPONENTS=1 PUBLISH_FEATURES=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + todo_wine + { + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* complete uninstall */ + r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!pf_exists("msitest\\maximus"), "File deleted\n"); + ok(!pf_exists("msitest"), "File deleted\n"); + } + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + todo_wine + { + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + } + + /* complete install */ + r = MsiInstallProductA(msifile, "FULL=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + todo_wine + { + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* no UnpublishFeatures */ + r = MsiInstallProductA(msifile, "REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!pf_exists("msitest\\maximus"), "File deleted\n"); + ok(!pf_exists("msitest"), "File deleted\n"); + } + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + /* complete install */ + r = MsiInstallProductA(msifile, "FULL=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + todo_wine + { + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* UnpublishFeatures, only feature removed. Only works when entire product is removed */ + r = MsiInstallProductA(msifile, "UNPUBLISH_FEATURES=1 REMOVE=feature"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File deleted\n"); + ok(pf_exists("msitest"), "File deleted\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + todo_wine + { + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* complete install */ + r = MsiInstallProductA(msifile, "FULL=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + todo_wine + { + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* UnpublishFeatures, both features removed */ + r = MsiInstallProductA(msifile, "UNPUBLISH_FEATURES=1 REMOVE=feature,montecristo"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!pf_exists("msitest\\maximus"), "File not deleted\n"); + ok(!pf_exists("msitest"), "File not deleted\n"); + } + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + /* complete install */ + r = MsiInstallProductA(msifile, "FULL=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + CHECK_REG_STR(prodkey, "DisplayName", "MSITEST"); + CHECK_REG_STR(prodkey, "DisplayVersion", "1.1.1"); + CHECK_REG_STR(prodkey, "InstallDate", date); + CHECK_REG_STR(prodkey, "InstallSource", temp); + CHECK_REG_ISTR(prodkey, "ModifyPath", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "Publisher", "Wine"); + CHECK_REG_STR(prodkey, "UninstallString", "MsiExec.exe /I{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + CHECK_REG_STR(prodkey, "AuthorizedCDFPrefix", NULL); + CHECK_REG_STR(prodkey, "Comments", NULL); + CHECK_REG_STR(prodkey, "Contact", NULL); + CHECK_REG_STR(prodkey, "HelpLink", NULL); + CHECK_REG_STR(prodkey, "HelpTelephone", NULL); + CHECK_REG_STR(prodkey, "InstallLocation", NULL); + CHECK_REG_STR(prodkey, "Readme", NULL); + CHECK_REG_STR(prodkey, "Size", NULL); + CHECK_REG_STR(prodkey, "URLInfoAbout", NULL); + CHECK_REG_STR(prodkey, "URLUpdateInfo", NULL); + CHECK_REG_DWORD(prodkey, "Language", 1033); + CHECK_REG_DWORD(prodkey, "Version", 0x1010001); + CHECK_REG_DWORD(prodkey, "VersionMajor", 1); + CHECK_REG_DWORD(prodkey, "VersionMinor", 1); + CHECK_REG_DWORD(prodkey, "WindowsInstaller", 1); + todo_wine + { + CHECK_REG_DWORD(prodkey, "EstimatedSize", 12); + } + + RegCloseKey(prodkey); + + /* complete uninstall */ + r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!pf_exists("msitest\\maximus"), "File not deleted\n"); + ok(!pf_exists("msitest"), "File not deleted\n"); + } + + state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + state = MsiQueryFeatureState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", "montecristo"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + "{DF2CBABC-3BCC-47E5-A998-448D1C0C895B}", &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegOpenKeyA(uninstall, prodcode, &prodkey); + todo_wine + { + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + } + + /* make sure 'Program Files\msitest' is removed */ + delete_pfmsitest_files(); + + RegCloseKey(uninstall); + DeleteFile(msifile); + DeleteFile("msitest\\maximus"); + RemoveDirectory("msitest"); +} + +static void test_publishsourcelist(void) +{ + UINT r; + DWORD size; + CHAR value[MAX_PATH]; + CHAR prodcode[] = "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"; + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\maximus", 500); + + create_database(msifile, pp_tables, sizeof(pp_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + /* nothing published */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size); + + r = MsiInstallProductA(msifile, "REGISTER_PRODUCT=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + /* after RegisterProduct */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size); + + r = MsiInstallProductA(msifile, "PROCESS_COMPONENTS=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + /* after ProcessComponents */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size); + + r = MsiInstallProductA(msifile, "PUBLISH_FEATURES=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + /* after PublishFeatures */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size); + + r = MsiInstallProductA(msifile, "PUBLISH_PRODUCT=1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\maximus"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + /* after PublishProduct */ + size = MAX_PATH; + lstrcpyA(value, "aaa"); + r = pMsiSourceListGetInfoA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, value, &size); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(value, "msitest.msi"), "Expected 'msitest.msi', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + } + + /* complete uninstall */ + r = MsiInstallProductA(msifile, "FULL=1 REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!pf_exists("msitest\\maximus"), "File not deleted\n"); + ok(!pf_exists("msitest"), "File not deleted\n"); + } + + /* make sure 'Program Files\msitest' is removed */ + delete_pfmsitest_files(); + + DeleteFile(msifile); + DeleteFile("msitest\\maximus"); + RemoveDirectory("msitest"); +} + +static UINT run_query(MSIHANDLE hdb, MSIHANDLE hrec, const char *query) +{ + MSIHANDLE hview = 0; + UINT r; + + r = MsiDatabaseOpenView(hdb, query, &hview); + if(r != ERROR_SUCCESS) + return r; + + r = MsiViewExecute(hview, hrec); + if(r == ERROR_SUCCESS) + r = MsiViewClose(hview); + MsiCloseHandle(hview); + return r; +} + +static void set_transform_summary_info(void) +{ + UINT r; + MSIHANDLE suminfo = 0; + + /* build summmary info */ + r = MsiGetSummaryInformation(0, mstfile, 3, &suminfo); + todo_wine + { + ok(r == ERROR_SUCCESS , "Failed to open summaryinfo\n"); + } + + r = MsiSummaryInfoSetProperty(suminfo, PID_TITLE, VT_LPSTR, 0, NULL, "MSITEST"); + todo_wine + { + ok(r == ERROR_SUCCESS, "Failed to set summary info\n"); + } + + r = MsiSummaryInfoSetProperty(suminfo, PID_REVNUMBER, VT_LPSTR, 0, NULL, + "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}1.1.1;" + "{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}1.1.1;" + "{4C0EAA15-0264-4E5A-8758-609EF142B92D}"); + todo_wine + { + ok(r == ERROR_SUCCESS , "Failed to set summary info\n"); + } + + r = MsiSummaryInfoSetProperty(suminfo, PID_PAGECOUNT, VT_I4, 100, NULL, NULL); + todo_wine + { + ok(r == ERROR_SUCCESS, "Failed to set summary info\n"); + } + + r = MsiSummaryInfoPersist(suminfo); + todo_wine + { + ok(r == ERROR_SUCCESS , "Failed to make summary info persist\n"); + } + + r = MsiCloseHandle(suminfo); + ok(r == ERROR_SUCCESS , "Failed to close suminfo\n"); +} + +static void generate_transform(void) +{ + MSIHANDLE hdb1, hdb2; + LPCSTR query; + UINT r; + + /* start with two identical databases */ + CopyFile(msifile, msifile2, FALSE); + + r = MsiOpenDatabase(msifile2, MSIDBOPEN_TRANSACT, &hdb1); + ok(r == ERROR_SUCCESS , "Failed to create database\n"); + + r = MsiDatabaseCommit(hdb1); + ok(r == ERROR_SUCCESS , "Failed to commit database\n"); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb2); + ok(r == ERROR_SUCCESS , "Failed to create database\n"); + + query = "INSERT INTO `Property` ( `Property`, `Value` ) VALUES ( 'prop', 'val' )"; + r = run_query(hdb1, 0, query); + ok(r == ERROR_SUCCESS, "failed to add property\n"); + + /* database needs to be committed */ + MsiDatabaseCommit(hdb1); + + r = MsiDatabaseGenerateTransform(hdb1, hdb2, mstfile, 0, 0); + ok(r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r); + +#if 0 /* not implemented in wine yet */ + r = MsiCreateTransformSummaryInfo(hdb2, hdb2, mstfile, 0, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); +#endif + + MsiCloseHandle(hdb1); + MsiCloseHandle(hdb2); +} + +/* data for generating a transform */ + +/* tables transform names - encoded as they would be in an msi database file */ +static const WCHAR name1[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */ +static const WCHAR name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */ +static const WCHAR name3[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */ + +/* data in each table */ +static const char data1[] = /* _StringData */ + "propval"; /* all the strings squashed together */ + +static const WCHAR data2[] = { /* _StringPool */ +/* len, refs */ + 0, 0, /* string 0 '' */ + 4, 1, /* string 1 'prop' */ + 3, 1, /* string 2 'val' */ +}; + +static const WCHAR data3[] = { /* Property */ + 0x0201, 0x0001, 0x0002, +}; + +static const struct { + LPCWSTR name; + const void *data; + DWORD size; +} table_transform_data[] = +{ + { name1, data1, sizeof data1 - 1 }, + { name2, data2, sizeof data2 }, + { name3, data3, sizeof data3 }, +}; + +#define NUM_TRANSFORM_TABLES (sizeof table_transform_data/sizeof table_transform_data[0]) + +static void generate_transform_manual(void) +{ + IStorage *stg = NULL; + IStream *stm; + WCHAR name[0x20]; + HRESULT r; + DWORD i, count; + const DWORD mode = STGM_CREATE|STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE; + + const CLSID CLSID_MsiTransform = { 0xc1082,0,0,{0xc0,0,0,0,0,0,0,0x46}}; + + MultiByteToWideChar(CP_ACP, 0, mstfile, -1, name, 0x20); + + r = StgCreateDocfile(name, mode, 0, &stg); + ok(r == S_OK, "failed to create storage\n"); + if (!stg) return; - create_test_files(); - create_database(msifile, tables, sizeof(tables) / sizeof(msi_table)); + r = IStorage_SetClass(stg, &CLSID_MsiTransform); + ok(r == S_OK, "failed to set storage type\n"); + + for (i=0; i #include #include #include +#include #include "wine/test.h" -typedef struct test_MSIFILEHASHINFO { - ULONG dwFileHashInfoSize; - ULONG dwData[4]; -} test_MSIFILEHASHINFO, *test_PMSIFILEHASHINFO; +static BOOL (WINAPI *pConvertSidToStringSidA)(PSID, LPSTR*); -typedef INSTALLSTATE (WINAPI *fnMsiUseFeatureExA)(LPCSTR, LPCSTR ,DWORD, DWORD ); -fnMsiUseFeatureExA pMsiUseFeatureExA; -typedef UINT (WINAPI *fnMsiOpenPackageExA)(LPCSTR, DWORD, MSIHANDLE*); -fnMsiOpenPackageExA pMsiOpenPackageExA; -typedef UINT (WINAPI *fnMsiOpenPackageExW)(LPCWSTR, DWORD, MSIHANDLE*); -fnMsiOpenPackageExW pMsiOpenPackageExW; -typedef INSTALLSTATE (WINAPI *fnMsiGetComponentPathA)(LPCSTR, LPCSTR, LPSTR, DWORD*); -fnMsiGetComponentPathA pMsiGetComponentPathA; -typedef UINT (WINAPI *fnMsiGetFileHashA)(LPCSTR, DWORD, test_PMSIFILEHASHINFO); -fnMsiGetFileHashA pMsiGetFileHashA; +static INSTALLSTATE (WINAPI *pMsiGetComponentPathA) + (LPCSTR, LPCSTR, LPSTR, DWORD*); +static UINT (WINAPI *pMsiGetFileHashA) + (LPCSTR, DWORD, PMSIFILEHASHINFO); +static UINT (WINAPI *pMsiOpenPackageExA) + (LPCSTR, DWORD, MSIHANDLE*); +static UINT (WINAPI *pMsiOpenPackageExW) + (LPCWSTR, DWORD, MSIHANDLE*); +static UINT (WINAPI *pMsiQueryComponentStateA) + (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, LPCSTR, INSTALLSTATE*); +static INSTALLSTATE (WINAPI *pMsiUseFeatureExA) + (LPCSTR, LPCSTR ,DWORD, DWORD ); + +static void init_functionpointers(void) +{ + HMODULE hmsi = GetModuleHandleA("msi.dll"); + HMODULE hadvapi32 = GetModuleHandleA("advapi32.dll"); + +#define GET_PROC(dll, func) \ + p ## func = (void *)GetProcAddress(dll, #func); \ + if(!p ## func) \ + trace("GetProcAddress(%s) failed\n", #func); + + GET_PROC(hmsi, MsiGetComponentPathA) + GET_PROC(hmsi, MsiGetFileHashA) + GET_PROC(hmsi, MsiOpenPackageExA) + GET_PROC(hmsi, MsiOpenPackageExW) + GET_PROC(hmsi, MsiQueryComponentStateA) + GET_PROC(hmsi, MsiUseFeatureExA) + + GET_PROC(hadvapi32, ConvertSidToStringSidA) + +#undef GET_PROC +} static void test_usefeature(void) { - UINT r; + INSTALLSTATE r; if (!pMsiUseFeatureExA) + { + skip("MsiUseFeatureExA not implemented\n"); return; + } r = MsiQueryFeatureState(NULL,NULL); ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n"); @@ -60,19 +87,19 @@ static void test_usefeature(void) r = pMsiUseFeatureExA(NULL, "WORDVIEWFiles", -2, 1 ); ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n"); - r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}", + r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}", NULL, -2, 0 ); ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n"); - r = pMsiUseFeatureExA("{9085040-6000-11d3-8cfe-0150048383c9}", + r = pMsiUseFeatureExA("{9085040-6000-11d3-8cfe-0150048383c9}", "WORDVIEWFiles", -2, 0 ); ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n"); - r = pMsiUseFeatureExA("{0085040-6000-11d3-8cfe-0150048383c9}", + r = pMsiUseFeatureExA("{0085040-6000-11d3-8cfe-0150048383c9}", "WORDVIEWFiles", -2, 0 ); ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n"); - r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}", + r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}", "WORDVIEWFiles", -2, 1 ); ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n"); } @@ -81,12 +108,16 @@ static void test_null(void) { MSIHANDLE hpkg; UINT r; + HKEY hkey; + DWORD dwType, cbData; + LPBYTE lpData = NULL; + INSTALLSTATE state; r = pMsiOpenPackageExW(NULL, 0, &hpkg); ok( r == ERROR_INVALID_PARAMETER,"wrong error\n"); - r = MsiQueryProductStateW(NULL); - ok( r == INSTALLSTATE_INVALIDARG, "wrong return\n"); + state = MsiQueryProductStateW(NULL); + ok( state == INSTALLSTATE_INVALIDARG, "wrong return\n"); r = MsiEnumFeaturesW(NULL,0,NULL,NULL); ok( r == ERROR_INVALID_PARAMETER,"wrong error\n"); @@ -102,6 +133,65 @@ static void test_null(void) r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", "foo", INSTALLSTATE_DEFAULT); ok( r == ERROR_UNKNOWN_PRODUCT, "wrong error %d\n", r); + + /* make sure empty string to MsiGetProductInfo is not a handle to default registry value, saving and restoring the + * necessary registry values */ + + /* empty product string */ + r = RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", &hkey); + ok( r == ERROR_SUCCESS, "wrong error %d\n", r); + + r = RegQueryValueExA(hkey, NULL, 0, &dwType, lpData, &cbData); + ok ( r == ERROR_SUCCESS || r == ERROR_FILE_NOT_FOUND, "wrong error %d\n", r); + if ( r == ERROR_SUCCESS ) + { + lpData = HeapAlloc(GetProcessHeap(), 0, cbData); + if (!lpData) + skip("Out of memory\n"); + else + { + r = RegQueryValueExA(hkey, NULL, 0, &dwType, lpData, &cbData); + ok ( r == ERROR_SUCCESS, "wrong error %d\n", r); + } + } + + r = RegSetValueA(hkey, NULL, REG_SZ, "test", strlen("test")); + ok( r == ERROR_SUCCESS, "wrong error %d\n", r); + + r = MsiGetProductInfoA("", "", NULL, NULL); + ok ( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r); + + if (lpData) + { + r = RegSetValueExA(hkey, NULL, 0, dwType, lpData, cbData); + ok ( r == ERROR_SUCCESS, "wrong error %d\n", r); + + HeapFree(GetProcessHeap(), 0, lpData); + } + else + { + r = RegDeleteValueA(hkey, NULL); + ok ( r == ERROR_SUCCESS, "wrong error %d\n", r); + } + + r = RegCloseKey(hkey); + ok( r == ERROR_SUCCESS, "wrong error %d\n", r); + + /* empty attribute */ + r = RegCreateKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}", &hkey); + ok( r == ERROR_SUCCESS, "wrong error %d\n", r); + + r = RegSetValueA(hkey, NULL, REG_SZ, "test", strlen("test")); + ok( r == ERROR_SUCCESS, "wrong error %d\n", r); + + r = MsiGetProductInfoA("{F1C3AF50-8B56-4A69-A00C-00773FE42F30}", "", NULL, NULL); + ok ( r == ERROR_UNKNOWN_PROPERTY, "wrong error %d\n", r); + + r = RegCloseKey(hkey); + ok( r == ERROR_SUCCESS, "wrong error %d\n", r); + + r = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}"); + ok( r == ERROR_SUCCESS, "wrong error %d\n", r); } static void test_getcomponentpath(void) @@ -144,76 +234,1835 @@ static void test_getcomponentpath(void) ok( r == INSTALLSTATE_UNKNOWN, "wrong return value\n"); } -static void test_filehash(void) +static void create_file(LPCSTR name, LPCSTR data, DWORD size) +{ + HANDLE file; + DWORD written; + + file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name); + WriteFile(file, data, strlen(data), &written, NULL); + + if (size) + { + SetFilePointer(file, size, NULL, FILE_BEGIN); + SetEndOfFile(file); + } + + CloseHandle(file); +} + +#define HASHSIZE sizeof(MSIFILEHASHINFO) + +static const struct +{ + LPCSTR data; + DWORD size; + MSIFILEHASHINFO hash; +} hash_data[] = +{ + { "abc", 0, + { HASHSIZE, + { 0x98500190, 0xb04fd23c, 0x7d3f96d6, 0x727fe128 }, + }, + }, + + { "C:\\Program Files\\msitest\\caesar\n", 0, + { HASHSIZE, + { 0x2b566794, 0xfd42181b, 0x2514d6e4, 0x5768b4e2 }, + }, + }, + + { "C:\\Program Files\\msitest\\caesar\n", 500, + { HASHSIZE, + { 0x58095058, 0x805efeff, 0x10f3483e, 0x0147d653 }, + }, + }, +}; + +static void test_MsiGetFileHash(void) { const char name[] = "msitest.bin"; - const char data[] = {'a','b','c'}; - HANDLE handle; UINT r; - test_MSIFILEHASHINFO hash; - DWORD count = 0; + MSIFILEHASHINFO hash; + DWORD i; if (!pMsiGetFileHashA) + { + skip("MsiGetFileHash not implemented\n"); return; + } - DeleteFile(name); + hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); - memset(&hash, 0, sizeof hash); + /* szFilePath is NULL */ + r = pMsiGetFileHashA(NULL, 0, &hash); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* szFilePath is empty */ + r = pMsiGetFileHashA("", 0, &hash); + ok(r == ERROR_PATH_NOT_FOUND, "Expected ERROR_PATH_NOT_FOUND, got %d\n", r); + + /* szFilePath is nonexistent */ r = pMsiGetFileHashA(name, 0, &hash); - ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r); + ok(r == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", r); - r = pMsiGetFileHashA(name, 0, NULL); - ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r); - - memset(&hash, 0, sizeof hash); - hash.dwFileHashInfoSize = sizeof hash; - r = pMsiGetFileHashA(name, 0, &hash); - ok( r == ERROR_FILE_NOT_FOUND, "wrong error %d\n", r); - - handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL); - ok(handle != INVALID_HANDLE_VALUE, "failed to create file\n"); - - WriteFile(handle, data, sizeof data, &count, NULL); - CloseHandle(handle); - - memset(&hash, 0, sizeof hash); - r = pMsiGetFileHashA(name, 0, &hash); - ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r); - - memset(&hash, 0, sizeof hash); - hash.dwFileHashInfoSize = sizeof hash; + /* dwOptions is non-zero */ r = pMsiGetFileHashA(name, 1, &hash); - ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + /* pHash.dwFileHashInfoSize is not correct */ + hash.dwFileHashInfoSize = 0; r = pMsiGetFileHashA(name, 0, &hash); - ok( r == ERROR_SUCCESS, "wrong error %d\n", r); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); - ok(hash.dwFileHashInfoSize == sizeof hash, "hash size changed\n"); - ok(hash.dwData[0] == 0x98500190 && - hash.dwData[1] == 0xb04fd23c && - hash.dwData[2] == 0x7d3f96d6 && - hash.dwData[3] == 0x727fe128, "hash of abc incorrect\n"); + /* pHash is NULL */ + r = pMsiGetFileHashA(name, 0, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); - DeleteFile(name); + for (i = 0; i < sizeof(hash_data) / sizeof(hash_data[0]); i++) + { + create_file(name, hash_data[i].data, hash_data[i].size); + + memset(&hash, 0, sizeof(MSIFILEHASHINFO)); + hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + + r = pMsiGetFileHashA(name, 0, &hash); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!memcmp(&hash, &hash_data[i].hash, HASHSIZE), "Hash incorrect\n"); + + DeleteFile(name); + } +} + +/* copied from dlls/msi/registry.c */ +static BOOL squash_guid(LPCWSTR in, LPWSTR out) +{ + DWORD i,n=1; + GUID guid; + + if (FAILED(CLSIDFromString((LPOLESTR)in, &guid))) + return FALSE; + + for(i=0; i<8; i++) + out[7-i] = in[n++]; + n++; + for(i=0; i<4; i++) + out[11-i] = in[n++]; + n++; + for(i=0; i<4; i++) + out[15-i] = in[n++]; + n++; + for(i=0; i<2; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + n++; + for( ; i<8; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + out[32]=0; + return TRUE; +} + +static void create_test_guid(LPSTR prodcode, LPSTR squashed) +{ + WCHAR guidW[MAX_PATH]; + WCHAR squashedW[MAX_PATH]; + GUID guid; + HRESULT hr; + int size; + + hr = CoCreateGuid(&guid); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + + size = StringFromGUID2(&guid, (LPOLESTR)guidW, MAX_PATH); + ok(size == 39, "Expected 39, got %d\n", hr); + + WideCharToMultiByte(CP_ACP, 0, guidW, size, prodcode, MAX_PATH, NULL, NULL); + squash_guid(guidW, squashedW); + WideCharToMultiByte(CP_ACP, 0, squashedW, -1, squashed, MAX_PATH, NULL, NULL); +} + +static void get_user_sid(LPSTR *usersid) +{ + HANDLE token; + BYTE buf[1024]; + DWORD size; + PTOKEN_USER user; + + OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token); + size = sizeof(buf); + GetTokenInformation(token, TokenUser, (void *)buf, size, &size); + user = (PTOKEN_USER)buf; + pConvertSidToStringSidA(user->User.Sid, usersid); +} + +static void test_MsiQueryProductState(void) +{ + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + CHAR keypath[MAX_PATH*2]; + LPSTR usersid; + INSTALLSTATE state; + LONG res; + HKEY userkey, localkey, props; + DWORD data; + + create_test_guid(prodcode, prod_squashed); + get_user_sid(&usersid); + + /* NULL prodcode */ + state = MsiQueryProductStateA(NULL); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* empty prodcode */ + state = MsiQueryProductStateA(""); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* garbage prodcode */ + state = MsiQueryProductStateA("garbage"); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* guid without brackets */ + state = MsiQueryProductStateA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D"); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* guid with brackets */ + state = MsiQueryProductStateA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + /* same length as guid, but random */ + state = MsiQueryProductStateA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + /* created guid cannot possibly be an installed product code */ + state = MsiQueryProductStateA(prodcode); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user product key exists */ + state = MsiQueryProductStateA(prodcode); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"); + lstrcatA(keypath, prodcode); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local uninstall key exists */ + state = MsiQueryProductStateA(prodcode); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + data = 1; + res = RegSetValueExA(localkey, "WindowsInstaller", 0, REG_DWORD, (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* WindowsInstaller value exists */ + state = MsiQueryProductStateA(prodcode); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + RegDeleteValueA(localkey, "WindowsInstaller"); + RegDeleteKeyA(localkey, ""); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local product key exists */ + state = MsiQueryProductStateA(prodcode); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + res = RegCreateKeyA(localkey, "InstallProperties", &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* install properties key exists */ + state = MsiQueryProductStateA(prodcode); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + data = 1; + res = RegSetValueExA(props, "WindowsInstaller", 0, REG_DWORD, (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* WindowsInstaller value exists */ + state = MsiQueryProductStateA(prodcode); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + data = 2; + res = RegSetValueExA(props, "WindowsInstaller", 0, REG_DWORD, (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* WindowsInstaller value is not 1 */ + state = MsiQueryProductStateA(prodcode); + ok(state == INSTALLSTATE_DEFAULT, "Expected INSTALLSTATE_DEFAULT, got %d\n", state); + + RegDeleteKeyA(userkey, ""); + + /* user product key does not exist */ + state = MsiQueryProductStateA(prodcode); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + + LocalFree(usersid); + RegDeleteValueA(props, "WindowsInstaller"); + RegDeleteKeyA(props, ""); + RegDeleteKeyA(localkey, ""); + RegCloseKey(userkey); + RegCloseKey(localkey); + RegCloseKey(props); +} + +static const char table_enc85[] = +"!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO" +"PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx" +"yz{}~"; + +/* + * Encodes a base85 guid given a GUID pointer + * Caller should provide a 21 character buffer for the encoded string. + * + * returns TRUE if successful, FALSE if not + */ +static BOOL encode_base85_guid( GUID *guid, LPWSTR str ) +{ + unsigned int x, *p, i; + + p = (unsigned int*) guid; + for( i=0; i<4; i++ ) + { + x = p[i]; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + } + *str = 0; + + return TRUE; +} + +static void compose_base85_guid(LPSTR component, LPSTR comp_base85, LPSTR squashed) +{ + WCHAR guidW[MAX_PATH]; + WCHAR base85W[MAX_PATH]; + WCHAR squashedW[MAX_PATH]; + GUID guid; + HRESULT hr; + int size; + + hr = CoCreateGuid(&guid); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + + size = StringFromGUID2(&guid, (LPOLESTR)guidW, MAX_PATH); + ok(size == 39, "Expected 39, got %d\n", hr); + + WideCharToMultiByte(CP_ACP, 0, guidW, size, component, MAX_PATH, NULL, NULL); + encode_base85_guid(&guid, base85W); + WideCharToMultiByte(CP_ACP, 0, base85W, -1, comp_base85, MAX_PATH, NULL, NULL); + squash_guid(guidW, squashedW); + WideCharToMultiByte(CP_ACP, 0, squashedW, -1, squashed, MAX_PATH, NULL, NULL); +} + +static void test_MsiQueryFeatureState(void) +{ + HKEY userkey, localkey, compkey; + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + CHAR component[MAX_PATH]; + CHAR comp_base85[MAX_PATH]; + CHAR comp_squashed[MAX_PATH]; + CHAR keypath[MAX_PATH*2]; + INSTALLSTATE state; + LPSTR usersid; + LONG res; + + create_test_guid(prodcode, prod_squashed); + compose_base85_guid(component, comp_base85, comp_squashed); + get_user_sid(&usersid); + + /* NULL prodcode */ + state = MsiQueryFeatureStateA(NULL, "feature"); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* empty prodcode */ + state = MsiQueryFeatureStateA("", "feature"); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* garbage prodcode */ + state = MsiQueryFeatureStateA("garbage", "feature"); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* guid without brackets */ + state = MsiQueryFeatureStateA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D", "feature"); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* guid with brackets */ + state = MsiQueryFeatureStateA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}", "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + /* same length as guid, but random */ + state = MsiQueryFeatureStateA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93", "feature"); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* NULL szFeature */ + state = MsiQueryFeatureStateA(prodcode, NULL); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + + /* empty szFeature */ + state = MsiQueryFeatureStateA(prodcode, ""); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + /* feature key does not exist yet */ + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Features\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* feature key exists */ + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegSetValueExA(userkey, "feature", 0, REG_SZ, (const BYTE *)"", 8); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + lstrcatA(keypath, "\\Features"); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &localkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + res = RegSetValueExA(localkey, "feature", 0, REG_SZ, (const BYTE *)"aaaaaaaaaaaaaaaaaaa", 20); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_BADCONFIG, "Expected INSTALLSTATE_BADCONFIG, got %d\n", state); + + res = RegSetValueExA(localkey, "feature", 0, REG_SZ, (const BYTE *)"aaaaaaaaaaaaaaaaaaaa", 21); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + res = RegSetValueExA(localkey, "feature", 0, REG_SZ, (const BYTE *)"aaaaaaaaaaaaaaaaaaaaa", 22); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + res = RegSetValueExA(localkey, "feature", 0, REG_SZ, (const BYTE *)comp_base85, 21); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_ADVERTISED, "Expected INSTALLSTATE_ADVERTISED, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"", 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"apple", 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + RegDeleteValueA(compkey, prod_squashed); + RegDeleteValueA(compkey, ""); + RegDeleteValueA(localkey, "feature"); + RegDeleteValueA(userkey, "feature"); + RegDeleteKeyA(userkey, ""); + RegCloseKey(compkey); + RegCloseKey(localkey); + RegCloseKey(userkey); +} + +static void test_MsiQueryComponentState(void) +{ + HKEY compkey, prodkey; + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + CHAR component[MAX_PATH]; + CHAR comp_base85[MAX_PATH]; + CHAR comp_squashed[MAX_PATH]; + CHAR keypath[MAX_PATH]; + INSTALLSTATE state; + LPSTR usersid; + LONG res; + UINT r; + + static const INSTALLSTATE MAGIC_ERROR = 0xdeadbeef; + + if (!pMsiQueryComponentStateA) + { + skip("MsiQueryComponentStateA not implemented\n"); + return; + } + + create_test_guid(prodcode, prod_squashed); + compose_base85_guid(component, comp_base85, comp_squashed); + get_user_sid(&usersid); + + /* NULL szProductCode */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(NULL, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + /* empty szProductCode */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA("", NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + /* random szProductCode */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA("random", NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + /* GUID-length szProductCode */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA("DJANE93KNDNAS-2KN2NR93KMN3LN13=L1N3KDE", NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + /* GUID-length with brackets */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA("{JANE93KNDNAS-2KN2NR93KMN3LN13=L1N3KD}", NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + /* actual GUID */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + lstrcpyA(keypath, "Software\\Classes\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* create local system product key */ + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, prod_squashed); + lstrcatA(keypath, "\\InstallProperties"); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local system product key exists */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + res = RegSetValueExA(prodkey, "LocalPackage", 0, REG_SZ, (const BYTE *)"msitest.msi", 11); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* LocalPackage value exists */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* component key exists */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"", 0); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* component\product exists */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_NOTUSED, "Expected INSTALLSTATE_NOTUSED, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"hi", 2); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + RegDeleteValueA(prodkey, "LocalPackage"); + RegDeleteKeyA(prodkey, ""); + RegDeleteValueA(compkey, prod_squashed); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + RegCloseKey(compkey); + + /* MSIINSTALLCONTEXT_USERUNMANAGED */ + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, component, &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, component, &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + lstrcatA(keypath, "\\InstallProperties"); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(prodkey, "LocalPackage", 0, REG_SZ, (const BYTE *)"msitest.msi", 11); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + RegCloseKey(prodkey); + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, component, &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* component key exists */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, component, &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"", 0); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* component\product exists */ + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, component, &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_NOTUSED, "Expected INSTALLSTATE_NOTUSED, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"hi", 2); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, component, &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + /* MSIINSTALLCONTEXT_USERMANAGED */ + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERMANAGED, component, &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERMANAGED, component, &state); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(state == MAGIC_ERROR, "Expected 0xdeadbeef, got %d\n", state); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERMANAGED, component, &state); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + lstrcatA(keypath, "\\InstallProperties"); + + res = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(prodkey, "ManagedLocalPackage", 0, REG_SZ, (const BYTE *)"msitest.msi", 11); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + state = MAGIC_ERROR; + r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_USERMANAGED, component, &state); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + RegDeleteValueA(prodkey, "LocalPackage"); + RegDeleteValueA(prodkey, "ManagedLocalPackage"); + RegDeleteKeyA(prodkey, ""); + RegDeleteValueA(compkey, prod_squashed); + RegDeleteKeyA(compkey, ""); + RegCloseKey(prodkey); + RegCloseKey(compkey); +} + +static void test_MsiGetComponentPath(void) +{ + HKEY compkey, prodkey, installprop; + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + CHAR component[MAX_PATH]; + CHAR comp_base85[MAX_PATH]; + CHAR comp_squashed[MAX_PATH]; + CHAR keypath[MAX_PATH]; + CHAR path[MAX_PATH]; + INSTALLSTATE state; + LPSTR usersid; + DWORD size, val; + LONG res; + + create_test_guid(prodcode, prod_squashed); + compose_base85_guid(component, comp_base85, comp_squashed); + get_user_sid(&usersid); + + /* NULL szProduct */ + size = MAX_PATH; + state = MsiGetComponentPathA(NULL, component, path, &size); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* NULL szComponent */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, NULL, path, &size); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* NULL lpPathBuf */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, NULL, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* NULL pcchBuf */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, NULL); + ok(state == INSTALLSTATE_INVALIDARG, "Expected INSTALLSTATE_INVALIDARG, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* all params valid */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local system component key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"C:\\imapath", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product value exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, prod_squashed); + lstrcatA(keypath, "\\InstallProperties"); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &installprop); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + val = 1; + res = RegSetValueExA(installprop, "WindowsInstaller", 0, REG_DWORD, (const BYTE *)&val, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* install properties key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + create_file("C:\\imapath", "C:\\imapath", 11); + + /* file exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + RegDeleteValueA(compkey, prod_squashed); + RegDeleteKeyA(compkey, ""); + RegDeleteValueA(installprop, "WindowsInstaller"); + RegDeleteKeyA(installprop, ""); + RegCloseKey(compkey); + RegCloseKey(installprop); + DeleteFileA("C:\\imapath"); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user managed component key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"C:\\imapath", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product value exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, prod_squashed); + lstrcatA(keypath, "\\InstallProperties"); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &installprop); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + val = 1; + res = RegSetValueExA(installprop, "WindowsInstaller", 0, REG_DWORD, (const BYTE *)&val, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* install properties key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + create_file("C:\\imapath", "C:\\imapath", 11); + + /* file exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + RegDeleteValueA(compkey, prod_squashed); + RegDeleteKeyA(compkey, ""); + RegDeleteValueA(installprop, "WindowsInstaller"); + RegDeleteKeyA(installprop, ""); + RegCloseKey(compkey); + RegCloseKey(installprop); + DeleteFileA("C:\\imapath"); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\Managed\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user managed product key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user managed component key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"C:\\imapath", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product value exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, prod_squashed); + lstrcatA(keypath, "\\InstallProperties"); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &installprop); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + val = 1; + res = RegSetValueExA(installprop, "WindowsInstaller", 0, REG_DWORD, (const BYTE *)&val, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* install properties key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + create_file("C:\\imapath", "C:\\imapath", 11); + + /* file exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + RegDeleteValueA(compkey, prod_squashed); + RegDeleteKeyA(prodkey, ""); + RegDeleteKeyA(compkey, ""); + RegDeleteValueA(installprop, "WindowsInstaller"); + RegDeleteKeyA(installprop, ""); + RegCloseKey(prodkey); + RegCloseKey(compkey); + RegCloseKey(installprop); + DeleteFileA("C:\\imapath"); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user unmanaged product key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user unmanaged component key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"C:\\imapath", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product value exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + create_file("C:\\imapath", "C:\\imapath", 11); + + /* file exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + RegDeleteValueA(compkey, prod_squashed); + RegDeleteKeyA(prodkey, ""); + RegDeleteKeyA(compkey, ""); + RegCloseKey(prodkey); + RegCloseKey(compkey); + RegCloseKey(installprop); + DeleteFileA("C:\\imapath"); + + lstrcpyA(keypath, "Software\\Classes\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local classes product key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local user component key exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"C:\\imapath", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product value exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + create_file("C:\\imapath", "C:\\imapath", 11); + + /* file exists */ + size = MAX_PATH; + state = MsiGetComponentPathA(prodcode, component, path, &size); + ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok(!lstrcmpA(path, "C:\\imapath"), "Expected C:\\imapath, got %s\n", path); + ok(size == 10, "Expected 10, got %d\n", size); + + RegDeleteValueA(compkey, prod_squashed); + RegDeleteKeyA(prodkey, ""); + RegDeleteKeyA(compkey, ""); + RegCloseKey(prodkey); + RegCloseKey(compkey); + DeleteFileA("C:\\imapath"); +} + +static void test_MsiGetProductCode(void) +{ + HKEY compkey, prodkey; + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + CHAR prodcode2[MAX_PATH]; + CHAR prod2_squashed[MAX_PATH]; + CHAR component[MAX_PATH]; + CHAR comp_base85[MAX_PATH]; + CHAR comp_squashed[MAX_PATH]; + CHAR keypath[MAX_PATH]; + CHAR product[MAX_PATH]; + LPSTR usersid; + LONG res; + UINT r; + + create_test_guid(prodcode, prod_squashed); + create_test_guid(prodcode2, prod2_squashed); + compose_base85_guid(component, comp_base85, comp_squashed); + get_user_sid(&usersid); + + /* szComponent is NULL */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(NULL, product); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, "prod"), "Expected product to be unchanged, got %s\n", product); + + /* szComponent is empty */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA("", product); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, "prod"), "Expected product to be unchanged, got %s\n", product); + + /* garbage szComponent */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA("garbage", product); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, "prod"), "Expected product to be unchanged, got %s\n", product); + + /* guid without brackets */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D", product); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, "prod"), "Expected product to be unchanged, got %s\n", product); + + /* guid with brackets */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}", product); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(!lstrcmpA(product, "prod"), "Expected product to be unchanged, got %s\n", product); + + /* same length as guid, but random */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93", product); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, "prod"), "Expected product to be unchanged, got %s\n", product); + + /* all params correct, szComponent not published */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(!lstrcmpA(product, "prod"), "Expected product to be unchanged, got %s\n", product); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user unmanaged component key exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(!lstrcmpA(product, "prod"), "Expected product to be unchanged, got %s\n", product); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"C:\\imapath", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product value exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + res = RegSetValueExA(compkey, prod2_squashed, 0, REG_SZ, (const BYTE *)"C:\\another", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\Managed\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user managed product key of first product exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user unmanaged product key exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + lstrcpyA(keypath, "Software\\Classes\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local classes product key exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\Managed\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Installer\\Products\\"); + lstrcatA(keypath, prod2_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user managed product key of second product exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode2), "Expected %s, got %s\n", prodcode2, product); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + RegDeleteValueA(compkey, prod_squashed); + RegDeleteValueA(compkey, prod2_squashed); + RegDeleteKeyA(compkey, ""); + RegCloseKey(compkey); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local user component key exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(!lstrcmpA(product, "prod"), "Expected product to be unchanged, got %s\n", product); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"C:\\imapath", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product value exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + res = RegSetValueExA(compkey, prod2_squashed, 0, REG_SZ, (const BYTE *)"C:\\another", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\Managed\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user managed product key of first product exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user unmanaged product key exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + lstrcpyA(keypath, "Software\\Classes\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local classes product key exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\Managed\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Installer\\Products\\"); + lstrcatA(keypath, prod2_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user managed product key of second product exists */ + lstrcpyA(product, "prod"); + r = MsiGetProductCodeA(component, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode2), "Expected %s, got %s\n", prodcode2, product); + + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + RegDeleteValueA(compkey, prod_squashed); + RegDeleteValueA(compkey, prod2_squashed); + RegDeleteKeyA(compkey, ""); + RegCloseKey(compkey); +} + +static void test_MsiEnumClients(void) +{ + HKEY compkey; + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + CHAR prodcode2[MAX_PATH]; + CHAR prod2_squashed[MAX_PATH]; + CHAR component[MAX_PATH]; + CHAR comp_base85[MAX_PATH]; + CHAR comp_squashed[MAX_PATH]; + CHAR product[MAX_PATH]; + CHAR keypath[MAX_PATH]; + LPSTR usersid; + LONG res; + UINT r; + + create_test_guid(prodcode, prod_squashed); + create_test_guid(prodcode2, prod2_squashed); + compose_base85_guid(component, comp_base85, comp_squashed); + get_user_sid(&usersid); + + /* NULL szComponent */ + product[0] = '\0'; + r = MsiEnumClientsA(NULL, 0, product); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + + /* empty szComponent */ + product[0] = '\0'; + r = MsiEnumClientsA("", 0, product); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + + /* NULL lpProductBuf */ + r = MsiEnumClientsA(component, 0, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* all params correct, component missing */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 0, product); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user unmanaged component key exists */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 0, product); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + + /* index > 0, no products exist */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 1, product); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"C:\\imapath", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product value exists */ + r = MsiEnumClientsA(component, 0, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + /* try index 0 again */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 0, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + /* try index 1, second product value does not exist */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 1, product); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + + res = RegSetValueExA(compkey, prod2_squashed, 0, REG_SZ, (const BYTE *)"C:\\another", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* try index 1, second product value does exist */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 1, product); + todo_wine + { + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + } + + /* start the enumeration over */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 0, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode) || !lstrcmpA(product, prodcode2), + "Expected %s or %s, got %s\n", prodcode, prodcode2, product); + + /* correctly query second product */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 1, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode) || !lstrcmpA(product, prodcode2), + "Expected %s or %s, got %s\n", prodcode, prodcode2, product); + + RegDeleteValueA(compkey, prod_squashed); + RegDeleteValueA(compkey, prod2_squashed); + RegDeleteKeyA(compkey, ""); + RegCloseKey(compkey); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Components\\"); + lstrcatA(keypath, comp_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &compkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user local component key exists */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 0, product); + ok(r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + + /* index > 0, no products exist */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 1, product); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"C:\\imapath", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product value exists */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 0, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode), "Expected %s, got %s\n", prodcode, product); + + /* try index 0 again */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 0, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* try index 1, second product value does not exist */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 1, product); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + + res = RegSetValueExA(compkey, prod2_squashed, 0, REG_SZ, (const BYTE *)"C:\\another", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* try index 1, second product value does exist */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 1, product); + todo_wine + { + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(product, ""), "Expected product to be unchanged, got %s\n", product); + } + + /* start the enumeration over */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 0, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode) || !lstrcmpA(product, prodcode2), + "Expected %s or %s, got %s\n", prodcode, prodcode2, product); + + /* correctly query second product */ + product[0] = '\0'; + r = MsiEnumClientsA(component, 1, product); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(product, prodcode) || !lstrcmpA(product, prodcode2), + "Expected %s or %s, got %s\n", prodcode, prodcode2, product); + + RegDeleteValueA(compkey, prod_squashed); + RegDeleteValueA(compkey, prod2_squashed); + RegDeleteKeyA(compkey, ""); + RegCloseKey(compkey); +} + +static void get_version_info(LPSTR path, LPSTR *vercheck, LPDWORD verchecksz, + LPSTR *langcheck, LPDWORD langchecksz) +{ + LPSTR version; + VS_FIXEDFILEINFO *ffi; + DWORD size = GetFileVersionInfoSizeA(path, NULL); + USHORT *lang; + + version = HeapAlloc(GetProcessHeap(), 0, size); + GetFileVersionInfoA(path, 0, size, version); + + VerQueryValueA(version, "\\", (LPVOID *)&ffi, &size); + *vercheck = HeapAlloc(GetProcessHeap(), 0, MAX_PATH); + sprintf(*vercheck, "%d.%d.%d.%d", HIWORD(ffi->dwFileVersionMS), + LOWORD(ffi->dwFileVersionMS), HIWORD(ffi->dwFileVersionLS), + LOWORD(ffi->dwFileVersionLS)); + *verchecksz = lstrlenA(*vercheck); + + VerQueryValue(version, "\\VarFileInfo\\Translation", (void **)&lang, &size); + *langcheck = HeapAlloc(GetProcessHeap(), 0, MAX_PATH); + sprintf(*langcheck, "%d", *lang); + *langchecksz = lstrlenA(*langcheck); + + HeapFree(GetProcessHeap(), 0, version); +} + +static void test_MsiGetFileVersion(void) +{ + UINT r; + DWORD versz, langsz; + char version[MAX_PATH]; + char lang[MAX_PATH]; + char path[MAX_PATH]; + LPSTR vercheck, langcheck; + DWORD verchecksz, langchecksz; + + /* NULL szFilePath */ + versz = MAX_PATH; + langsz = MAX_PATH; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA(NULL, version, &versz, lang, &langsz); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(version, "version"), + "Expected version to be unchanged, got %s\n", version); + ok(versz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, versz); + ok(!lstrcmpA(lang, "lang"), + "Expected lang to be unchanged, got %s\n", lang); + ok(langsz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, langsz); + + /* empty szFilePath */ + versz = MAX_PATH; + langsz = MAX_PATH; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA("", version, &versz, lang, &langsz); + ok(r == ERROR_FILE_NOT_FOUND, + "Expected ERROR_FILE_NOT_FOUND, got %d\n", r); + ok(!lstrcmpA(version, "version"), + "Expected version to be unchanged, got %s\n", version); + ok(versz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, versz); + ok(!lstrcmpA(lang, "lang"), + "Expected lang to be unchanged, got %s\n", lang); + ok(langsz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, langsz); + + /* nonexistent szFilePath */ + versz = MAX_PATH; + langsz = MAX_PATH; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA("nonexistent", version, &versz, lang, &langsz); + ok(r == ERROR_FILE_NOT_FOUND, + "Expected ERROR_FILE_NOT_FOUND, got %d\n", r); + ok(!lstrcmpA(version, "version"), + "Expected version to be unchanged, got %s\n", version); + ok(versz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, versz); + ok(!lstrcmpA(lang, "lang"), + "Expected lang to be unchanged, got %s\n", lang); + ok(langsz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, langsz); + + /* nonexistent szFilePath, valid lpVersionBuf, NULL pcchVersionBuf */ + versz = MAX_PATH; + langsz = MAX_PATH; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA("nonexistent", version, NULL, lang, &langsz); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(version, "version"), + "Expected version to be unchanged, got %s\n", version); + ok(versz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, versz); + ok(!lstrcmpA(lang, "lang"), + "Expected lang to be unchanged, got %s\n", lang); + ok(langsz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, langsz); + + /* nonexistent szFilePath, valid lpLangBuf, NULL pcchLangBuf */ + versz = MAX_PATH; + langsz = MAX_PATH; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA("nonexistent", version, &versz, lang, NULL); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(version, "version"), + "Expected version to be unchanged, got %s\n", version); + ok(versz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, versz); + ok(!lstrcmpA(lang, "lang"), + "Expected lang to be unchanged, got %s\n", lang); + ok(langsz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, langsz); + + /* nonexistent szFilePath, valid lpVersionBuf, pcchVersionBuf is zero */ + versz = 0; + langsz = MAX_PATH; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA("nonexistent", version, &versz, lang, &langsz); + ok(r == ERROR_FILE_NOT_FOUND, + "Expected ERROR_FILE_NOT_FOUND, got %d\n", r); + ok(!lstrcmpA(version, "version"), + "Expected version to be unchanged, got %s\n", version); + ok(versz == 0, "Expected 0, got %d\n", versz); + ok(!lstrcmpA(lang, "lang"), + "Expected lang to be unchanged, got %s\n", lang); + ok(langsz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, langsz); + + /* nonexistent szFilePath, valid lpLangBuf, pcchLangBuf is zero */ + versz = MAX_PATH; + langsz = 0; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA("nonexistent", version, &versz, lang, &langsz); + ok(r == ERROR_FILE_NOT_FOUND, + "Expected ERROR_FILE_NOT_FOUND, got %d\n", r); + ok(!lstrcmpA(version, "version"), + "Expected version to be unchanged, got %s\n", version); + ok(versz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, versz); + ok(!lstrcmpA(lang, "lang"), + "Expected lang to be unchanged, got %s\n", lang); + ok(langsz == 0, "Expected 0, got %d\n", langsz); + + /* nonexistent szFilePath, rest NULL */ + r = MsiGetFileVersionA("nonexistent", NULL, NULL, NULL, NULL); + ok(r == ERROR_FILE_NOT_FOUND, + "Expected ERROR_FILE_NOT_FOUND, got %d\n", r); + + create_file("ver.txt", "ver.txt", 20); + + /* file exists, no version information */ + versz = MAX_PATH; + langsz = MAX_PATH; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA("ver.txt", version, &versz, lang, &langsz); + ok(versz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, versz); + ok(!lstrcmpA(version, "version"), + "Expected version to be unchanged, got %s\n", version); + ok(langsz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, langsz); + ok(!lstrcmpA(lang, "lang"), + "Expected lang to be unchanged, got %s\n", lang); + ok(r == ERROR_FILE_INVALID, + "Expected ERROR_FILE_INVALID, got %d\n", r); + + DeleteFileA("ver.txt"); + + /* relative path, has version information */ + versz = MAX_PATH; + langsz = MAX_PATH; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA("kernel32.dll", version, &versz, lang, &langsz); + todo_wine + { + ok(r == ERROR_FILE_NOT_FOUND, + "Expected ERROR_FILE_NOT_FOUND, got %d\n", r); + ok(!lstrcmpA(version, "version"), + "Expected version to be unchanged, got %s\n", version); + ok(versz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, versz); + ok(!lstrcmpA(lang, "lang"), + "Expected lang to be unchanged, got %s\n", lang); + ok(langsz == MAX_PATH, "Expected %d, got %d\n", MAX_PATH, langsz); + } + + GetSystemDirectoryA(path, MAX_PATH); + lstrcatA(path, "\\kernel32.dll"); + + get_version_info(path, &vercheck, &verchecksz, &langcheck, &langchecksz); + + /* absolute path, has version information */ + versz = MAX_PATH; + langsz = MAX_PATH; + lstrcpyA(version, "version"); + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA(path, version, &versz, lang, &langsz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(versz == verchecksz, "Expected %d, got %d\n", verchecksz, versz); + ok(!lstrcmpA(lang, langcheck), "Expected %s, got %s\n", langcheck, lang); + ok(langsz == langchecksz, "Expected %d, got %d\n", langchecksz, langsz); + ok(!lstrcmpA(version, vercheck), + "Expected %s, got %s\n", vercheck, version); + + /* only check version */ + versz = MAX_PATH; + lstrcpyA(version, "version"); + r = MsiGetFileVersionA(path, version, &versz, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(versz == verchecksz, "Expected %d, got %d\n", verchecksz, versz); + ok(!lstrcmpA(version, vercheck), + "Expected %s, got %s\n", vercheck, version); + + /* only check language */ + langsz = MAX_PATH; + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA(path, NULL, NULL, lang, &langsz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(lang, langcheck), "Expected %s, got %s\n", langcheck, lang); + ok(langsz == langchecksz, "Expected %d, got %d\n", langchecksz, langsz); + + /* get pcchVersionBuf */ + versz = MAX_PATH; + r = MsiGetFileVersionA(path, NULL, &versz, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(versz == verchecksz, "Expected %d, got %d\n", verchecksz, versz); + + /* get pcchLangBuf */ + langsz = MAX_PATH; + r = MsiGetFileVersionA(path, NULL, NULL, NULL, &langsz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(langsz == langchecksz, "Expected %d, got %d\n", langchecksz, langsz); + + /* pcchVersionBuf not big enough */ + versz = 5; + lstrcpyA(version, "version"); + r = MsiGetFileVersionA(path, version, &versz, NULL, NULL); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!strncmp(version, vercheck, 4), + "Expected first 4 characters of %s, got %s\n", vercheck, version); + ok(versz == verchecksz, "Expected %d, got %d\n", verchecksz, versz); + + /* pcchLangBuf not big enough */ + langsz = 3; + lstrcpyA(lang, "lang"); + r = MsiGetFileVersionA(path, NULL, NULL, lang, &langsz); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!strncmp(lang, langcheck, 2), + "Expected first character of %s, got %s\n", langcheck, lang); + ok(langsz == langchecksz, "Expected %d, got %d\n", langchecksz, langsz); + + HeapFree(GetProcessHeap(), 0, vercheck); + HeapFree(GetProcessHeap(), 0, langcheck); } START_TEST(msi) { - HMODULE hmod = GetModuleHandle("msi.dll"); - pMsiUseFeatureExA = (fnMsiUseFeatureExA) - GetProcAddress(hmod, "MsiUseFeatureExA"); - pMsiOpenPackageExA = (fnMsiOpenPackageExA) - GetProcAddress(hmod, "MsiOpenPackageExA"); - pMsiOpenPackageExW = (fnMsiOpenPackageExW) - GetProcAddress(hmod, "MsiOpenPackageExW"); - pMsiGetComponentPathA = (fnMsiGetComponentPathA) - GetProcAddress(hmod, "MsiGetComponentPathA" ); - pMsiGetFileHashA = (fnMsiGetFileHashA) - GetProcAddress(hmod, "MsiGetFileHashA" ); + init_functionpointers(); test_usefeature(); test_null(); test_getcomponentpath(); - test_filehash(); + test_MsiGetFileHash(); + + if (!pConvertSidToStringSidA) + skip("ConvertSidToStringSidA not implemented\n"); + else + { + /* These tests rely on get_user_sid that needs ConvertSidToStringSidA */ + test_MsiQueryProductState(); + test_MsiQueryFeatureState(); + test_MsiQueryComponentState(); + test_MsiGetComponentPath(); + test_MsiGetProductCode(); + test_MsiEnumClients(); + } + + test_MsiGetFileVersion(); } diff --git a/rostests/winetests/msi/msi.rbuild b/rostests/winetests/msi/msi.rbuild index d71ff11873f..545c8e3e842 100644 --- a/rostests/winetests/msi/msi.rbuild +++ b/rostests/winetests/msi/msi.rbuild @@ -1,18 +1,30 @@ + + + - . - - cabinet - msi - ole32 - advapi32 - kernel32 - ntdll - db.c - format.c - install.c - msi.c - package.c - record.c - suminfo.c - testlist.c + . + 0x600 + 0x600 + automation.c + db.c + format.c + install.c + msi.c + package.c + record.c + source.c + suminfo.c + testlist.c + wine + cabinet + msi + shell32 + ole32 + oleaut32 + advapi32 + kernel32 + version + uuid + ntdll + diff --git a/rostests/winetests/msi/package.c b/rostests/winetests/msi/package.c index 4d9eefae2cc..dc0e31ec41c 100644 --- a/rostests/winetests/msi/package.c +++ b/rostests/winetests/msi/package.c @@ -23,12 +23,100 @@ #include #include +#include #include #include #include "wine/test.h" static const char msifile[] = "winetest.msi"; +char CURR_DIR[MAX_PATH]; + +/* RegDeleteTreeW from dlls/advapi32/registry.c */ +LSTATUS WINAPI package_RegDeleteTreeW(HKEY hKey, LPCWSTR lpszSubKey) +{ + LONG ret; + DWORD dwMaxSubkeyLen, dwMaxValueLen; + DWORD dwMaxLen, dwSize; + WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf; + HKEY hSubKey = hKey; + + if(lpszSubKey) + { + ret = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey); + if (ret) return ret; + } + + ret = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL, + &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL); + if (ret) goto cleanup; + + dwMaxSubkeyLen++; + dwMaxValueLen++; + dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen); + if (dwMaxLen > sizeof(szNameBuf)/sizeof(WCHAR)) + { + /* Name too big: alloc a buffer for it */ + if (!(lpszName = HeapAlloc( GetProcessHeap(), 0, dwMaxLen*sizeof(WCHAR)))) + { + ret = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + } + + /* Recursively delete all the subkeys */ + while (TRUE) + { + dwSize = dwMaxLen; + if (RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, + NULL, NULL, NULL)) break; + + ret = package_RegDeleteTreeW(hSubKey, lpszName); + if (ret) goto cleanup; + } + + if (lpszSubKey) + ret = RegDeleteKeyW(hKey, lpszSubKey); + else + while (TRUE) + { + dwSize = dwMaxLen; + if (RegEnumValueW(hKey, 0, lpszName, &dwSize, + NULL, NULL, NULL, NULL)) break; + + ret = RegDeleteValueW(hKey, lpszName); + if (ret) goto cleanup; + } + +cleanup: + if (lpszName != szNameBuf) + HeapFree(GetProcessHeap(), 0, lpszName); + if(lpszSubKey) + RegCloseKey(hSubKey); + return ret; +} + +static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec) +{ + MSIHANDLE hview = 0; + UINT r, ret; + + /* open a select query */ + r = MsiDatabaseOpenView(hdb, query, &hview); + if (r != ERROR_SUCCESS) + return r; + r = MsiViewExecute(hview, 0); + if (r != ERROR_SUCCESS) + return r; + ret = MsiViewFetch(hview, phrec); + r = MsiViewClose(hview); + if (r != ERROR_SUCCESS) + return r; + r = MsiCloseHandle(hview); + if (r != ERROR_SUCCESS) + return r; + return ret; +} static UINT run_query( MSIHANDLE hdb, const char *query ) { @@ -147,6 +235,76 @@ static UINT create_signature_table( MSIHANDLE hdb ) "PRIMARY KEY `Signature`)" ); } +static UINT create_launchcondition_table( MSIHANDLE hdb ) +{ + return run_query( hdb, + "CREATE TABLE `LaunchCondition` (" + "`Condition` CHAR(255) NOT NULL, " + "`Description` CHAR(255) NOT NULL " + "PRIMARY KEY `Condition`)" ); +} + +static UINT create_property_table( MSIHANDLE hdb ) +{ + return run_query( hdb, + "CREATE TABLE `Property` (" + "`Property` CHAR(72) NOT NULL, " + "`Value` CHAR(0) " + "PRIMARY KEY `Property`)" ); +} + +static UINT create_install_execute_sequence_table( MSIHANDLE hdb ) +{ + return run_query( hdb, + "CREATE TABLE `InstallExecuteSequence` (" + "`Action` CHAR(72) NOT NULL, " + "`Condition` CHAR(255), " + "`Sequence` SHORT " + "PRIMARY KEY `Action`)" ); +} + +static UINT create_media_table( MSIHANDLE hdb ) +{ + return run_query( hdb, + "CREATE TABLE `Media` (" + "`DiskId` SHORT NOT NULL, " + "`LastSequence` SHORT NOT NULL, " + "`DiskPrompt` CHAR(64), " + "`Cabinet` CHAR(255), " + "`VolumeLabel` CHAR(32), " + "`Source` CHAR(72) " + "PRIMARY KEY `DiskId`)" ); +} + +static UINT create_ccpsearch_table( MSIHANDLE hdb ) +{ + return run_query( hdb, + "CREATE TABLE `CCPSearch` (" + "`Signature_` CHAR(72) NOT NULL " + "PRIMARY KEY `Signature_`)" ); +} + +static UINT create_drlocator_table( MSIHANDLE hdb ) +{ + return run_query( hdb, + "CREATE TABLE `DrLocator` (" + "`Signature_` CHAR(72) NOT NULL, " + "`Parent` CHAR(72), " + "`Path` CHAR(255), " + "`Depth` SHORT " + "PRIMARY KEY `Signature_`, `Parent`, `Path`)" ); +} + +static UINT create_complocator_table( MSIHANDLE hdb ) +{ + return run_query( hdb, + "CREATE TABLE `CompLocator` (" + "`Signature_` CHAR(72) NOT NULL, " + "`ComponentId` CHAR(38) NOT NULL, " + "`Type` SHORT " + "PRIMARY KEY `Signature_`)" ); +} + static UINT add_component_entry( MSIHANDLE hdb, const char *values ) { char insert[] = "INSERT INTO `Component` " @@ -259,6 +417,116 @@ static UINT add_signature_entry( MSIHANDLE hdb, const char *values ) return r; } +static UINT add_launchcondition_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `LaunchCondition` " + "(`Condition`, `Description`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + +static UINT add_property_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `Property` " + "(`Property`, `Value`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + +static UINT add_install_execute_sequence_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `InstallExecuteSequence` " + "(`Action`, `Condition`, `Sequence`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + +static UINT add_media_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `Media` " + "(`DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + +static UINT add_ccpsearch_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `CCPSearch` (`Signature_`) VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + +static UINT add_drlocator_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `DrLocator` " + "(`Signature_`, `Parent`, `Path`, `Depth`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + +static UINT add_complocator_entry( MSIHANDLE hdb, const char *values ) +{ + char insert[] = "INSERT INTO `CompLocator` " + "(`Signature_`, `ComponentId`, `Type`) " + "VALUES( %s )"; + char *query; + UINT sz, r; + + sz = strlen(values) + sizeof insert; + query = HeapAlloc(GetProcessHeap(),0,sz); + sprintf(query,insert,values); + r = run_query( hdb, query ); + HeapFree(GetProcessHeap(), 0, query); + return r; +} + static UINT set_summary_info(MSIHANDLE hdb) { UINT res; @@ -304,7 +572,7 @@ static UINT set_summary_info(MSIHANDLE hdb) } -MSIHANDLE create_package_db(void) +static MSIHANDLE create_package_db(void) { MSIHANDLE hdb = 0; UINT res; @@ -333,7 +601,7 @@ MSIHANDLE create_package_db(void) return hdb; } -MSIHANDLE package_from_db(MSIHANDLE hdb) +static MSIHANDLE package_from_db(MSIHANDLE hdb) { UINT res; CHAR szPackage[10]; @@ -341,10 +609,12 @@ MSIHANDLE package_from_db(MSIHANDLE hdb) sprintf(szPackage,"#%li",hdb); res = MsiOpenPackage(szPackage,&hPackage); - ok( res == ERROR_SUCCESS , "Failed to open package\n" ); + if (res != ERROR_SUCCESS) + return 0; res = MsiCloseHandle(hdb); - ok( res == ERROR_SUCCESS , "Failed to close db handle\n" ); + if (res != ERROR_SUCCESS) + return 0; return hPackage; } @@ -461,7 +731,6 @@ static void test_getsourcepath( void ) r = MsiDoAction( hpkg, "CostFinalize"); ok( r == ERROR_SUCCESS, "cost finalize failed\n"); - todo_wine { sz = sizeof buffer -1; buffer[0] = 'x'; r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz ); @@ -472,13 +741,10 @@ static void test_getsourcepath( void ) strcpy(buffer,"x bad"); r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz ); ok( r == ERROR_MORE_DATA, "return value wrong\n"); - } ok( buffer[0] == 'x', "buffer modified\n"); - todo_wine { r = MsiGetSourcePath( hpkg, "TARGETDIR", NULL, NULL ); ok( r == ERROR_SUCCESS, "return value wrong\n"); - } r = MsiGetSourcePath( hpkg, "TARGETDIR ", NULL, NULL ); ok( r == ERROR_DIRECTORY, "return value wrong\n"); @@ -489,10 +755,8 @@ static void test_getsourcepath( void ) r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, NULL ); ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n"); - todo_wine { r = MsiGetSourcePath( hpkg, "TARGETDIR", NULL, &sz ); ok( r == ERROR_SUCCESS, "return value wrong\n"); - } MsiCloseHandle( hpkg ); DeleteFile(msifile); @@ -575,7 +839,7 @@ static void query_file_path(MSIHANDLE hpkg, LPCSTR file, LPSTR buff) static void test_settargetpath(void) { - char tempdir[MAX_PATH+8], buffer[MAX_PATH]; + char tempdir[MAX_PATH+8], buffer[MAX_PATH], file[MAX_PATH]; DWORD sz; MSIHANDLE hpkg; UINT r; @@ -587,81 +851,40 @@ static void test_settargetpath(void) r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'" ); ok( r == S_OK, "failed to add directory entry: %d\n" , r ); - r = run_query( hdb, /* these tables required by Windows Installer for MsiSetTargetPath */ - "CREATE TABLE `Component` ( " - "`Component` CHAR(72) NOT NULL, " - "`ComponentId` CHAR(38), " - "`Directory_` CHAR(72) NOT NULL, " - "`Attributes` SHORT NOT NULL, " - "`Condition` CHAR(255), " - "`KeyPath` CHAR(72) " - "PRIMARY KEY `Component`)" ); + r = create_component_table( hdb ); ok( r == S_OK, "cannot create Component table: %d\n", r ); - r = run_query( hdb, - "INSERT INTO `Component` " - "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) " - "VALUES( 'WinWorkAround', '{83e2694d-0864-4124-9323-6d37630912a1}', 'TARGETDIR', 8, '', 'FL_dummycomponent')" ); + r = add_component_entry( hdb, "'RootComp', '{83e2694d-0864-4124-9323-6d37630912a1}', 'TARGETDIR', 8, '', 'RootFile'" ); ok( r == S_OK, "cannot add dummy component: %d\n", r ); - r = run_query( hdb, - "INSERT INTO `Component` " - "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) " - "VALUES( 'TestComp', '{A3FB59C8-C293-4F7E-B8C5-F0E1D8EEE4E5}', 'TestDir', 0, '', 'TestFile')" ); + r = add_component_entry( hdb, "'TestComp', '{A3FB59C8-C293-4F7E-B8C5-F0E1D8EEE4E5}', 'TestDir', 0, '', 'TestFile'" ); ok( r == S_OK, "cannot add test component: %d\n", r ); - r = run_query( hdb, - "CREATE TABLE `Feature` ( " - "`Feature` CHAR(38) NOT NULL, " - "`Feature_Parent` CHAR(38), " - "`Title` CHAR(64), " - "`Description` CHAR(255), " - "`Display` SHORT NOT NULL, " - "`Level` SHORT NOT NULL, " - "`Directory_` CHAR(72), " - "`Attributes` SHORT NOT NULL " - "PRIMARY KEY `Feature`)" ); + r = create_feature_table( hdb ); ok( r == S_OK, "cannot create Feature table: %d\n", r ); - r = run_query( hdb, - "INSERT INTO `Feature` " - "(`Feature`, `Feature_Parent`, `Display`, `Level`, `Attributes`) " - "VALUES( 'TestFeature', '', 0, 1, 0 )" ); + r = add_feature_entry( hdb, "'TestFeature', '', '', '', 0, 1, '', 0" ); ok( r == ERROR_SUCCESS, "cannot add TestFeature to Feature table: %d\n", r ); - r = run_query( hdb, - "CREATE TABLE `FeatureComponents` ( " - "`Feature_` CHAR(38) NOT NULL, " - "`Component_` CHAR(72) NOT NULL " - "PRIMARY KEY `Feature_` )" ); + r = create_feature_components_table( hdb ); ok( r == S_OK, "cannot create FeatureComponents table: %d\n", r ); - r = run_query( hdb, - "INSERT INTO `FeatureComponents` " - "(`Feature_`, `Component_`) " - "VALUES( 'TestFeature', 'TestComp' )" ); + r = add_feature_components_entry( hdb, "'TestFeature', 'RootComp'" ); + ok( r == S_OK, "cannot insert component into FeatureComponents table: %d\n", r ); + + r = add_feature_components_entry( hdb, "'TestFeature', 'TestComp'" ); ok( r == S_OK, "cannot insert component into FeatureComponents table: %d\n", r ); add_directory_entry( hdb, "'TestParent', 'TARGETDIR', 'TestParent'" ); add_directory_entry( hdb, "'TestDir', 'TestParent', 'TestDir'" ); - r = run_query( hdb, - "CREATE TABLE `File` (" - "`File` CHAR(72) NOT NULL, " - "`Component_` CHAR(72) NOT NULL, " - "`FileName` CHAR(255) NOT NULL, " - "`FileSize` LONG NOT NULL, " - "`Version` CHAR(72), " - "`Language` CHAR(20), " - "`Attributes` SHORT, " - "`Sequence` SHORT NOT NULL " - "PRIMARY KEY `File`)" ); + r = create_file_table( hdb ); ok( r == S_OK, "cannot create File table: %d\n", r ); - r = run_query( hdb, - "INSERT INTO `File` " - "(`File`, `Component_`, `FileName`, `FileSize`, `Version`, `Language`, `Attributes`, `Sequence`) " - "VALUES( 'TestFile', 'TestComp', 'testfile.txt', 0, '', '1033', 8192, 1 )" ); + r = add_file_entry( hdb, "'RootFile', 'RootComp', 'rootfile.txt', 0, '', '1033', 8192, 1" ); + ok( r == S_OK, "cannot add file to the File table: %d\n", r ); + + r = add_file_entry( hdb, "'TestFile', 'TestComp', 'testfile.txt', 0, '', '1033', 8192, 1" ); ok( r == S_OK, "cannot add file to the File table: %d\n", r ); hpkg = package_from_db( hdb ); @@ -690,33 +913,40 @@ static void test_settargetpath(void) sz = sizeof tempdir - 1; r = MsiGetTargetPath( hpkg, "TARGETDIR", tempdir, &sz ); - if ( r == S_OK ) - { - if ( GetTempFileName( tempdir, "_wt", 0, buffer ) ) - { - sprintf( tempdir, "%s\\subdir", buffer ); - r = MsiSetTargetPath( hpkg, "TARGETDIR", buffer ); - ok( r == ERROR_SUCCESS, "MsiSetTargetPath on file returned %d\n", r ); + sprintf( file, "%srootfile.txt", tempdir ); + query_file_path( hpkg, "[#RootFile]", buffer ); + ok( r == ERROR_SUCCESS, "failed to get target path: %d\n", r); + ok( !lstrcmp(buffer, file), "Expected %s, got %s\n", file, buffer ); - r = MsiSetTargetPath( hpkg, "TARGETDIR", tempdir ); - ok( r == ERROR_SUCCESS, "MsiSetTargetPath on 'subdir' of file returned %d\n", r ); + GetTempFileName( tempdir, "_wt", 0, buffer ); + sprintf( tempdir, "%s\\subdir", buffer ); - DeleteFile( buffer ); + r = MsiSetTargetPath( hpkg, "TARGETDIR", buffer ); + ok( r == ERROR_SUCCESS, "MsiSetTargetPath on file returned %d\n", r ); - r = MsiSetTargetPath( hpkg, "TARGETDIR", buffer ); - ok( r == ERROR_SUCCESS, "MsiSetTargetPath returned %d\n", r ); + r = MsiSetTargetPath( hpkg, "TARGETDIR", tempdir ); + ok( r == ERROR_SUCCESS, "MsiSetTargetPath on 'subdir' of file returned %d\n", r ); - r = GetFileAttributes( buffer ); - ok ( r == INVALID_FILE_ATTRIBUTES, "file/directory exists after MsiSetTargetPath. Attributes: %08X\n", r ); + DeleteFile( buffer ); - r = MsiSetTargetPath( hpkg, "TARGETDIR", tempdir ); - ok( r == ERROR_SUCCESS, "MsiSetTargetPath on subsubdir returned %d\n", r ); - } else { - trace("GetTempFileName failed, cannot do some tests\n"); - } - } else { - trace( "MsiGetTargetPath failed: %d\n", r ); - } + r = MsiSetTargetPath( hpkg, "TARGETDIR", buffer ); + ok( r == ERROR_SUCCESS, "MsiSetTargetPath returned %d\n", r ); + + r = GetFileAttributes( buffer ); + ok ( r == INVALID_FILE_ATTRIBUTES, "file/directory exists after MsiSetTargetPath. Attributes: %08X\n", r ); + + r = MsiSetTargetPath( hpkg, "TARGETDIR", tempdir ); + ok( r == ERROR_SUCCESS, "MsiSetTargetPath on subsubdir returned %d\n", r ); + + sz = sizeof buffer - 1; + lstrcat( tempdir, "\\" ); + r = MsiGetTargetPath( hpkg, "TARGETDIR", buffer, &sz ); + ok( r == ERROR_SUCCESS, "failed to get target path: %d\n", r); + ok( !lstrcmp(buffer, tempdir), "Expected %s, got %s\n", tempdir, buffer); + + sprintf( file, "%srootfile.txt", tempdir ); + query_file_path( hpkg, "[#RootFile]", buffer ); + ok( !lstrcmp(buffer, file), "Expected %s, got %s\n", file, buffer); r = MsiSetTargetPath( hpkg, "TestParent", "C:\\one\\two" ); ok( r == ERROR_SUCCESS, "MsiSetTargetPath returned %d\n", r ); @@ -725,6 +955,11 @@ static void test_settargetpath(void) ok( !lstrcmp(buffer, "C:\\one\\two\\TestDir\\testfile.txt"), "Expected C:\\one\\two\\TestDir\\testfile.txt, got %s\n", buffer ); + sz = sizeof buffer - 1; + r = MsiGetTargetPath( hpkg, "TestParent", buffer, &sz ); + ok( r == ERROR_SUCCESS, "failed to get target path: %d\n", r); + ok( !lstrcmp(buffer, "C:\\one\\two\\"), "Expected C:\\one\\two\\, got %s\n", buffer); + MsiCloseHandle( hpkg ); } @@ -1231,6 +1466,162 @@ static void test_condition(void) r = MsiEvaluateCondition(hpkg, "one >> two"); ok( r == MSICONDITION_TRUE, "wrong return val\n"); + MsiSetProperty(hpkg, "MsiNetAssemblySupport", NULL); /* make sure it's empty */ + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1.1.4322\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport > \"1.1.4322\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport >= \"1.1.4322\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport <= \"1.1.4322\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport <> \"1.1.4322\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport ~< \"1.1.4322\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"abcd\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"a1.1.4322\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1.1.4322a\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"0000001.1.4322\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1.1.4322.1\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1.1.4322.1.1\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "\"2\" < \"1.1"); + ok( r == MSICONDITION_ERROR, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "\"2\" < \"1.1\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "\"2\" < \"12.1\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "\"02.1\" < \"2.11\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "\"02.1.1\" < \"2.1\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1.1\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"0\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"-1\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"a\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"!\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"!\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"/\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \" \""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"azAZ_\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"a[a]\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"a[a]a\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"[a]\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"[a]a\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"{a}\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"{a\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"[a\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"a{\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"a]\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"A\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + MsiSetProperty(hpkg, "MsiNetAssemblySupport", "1.1.4322"); + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1.1.4322\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1.1.14322\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1.1.5\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1.1\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "MsiNetAssemblySupport < \"1\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + MsiSetProperty(hpkg, "one", "1"); + r = MsiEvaluateCondition(hpkg, "one < \"1\""); + ok( r == MSICONDITION_FALSE, "wrong return val\n"); + + MsiSetProperty(hpkg, "X", "5.0"); + + r = MsiEvaluateCondition(hpkg, "X != \"\""); + ok( r == MSICONDITION_ERROR, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "X =\"5.0\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "X =\"5.1\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "X =\"6.0\""); + ok( r == MSICONDITION_FALSE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "X =\"5.0\" or X =\"5.1\" or X =\"6.0\""); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "(X =\"5.0\" or X =\"5.1\" or X =\"6.0\")"); + ok( r == MSICONDITION_TRUE, "wrong return val (%d)\n", r); + + r = MsiEvaluateCondition(hpkg, "X !=\"\" and (X =\"5.0\" or X =\"5.1\" or X =\"6.0\")"); + ok( r == MSICONDITION_ERROR, "wrong return val (%d)\n", r); + MsiCloseHandle( hpkg ); DeleteFile(msifile); } @@ -1249,12 +1640,26 @@ static BOOL check_prop_empty( MSIHANDLE hpkg, const char * prop) static void test_props(void) { - MSIHANDLE hpkg; + MSIHANDLE hpkg, hdb; UINT r; DWORD sz; char buffer[0x100]; - hpkg = package_from_db(create_package_db()); + hdb = create_package_db(); + r = run_query( hdb, + "CREATE TABLE `Property` ( " + "`Property` CHAR(255) NOT NULL, " + "`Value` CHAR(255) " + "PRIMARY KEY `Property`)" ); + ok( r == ERROR_SUCCESS , "Failed\n" ); + + r = run_query(hdb, + "INSERT INTO `Property` " + "(`Property`, `Value`) " + "VALUES( 'MetadataCompName', 'Photoshop.dll' )"); + ok( r == ERROR_SUCCESS , "Failed\n" ); + + hpkg = package_from_db( hdb ); ok( hpkg, "failed to create package\n"); /* test invalid values */ @@ -1354,10 +1759,207 @@ static void test_props(void) ok( !strcmp(buffer,"xy"), "buffer was not changed\n"); ok( sz == 3, "wrong size returned\n"); + r = MsiSetProperty(hpkg, "SourceDir", "foo"); + ok( r == ERROR_SUCCESS, "wrong return val\n"); + + sz = 4; + r = MsiGetProperty(hpkg, "SOURCEDIR", buffer, &sz); + ok( r == ERROR_SUCCESS, "wrong return val\n"); + ok( !strcmp(buffer,""), "buffer wrong\n"); + ok( sz == 0, "wrong size returned\n"); + + sz = 4; + r = MsiGetProperty(hpkg, "SOMERANDOMNAME", buffer, &sz); + ok( r == ERROR_SUCCESS, "wrong return val\n"); + ok( !strcmp(buffer,""), "buffer wrong\n"); + ok( sz == 0, "wrong size returned\n"); + + sz = 4; + r = MsiGetProperty(hpkg, "SourceDir", buffer, &sz); + ok( r == ERROR_SUCCESS, "wrong return val\n"); + ok( !strcmp(buffer,"foo"), "buffer wrong\n"); + ok( sz == 3, "wrong size returned\n"); + + r = MsiSetProperty(hpkg, "MetadataCompName", "Photoshop.dll"); + ok( r == ERROR_SUCCESS, "wrong return val\n"); + + sz = 0; + r = MsiGetProperty(hpkg, "MetadataCompName", NULL, &sz ); + ok( r == ERROR_SUCCESS, "return wrong\n"); + ok( sz == 13, "size wrong (%d)\n", sz); + + sz = 13; + r = MsiGetProperty(hpkg, "MetadataCompName", buffer, &sz ); + ok( r == ERROR_MORE_DATA, "return wrong\n"); + ok( !strcmp(buffer,"Photoshop.dl"), "buffer wrong\n"); + + r = MsiSetProperty(hpkg, "property", "value"); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = 6; + r = MsiGetProperty(hpkg, "property", buffer, &sz); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok( !strcmp(buffer, "value"), "Expected value, got %s\n", buffer); + + r = MsiSetProperty(hpkg, "property", NULL); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = 6; + r = MsiGetProperty(hpkg, "property", buffer, &sz); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok( !strlen(buffer), "Expected empty string, got %s\n", buffer); + MsiCloseHandle( hpkg ); DeleteFile(msifile); } +static BOOL find_prop_in_property(MSIHANDLE hdb, LPCSTR prop, LPCSTR val) +{ + MSIHANDLE hview, hrec; + BOOL found; + CHAR buffer[MAX_PATH]; + DWORD sz; + UINT r; + + r = MsiDatabaseOpenView(hdb, "SELECT * FROM `_Property`", &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + found = FALSE; + while (r == ERROR_SUCCESS && !found) + { + r = MsiViewFetch(hview, &hrec); + if (r != ERROR_SUCCESS) break; + + sz = MAX_PATH; + r = MsiRecordGetString(hrec, 1, buffer, &sz); + if (r == ERROR_SUCCESS && !lstrcmpA(buffer, prop)) + { + sz = MAX_PATH; + r = MsiRecordGetString(hrec, 2, buffer, &sz); + if (r == ERROR_SUCCESS && !lstrcmpA(buffer, val)) + found = TRUE; + } + + MsiCloseHandle(hrec); + } + + MsiViewClose(hview); + MsiCloseHandle(hview); + + return found; +} + +static void test_property_table(void) +{ + const char *query; + UINT r; + MSIHANDLE hpkg, hdb, hrec; + char buffer[MAX_PATH]; + DWORD sz; + BOOL found; + + hdb = create_package_db(); + ok( hdb, "failed to create package\n"); + + hpkg = package_from_db(hdb); + ok( hpkg, "failed to create package\n"); + + MsiCloseHandle(hdb); + + hdb = MsiGetActiveDatabase(hpkg); + + query = "CREATE TABLE `_Property` ( " + "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)"; + r = run_query(hdb, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + MsiCloseHandle(hdb); + MsiCloseHandle(hpkg); + DeleteFile(msifile); + + hdb = create_package_db(); + ok( hdb, "failed to create package\n"); + + query = "CREATE TABLE `_Property` ( " + "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)"; + r = run_query(hdb, query); + ok(r == ERROR_SUCCESS, "failed to create table\n"); + + query = "ALTER `_Property` ADD `foo` INTEGER"; + r = run_query(hdb, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "failed to add column\n"); + + query = "ALTER TABLE `_Property` ADD `foo` INTEGER"; + r = run_query(hdb, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "failed to add column\n"); + + query = "ALTER TABLE `_Property` ADD `extra` INTEGER"; + r = run_query(hdb, query); + ok(r == ERROR_SUCCESS, "failed to add column\n"); + + hpkg = package_from_db(hdb); + todo_wine + { + ok(!hpkg, "package should not be created\n"); + } + + MsiCloseHandle(hdb); + MsiCloseHandle(hpkg); + DeleteFile(msifile); + + hdb = create_package_db(); + ok (hdb, "failed to create package database\n"); + + r = create_property_table(hdb); + ok(r == ERROR_SUCCESS, "cannot create Property table: %d\n", r); + + r = add_property_entry(hdb, "'prop', 'val'"); + ok(r == ERROR_SUCCESS, "cannot add property: %d\n", r); + + hpkg = package_from_db(hdb); + ok(hpkg, "failed to create package\n"); + + MsiCloseHandle(hdb); + + sz = MAX_PATH; + r = MsiGetProperty(hpkg, "prop", buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "val"), "Expected val, got %s\n", buffer); + + hdb = MsiGetActiveDatabase(hpkg); + + found = find_prop_in_property(hdb, "prop", "val"); + ok(found, "prop should be in the _Property table\n"); + + r = add_property_entry(hdb, "'dantes', 'mercedes'"); + ok(r == ERROR_SUCCESS, "cannot add property: %d\n", r); + + query = "SELECT * FROM `_Property` WHERE `Property` = 'dantes'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + found = find_prop_in_property(hdb, "dantes", "mercedes"); + ok(found == FALSE, "dantes should not be in the _Property table\n"); + + sz = MAX_PATH; + lstrcpy(buffer, "aaa"); + r = MsiGetProperty(hpkg, "dantes", buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(lstrlenA(buffer) == 0, "Expected empty string, got %s\n", buffer); + + r = MsiSetProperty(hpkg, "dantes", "mercedes"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + found = find_prop_in_property(hdb, "dantes", "mercedes"); + ok(found == TRUE, "dantes should be in the _Property table\n"); + + MsiCloseHandle(hdb); + MsiCloseHandle(hpkg); + DeleteFile(msifile); +} + static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec ) { MSIHANDLE htab = 0; @@ -1391,6 +1993,40 @@ static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery ) return try_query_param( hdb, szQuery, 0 ); } +static void set_summary_str(MSIHANDLE hdb, DWORD pid, LPCSTR value) +{ + MSIHANDLE summary; + UINT r; + + r = MsiGetSummaryInformationA(hdb, NULL, 1, &summary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiSummaryInfoSetPropertyA(summary, pid, VT_LPSTR, 0, NULL, value); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + r = MsiSummaryInfoPersist(summary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + MsiCloseHandle(summary); +} + +static void set_summary_dword(MSIHANDLE hdb, DWORD pid, DWORD value) +{ + MSIHANDLE summary; + UINT r; + + r = MsiGetSummaryInformationA(hdb, NULL, 1, &summary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiSummaryInfoSetPropertyA(summary, pid, VT_I4, value, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + r = MsiSummaryInfoPersist(summary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + MsiCloseHandle(summary); +} + static void test_msipackage(void) { MSIHANDLE hdb = 0, hpack = 100; @@ -1398,55 +2034,86 @@ static void test_msipackage(void) const char *query; char name[10]; - DeleteFile(msifile); + /* NULL szPackagePath */ + r = MsiOpenPackage(NULL, &hpack); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); - todo_wine { - name[0] = 0; - r = MsiOpenPackage(name, &hpack); - ok(r == ERROR_SUCCESS, "failed to open package with no name\n"); - r = MsiCloseHandle(hpack); - ok(r == ERROR_SUCCESS, "failed to close package\n"); + /* empty szPackagePath */ + r = MsiOpenPackage("", &hpack); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); } - /* just MsiOpenDatabase should not create a file */ - r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); - ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + if (r == ERROR_SUCCESS) + MsiCloseHandle(hpack); + + /* nonexistent szPackagePath */ + r = MsiOpenPackage("nonexistent", &hpack); + ok(r == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", r); + + /* NULL hProduct */ + r = MsiOpenPackage(msifile, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); name[0]='#'; name[1]=0; r = MsiOpenPackage(name, &hpack); - ok(r == ERROR_INVALID_HANDLE, "MsiOpenPackage returned wrong code\n"); + ok(r == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", r); - todo_wine { - /* now try again with our empty database */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* database exists, but is emtpy */ sprintf(name, "#%ld", hdb); r = MsiOpenPackage(name, &hpack); - ok(r == ERROR_INSTALL_PACKAGE_INVALID, "MsiOpenPackage returned wrong code\n"); - if (!r) MsiCloseHandle(hpack); - } + ok(r == ERROR_INSTALL_PACKAGE_INVALID, + "Expected ERROR_INSTALL_PACKAGE_INVALID, got %d\n", r); - /* create a table */ query = "CREATE TABLE `Property` ( " "`Property` CHAR(72), `Value` CHAR(0) " "PRIMARY KEY `Property`)"; r = try_query(hdb, query); ok(r == ERROR_SUCCESS, "failed to create Properties table\n"); - todo_wine { query = "CREATE TABLE `InstallExecuteSequence` (" "`Action` CHAR(72), `Condition` CHAR(0), `Sequence` INTEGER " "PRIMARY KEY `Action`)"; r = try_query(hdb, query); ok(r == ERROR_SUCCESS, "failed to create InstallExecuteSequence table\n"); + /* a few key tables exist */ sprintf(name, "#%ld", hdb); r = MsiOpenPackage(name, &hpack); - ok(r == ERROR_INSTALL_PACKAGE_INVALID, "MsiOpenPackage returned wrong code\n"); - if (!r) MsiCloseHandle(hpack); - } + ok(r == ERROR_INSTALL_PACKAGE_INVALID, + "Expected ERROR_INSTALL_PACKAGE_INVALID, got %d\n", r); - r = MsiCloseHandle(hdb); - ok(r == ERROR_SUCCESS, "MsiCloseHandle(database) failed\n"); + MsiCloseHandle(hdb); + DeleteFile(msifile); + + /* start with a clean database to show what constitutes a valid package */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sprintf(name, "#%ld", hdb); + + /* The following summary information props must exist: + * - PID_REVNUMBER + * - PID_PAGECOUNT + */ + + set_summary_dword(hdb, PID_PAGECOUNT, 100); + r = MsiOpenPackage(name, &hpack); + ok(r == ERROR_INSTALL_PACKAGE_INVALID, + "Expected ERROR_INSTALL_PACKAGE_INVALID, got %d\n", r); + + set_summary_str(hdb, PID_REVNUMBER, "{004757CD-5092-49c2-AD20-28E1CE0DF5F2}"); + r = MsiOpenPackage(name, &hpack); + ok(r == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hpack); + MsiCloseHandle(hdb); DeleteFile(msifile); } @@ -1524,6 +2191,9 @@ static void test_formatrecord2(void) DeleteFile(msifile); } +/* FIXME: state is INSTALLSTATE_UNKNOWN if any features are removed and the + * feature in question is not in ADD* + */ static void test_states(void) { MSIHANDLE hpkg; @@ -1537,6 +2207,63 @@ static void test_states(void) r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'"); ok( r == ERROR_SUCCESS, "cannot add directory: %d\n", r ); + r = create_property_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create Property table: %d\n", r ); + + r = add_property_entry( hdb, "'ProductCode', '{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}'" ); + ok( r == ERROR_SUCCESS, "cannot add property entry: %d\n", r ); + + r = add_property_entry( hdb, "'ProductLanguage', '1033'" ); + ok( r == ERROR_SUCCESS, "cannot add property entry: %d\n", r ); + + r = add_property_entry( hdb, "'ProductName', 'MSITEST'" ); + ok( r == ERROR_SUCCESS, "cannot add property entry: %d\n", r ); + + r = add_property_entry( hdb, "'ProductVersion', '1.1.1'" ); + ok( r == ERROR_SUCCESS, "cannot add property entry: %d\n", r ); + + r = create_install_execute_sequence_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create InstallExecuteSequence table: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'CostInitialize', '', '800'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'FileCost', '', '900'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'CostFinalize', '', '1000'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'InstallValidate', '', '1400'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'InstallInitialize', '', '1500'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'ProcessComponents', '', '1600'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'UnpublishFeatures', '', '1800'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'RegisterProduct', '', '6100'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'PublishFeatures', '', '6300'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'PublishProduct', '', '6400'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = add_install_execute_sequence_entry( hdb, "'InstallFinalize', '', '6600'" ); + ok( r == ERROR_SUCCESS, "cannot add install execute sequence entry: %d\n", r ); + + r = create_media_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create media table: %d\n", r ); + + r = add_media_entry( hdb, "'1', '3', '', '', 'DISK1', ''"); + ok( r == ERROR_SUCCESS, "cannot add media entry: %d\n", r ); + r = create_feature_table( hdb ); ok( r == ERROR_SUCCESS, "cannot create Feature table: %d\n", r ); @@ -1587,10 +2314,62 @@ static void test_states(void) r = add_feature_entry( hdb, "'three', '', '', '', 2, 1, '', 1" ); ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + /* msidbFeatureAttributesFavorLocal */ + r = add_feature_entry( hdb, "'four', '', '', '', 2, 1, '', 0" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + + /* disabled */ + r = add_feature_entry( hdb, "'five', '', '', '', 2, 0, '', 1" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + /* msidbFeatureAttributesFavorSource:msidbComponentAttributesSourceOnly */ r = add_component_entry( hdb, "'eta', '{DD89003F-0DD4-41B8-81C0-3411A7DA2695}', 'TARGETDIR', 1, '', 'eta_file'" ); ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + /* no feature parent:msidbComponentAttributesLocalOnly */ + r = add_component_entry( hdb, "'kappa', '{D6B93DC3-8DA5-4769-9888-42BFE156BB8B}', 'TARGETDIR', 1, '', 'kappa_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:removed */ + r = add_feature_entry( hdb, "'six', '', '', '', 2, 1, '', 0" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:removed:msidbComponentAttributesLocalOnly */ + r = add_component_entry( hdb, "'lambda', '{6528C5E4-02A4-4636-A214-7A66A6C35B64}', 'TARGETDIR', 0, '', 'lambda_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:removed:msidbComponentAttributesSourceOnly */ + r = add_component_entry( hdb, "'mu', '{97014BAB-6C56-4013-9A63-2BF913B42519}', 'TARGETDIR', 1, '', 'mu_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:removed:msidbComponentAttributesOptional */ + r = add_component_entry( hdb, "'nu', '{943DD0D8-5808-4954-8526-3B8493FEDDCD}', 'TARGETDIR', 2, '', 'nu_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:removed:msidbComponentAttributesSharedDllRefCount */ + r = add_component_entry( hdb, "'xi', '{D6CF9EF7-6FCF-4930-B34B-F938AEFF9BDB}', 'TARGETDIR', 8, '', 'xi_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorSource:removed */ + r = add_feature_entry( hdb, "'seven', '', '', '', 2, 1, '', 1" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + + /* msidbFeatureAttributesFavorSource:removed:msidbComponentAttributesLocalOnly */ + r = add_component_entry( hdb, "'omicron', '{7B57521D-15DB-4141-9AA6-01D934A4433F}', 'TARGETDIR', 0, '', 'omicron_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorSource:removed:msidbComponentAttributesSourceOnly */ + r = add_component_entry( hdb, "'pi', '{FB85346B-378E-4492-8769-792305471C81}', 'TARGETDIR', 1, '', 'pi_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorSource:removed:msidbComponentAttributesOptional */ + r = add_component_entry( hdb, "'rho', '{798F2047-7B0C-4783-8BB0-D703E554114B}', 'TARGETDIR', 2, '', 'rho_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorSource:removed:msidbComponentAttributesSharedDllRefCount */ + r = add_component_entry( hdb, "'sigma', '{5CE9DDA8-B67B-4736-9D93-99D61C5B93E7}', 'TARGETDIR', 8, '', 'sigma_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + r = create_feature_components_table( hdb ); ok( r == ERROR_SUCCESS, "cannot create FeatureComponents table: %d\n", r ); @@ -1621,6 +2400,36 @@ static void test_states(void) r = add_feature_components_entry( hdb, "'three', 'eta'" ); ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + r = add_feature_components_entry( hdb, "'four', 'eta'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'five', 'eta'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'six', 'lambda'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'six', 'mu'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'six', 'nu'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'six', 'xi'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'seven', 'omicron'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'seven', 'pi'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'seven', 'rho'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'seven', 'sigma'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + r = create_file_table( hdb ); ok( r == ERROR_SUCCESS, "cannot create File table: %d\n", r ); @@ -1652,9 +2461,50 @@ static void test_states(void) r = add_file_entry( hdb, "'eta_file', 'eta', 'eta.txt', 0, '', '1033', 16384, 1" ); ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + r = add_file_entry( hdb, "'kappa_file', 'kappa', 'kappa.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'lambda_file', 'lambda', 'lambda.txt', 100, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'mu_file', 'mu', 'mu.txt', 100, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'nu_file', 'nu', 'nu.txt', 100, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'xi_file', 'xi', 'xi.txt', 100, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'omicron_file', 'omicron', 'omicron.txt', 100, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'pi_file', 'pi', 'pi.txt', 100, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'rho_file', 'rho', 'rho.txt', 100, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'sigma_file', 'sigma', 'sigma.txt', 100, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + MsiDatabaseCommit(hdb); + + /* these properties must not be in the saved msi file */ + r = add_property_entry( hdb, "'ADDLOCAL', 'one,four'"); + ok( r == ERROR_SUCCESS, "cannot add property: %d\n", r ); + + r = add_property_entry( hdb, "'ADDSOURCE', 'two,three'"); + ok( r == ERROR_SUCCESS, "cannot add property: %d\n", r ); + + r = add_property_entry( hdb, "'REMOVE', 'six,seven'"); + ok( r == ERROR_SUCCESS, "cannot add property: %d\n", r ); + hpkg = package_from_db( hdb ); ok( hpkg, "failed to create package\n"); + MsiCloseHandle(hdb); + state = 0xdeadbee; action = 0xdeadbee; r = MsiGetFeatureState(hpkg, "one", &state, &action); @@ -1676,6 +2526,34 @@ static void test_states(void) ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "four", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "five", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "six", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "seven", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "alpha", &state, &action); @@ -1739,6 +2617,69 @@ static void test_states(void) ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "kappa", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lambda", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "nu", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "xi", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "rho", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "sigma", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + r = MsiDoAction( hpkg, "CostInitialize"); ok( r == ERROR_SUCCESS, "cost init failed\n"); @@ -1746,121 +2687,176 @@ static void test_states(void) action = 0xdeadbee; r = MsiGetFeatureState(hpkg, "one", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetFeatureState(hpkg, "two", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetFeatureState(hpkg, "three", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "four", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "five", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "six", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "seven", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "alpha", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "beta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "gamma", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "theta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "delta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "epsilon", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "zeta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "iota", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "eta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "kappa", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lambda", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "nu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "xi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "rho", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "sigma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); r = MsiDoAction( hpkg, "FileCost"); ok( r == ERROR_SUCCESS, "file cost failed\n"); @@ -1869,121 +2865,176 @@ static void test_states(void) action = 0xdeadbee; r = MsiGetFeatureState(hpkg, "one", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetFeatureState(hpkg, "two", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetFeatureState(hpkg, "three", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "four", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "five", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "six", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "seven", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "alpha", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "beta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "gamma", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "theta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "delta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "epsilon", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "zeta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "iota", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "eta", &state, &action); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); - todo_wine - { - ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); - ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); - } + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "kappa", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lambda", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "nu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "xi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "rho", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "sigma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); r = MsiDoAction( hpkg, "CostFinalize"); ok( r == ERROR_SUCCESS, "cost finalize failed: %d\n", r); @@ -2009,6 +3060,40 @@ static void test_states(void) ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "four", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "five", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "six", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "seven", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + state = 0xdeadbee; action = 0xdeadbee; r = MsiGetComponentState(hpkg, "alpha", &state, &action); @@ -2072,7 +3157,902 @@ static void test_states(void) ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "kappa", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lambda", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "nu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "xi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "rho", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "sigma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + MsiCloseHandle( hpkg ); + + /* publish the features and components */ + r = MsiInstallProduct(msifile, "ADDLOCAL=one,four ADDSOURCE=two,three REMOVE=six,seven"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_DIRECT, &hdb); + ok(r == ERROR_SUCCESS, "failed to open database: %d\n", r); + + /* these properties must not be in the saved msi file */ + r = add_property_entry( hdb, "'ADDLOCAL', 'one,four'"); + ok( r == ERROR_SUCCESS, "cannot add property: %d\n", r ); + + r = add_property_entry( hdb, "'ADDSOURCE', 'two,three'"); + ok( r == ERROR_SUCCESS, "cannot add property: %d\n", r ); + + r = add_property_entry( hdb, "'REMOVE', 'six,seven'"); + ok( r == ERROR_SUCCESS, "cannot add property: %d\n", r ); + + hpkg = package_from_db( hdb ); + ok( hpkg, "failed to create package\n"); + + MsiCloseHandle(hdb); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "one", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "two", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "three", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "four", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "five", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "six", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "seven", &state, &action); + ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "alpha", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "beta", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "gamma", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "theta", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "delta", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "epsilon", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "zeta", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "iota", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "eta", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "kappa", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lambda", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "nu", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "xi", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "rho", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "sigma", &state, &action); + ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r ); + ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state); + ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action); + + r = MsiDoAction( hpkg, "CostInitialize"); + ok( r == ERROR_SUCCESS, "cost init failed\n"); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "one", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "two", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "three", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "four", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "five", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "six", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "seven", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "alpha", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "beta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "gamma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "theta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "delta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "epsilon", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "zeta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "iota", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "eta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "kappa", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lambda", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "nu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "xi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "rho", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "sigma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + r = MsiDoAction( hpkg, "FileCost"); + ok( r == ERROR_SUCCESS, "file cost failed\n"); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "one", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "two", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "three", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "four", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "five", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "six", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "seven", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "alpha", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "beta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "gamma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "theta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "delta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "epsilon", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "zeta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "iota", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "eta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "kappa", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lambda", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "nu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "xi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "rho", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "sigma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + r = MsiDoAction( hpkg, "CostFinalize"); + ok( r == ERROR_SUCCESS, "cost finalize failed: %d\n", r); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "one", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + } + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "two", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + } + ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "three", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + } + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "four", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + } + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "five", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "six", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "seven", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "alpha", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + } + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "beta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + } + ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "gamma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + } + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "theta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + } + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "delta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + } + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "epsilon", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "zeta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "iota", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + } + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "eta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + todo_wine + { + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + } + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "kappa", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lambda", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "nu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "xi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "rho", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "sigma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + todo_wine + { + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + } + + MsiCloseHandle(hpkg); + + /* uninstall the product */ + r = MsiInstallProduct(msifile, "REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + DeleteFileA(msifile); } static void test_getproperty(void) @@ -2094,25 +4074,25 @@ static void test_getproperty(void) size = 0; r = MsiGetProperty(hPackage, "Name", NULL, &size); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); - ok( size == 5, "Expected 5, got %ld\n", size); + ok( size == 5, "Expected 5, got %d\n", size); /* retrieve the size, empty string */ size = 0; r = MsiGetProperty(hPackage, "Name", empty, &size); ok( r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); - ok( size == 5, "Expected 5, got %ld\n", size); + ok( size == 5, "Expected 5, got %d\n", size); /* don't change size */ r = MsiGetProperty(hPackage, "Name", prop, &size); ok( r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); - ok( size == 5, "Expected 5, got %ld\n", size); + ok( size == 5, "Expected 5, got %d\n", size); ok( !lstrcmp(prop, "Valu"), "Expected Valu, got %s\n", prop); /* increase the size by 1 */ size++; r = MsiGetProperty(hPackage, "Name", prop, &size); ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); - ok( size == 5, "Expected 5, got %ld\n", size); + ok( size == 5, "Expected 5, got %d\n", size); ok( !lstrcmp(prop, "Value"), "Expected Value, got %s\n", prop); r = MsiCloseHandle( hPackage); @@ -2125,9 +4105,6 @@ static void test_removefiles(void) MSIHANDLE hpkg; UINT r; MSIHANDLE hdb; - char CURR_DIR[MAX_PATH]; - - GetCurrentDirectoryA(MAX_PATH, CURR_DIR); hdb = create_package_db(); ok ( hdb, "failed to create package database\n" ); @@ -2210,6 +4187,8 @@ static void test_removefiles(void) hpkg = package_from_db( hdb ); ok( hpkg, "failed to create package\n"); + MsiCloseHandle( hdb ); + create_test_file( "hydrogen.txt" ); create_test_file( "helium.txt" ); create_test_file( "lithium.txt" ); @@ -2254,7 +4233,7 @@ static void test_removefiles(void) ok( r == ERROR_SUCCESS, "remove files failed\n"); ok(DeleteFileA("hydrogen.txt"), "Expected hydrogen.txt to exist\n"); - ok(DeleteFileA("lithium.txt"), "Expected lithium.txt to exist\n"); + ok(DeleteFileA("lithium.txt"), "Expected lithium.txt to exist\n"); ok(DeleteFileA("beryllium.txt"), "Expected beryllium.txt to exist\n"); ok(DeleteFileA("carbon.txt"), "Expected carbon.txt to exist\n"); ok(DeleteFileA("helium.txt"), "Expected helium.txt to exist\n"); @@ -2296,22 +4275,1143 @@ static void test_appsearch(void) hpkg = package_from_db( hdb ); ok( hpkg, "failed to create package\n"); + MsiCloseHandle( hdb ); + r = MsiDoAction( hpkg, "AppSearch" ); ok( r == ERROR_SUCCESS, "AppSearch failed: %d\n", r); r = MsiGetPropertyA( hpkg, "WEBBROWSERPROG", prop, &size ); ok( r == ERROR_SUCCESS, "get property failed: %d\n", r); - todo_wine - { - ok( lstrlenA(prop) != 0, "Expected non-zero length\n"); - } + ok( lstrlenA(prop) != 0, "Expected non-zero length\n"); MsiCloseHandle( hpkg ); DeleteFileA(msifile); } +static void test_featureparents(void) +{ + MSIHANDLE hpkg; + UINT r; + MSIHANDLE hdb; + INSTALLSTATE state, action; + + hdb = create_package_db(); + ok ( hdb, "failed to create package database\n" ); + + r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'"); + ok( r == ERROR_SUCCESS, "cannot add directory: %d\n", r ); + + r = create_feature_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create Feature table: %d\n", r ); + + r = create_component_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create Component table: %d\n", r ); + + r = create_feature_components_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create FeatureComponents table: %d\n", r ); + + r = create_file_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create File table: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal */ + r = add_feature_entry( hdb, "'zodiac', '', '', '', 2, 1, '', 0" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + + /* msidbFeatureAttributesFavorSource */ + r = add_feature_entry( hdb, "'perseus', '', '', '', 2, 1, '', 1" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal */ + r = add_feature_entry( hdb, "'orion', '', '', '', 2, 1, '', 0" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + + /* disabled because of install level */ + r = add_feature_entry( hdb, "'waters', '', '', '', 15, 101, '', 9" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + + /* child feature of disabled feature */ + r = add_feature_entry( hdb, "'bayer', 'waters', '', '', 14, 1, '', 9" ); + ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r ); + + /* component of disabled feature (install level) */ + r = add_component_entry( hdb, "'delphinus', '', 'TARGETDIR', 0, '', 'delphinus_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* component of disabled child feature (install level) */ + r = add_component_entry( hdb, "'hydrus', '', 'TARGETDIR', 0, '', 'hydrus_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesLocalOnly */ + r = add_component_entry( hdb, "'leo', '', 'TARGETDIR', 0, '', 'leo_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesSourceOnly */ + r = add_component_entry( hdb, "'virgo', '', 'TARGETDIR', 1, '', 'virgo_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesOptional */ + r = add_component_entry( hdb, "'libra', '', 'TARGETDIR', 2, '', 'libra_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorSource:msidbComponentAttributesLocalOnly */ + r = add_component_entry( hdb, "'cassiopeia', '', 'TARGETDIR', 0, '', 'cassiopeia_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorSource:msidbComponentAttributesSourceOnly */ + r = add_component_entry( hdb, "'cepheus', '', 'TARGETDIR', 1, '', 'cepheus_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorSource:msidbComponentAttributesOptional */ + r = add_component_entry( hdb, "'andromeda', '', 'TARGETDIR', 2, '', 'andromeda_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesLocalOnly */ + r = add_component_entry( hdb, "'canis', '', 'TARGETDIR', 0, '', 'canis_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesSourceOnly */ + r = add_component_entry( hdb, "'monoceros', '', 'TARGETDIR', 1, '', 'monoceros_file'" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesOptional */ + r = add_component_entry( hdb, "'lepus', '', 'TARGETDIR', 2, '', 'lepus_file'" ); + + r = add_feature_components_entry( hdb, "'zodiac', 'leo'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'zodiac', 'virgo'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'zodiac', 'libra'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'perseus', 'cassiopeia'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'perseus', 'cepheus'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'perseus', 'andromeda'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'orion', 'leo'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'orion', 'virgo'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'orion', 'libra'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'orion', 'cassiopeia'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'orion', 'cepheus'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'orion', 'andromeda'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'orion', 'canis'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'orion', 'monoceros'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'orion', 'lepus'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'waters', 'delphinus'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'bayer', 'hydrus'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_file_entry( hdb, "'leo_file', 'leo', 'leo.txt', 100, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'virgo_file', 'virgo', 'virgo.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'libra_file', 'libra', 'libra.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'cassiopeia_file', 'cassiopeia', 'cassiopeia.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'cepheus_file', 'cepheus', 'cepheus.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'andromeda_file', 'andromeda', 'andromeda.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'canis_file', 'canis', 'canis.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'monoceros_file', 'monoceros', 'monoceros.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'lepus_file', 'lepus', 'lepus.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'delphinus_file', 'delphinus', 'delphinus.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + r = add_file_entry( hdb, "'hydrus_file', 'hydrus', 'hydrus.txt', 0, '', '1033', 8192, 1" ); + ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r); + + hpkg = package_from_db( hdb ); + ok( hpkg, "failed to create package\n"); + + MsiCloseHandle( hdb ); + + r = MsiDoAction( hpkg, "CostInitialize"); + ok( r == ERROR_SUCCESS, "cost init failed\n"); + + r = MsiDoAction( hpkg, "FileCost"); + ok( r == ERROR_SUCCESS, "file cost failed\n"); + + r = MsiDoAction( hpkg, "CostFinalize"); + ok( r == ERROR_SUCCESS, "cost finalize failed\n"); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "zodiac", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "perseus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "orion", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "waters", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "bayer", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "leo", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "virgo", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected virgo INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_SOURCE, "Expected virgo INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "libra", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected libra INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected libra INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "cassiopeia", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected cassiopeia INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected cassiopeia INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "cepheus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected cepheus INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_SOURCE, "Expected cepheus INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "andromeda", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected andromeda INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected andromeda INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "canis", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected canis INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected canis INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "monoceros", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected monoceros INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_SOURCE, "Expected monoceros INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lepus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected lepus INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected lepus INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "delphinus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected delphinus INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected delphinus INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "hydrus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected hydrus INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected hydrus INSTALLSTATE_UNKNOWN, got %d\n", action); + + r = MsiSetFeatureState(hpkg, "orion", INSTALLSTATE_ABSENT); + ok( r == ERROR_SUCCESS, "failed to set feature state: %d\n", r); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "zodiac", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected zodiac INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected zodiac INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "perseus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected perseus INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_SOURCE, "Expected perseus INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "orion", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_ABSENT, "Expected orion INSTALLSTATE_ABSENT, got %d\n", state); + ok( action == INSTALLSTATE_ABSENT, "Expected orion INSTALLSTATE_ABSENT, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "leo", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected leo INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected leo INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "virgo", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected virgo INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_SOURCE, "Expected virgo INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "libra", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected libra INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected libra INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "cassiopeia", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected cassiopeia INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_LOCAL, "Expected cassiopeia INSTALLSTATE_LOCAL, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "cepheus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected cepheus INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_SOURCE, "Expected cepheus INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "andromeda", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected andromeda INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_SOURCE, "Expected andromeda INSTALLSTATE_SOURCE, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "canis", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected canis INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected canis INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "monoceros", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected monoceros INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected monoceros INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "lepus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected lepus INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected lepus INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "delphinus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected delphinus INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected delphinus INSTALLSTATE_UNKNOWN, got %d\n", action); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "hydrus", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_UNKNOWN, "Expected hydrus INSTALLSTATE_UNKNOWN, got %d\n", state); + ok( action == INSTALLSTATE_UNKNOWN, "Expected hydrus INSTALLSTATE_UNKNOWN, got %d\n", action); + + MsiCloseHandle(hpkg); + DeleteFileA(msifile); +} + +static void test_installprops(void) +{ + MSIHANDLE hpkg, hdb; + CHAR path[MAX_PATH]; + CHAR buf[MAX_PATH]; + DWORD size, type; + HKEY hkey; + UINT r; + + GetCurrentDirectory(MAX_PATH, path); + lstrcat(path, "\\"); + lstrcat(path, msifile); + + hdb = create_package_db(); + ok( hdb, "failed to create database\n"); + + hpkg = package_from_db(hdb); + ok( hpkg, "failed to create package\n"); + + MsiCloseHandle(hdb); + + size = MAX_PATH; + r = MsiGetProperty(hpkg, "DATABASE", buf, &size); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrcmp(buf, path), "Expected %s, got %s\n", path, buf); + + RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hkey); + + size = MAX_PATH; + type = REG_SZ; + RegQueryValueEx(hkey, "RegisteredOwner", NULL, &type, (LPBYTE)path, &size); + + size = MAX_PATH; + r = MsiGetProperty(hpkg, "USERNAME", buf, &size); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrcmp(buf, path), "Expected %s, got %s\n", path, buf); + + size = MAX_PATH; + type = REG_SZ; + RegQueryValueEx(hkey, "RegisteredOrganization", NULL, &type, (LPBYTE)path, &size); + + size = MAX_PATH; + r = MsiGetProperty(hpkg, "COMPANYNAME", buf, &size); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrcmp(buf, path), "Expected %s, got %s\n", path, buf); + + size = MAX_PATH; + r = MsiGetProperty(hpkg, "VersionDatabase", buf, &size); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + trace("VersionDatabase = %s\n", buf); + + size = MAX_PATH; + r = MsiGetProperty(hpkg, "VersionMsi", buf, &size); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + trace("VersionMsi = %s\n", buf); + + size = MAX_PATH; + r = MsiGetProperty(hpkg, "Date", buf, &size); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + trace("Date = %s\n", buf); + + size = MAX_PATH; + r = MsiGetProperty(hpkg, "Time", buf, &size); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + trace("Time = %s\n", buf); + + size = MAX_PATH; + r = MsiGetProperty(hpkg, "PackageCode", buf, &size); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + trace("PackageCode = %s\n", buf); + + CloseHandle(hkey); + MsiCloseHandle(hpkg); + DeleteFile(msifile); +} + +static void test_sourcedirprop(void) +{ + MSIHANDLE hpkg, hdb; + CHAR source_dir[MAX_PATH]; + CHAR path[MAX_PATH]; + DWORD size; + UINT r; + + hdb = create_package_db(); + ok ( hdb, "failed to create package database\n" ); + + r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'"); + ok( r == ERROR_SUCCESS, "cannot add directory: %d\n", r ); + + hpkg = package_from_db( hdb ); + ok( hpkg, "failed to create package\n"); + + MsiCloseHandle( hdb ); + + size = MAX_PATH; + r = MsiGetProperty( hpkg, "SourceDir", source_dir, &size ); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrlenA(source_dir), "Expected emtpy source dir, got %s\n", source_dir); + + size = MAX_PATH; + r = MsiGetProperty( hpkg, "SOURCEDIR", source_dir, &size ); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrlenA(source_dir), "Expected emtpy source dir, got %s\n", source_dir); + + r = MsiDoAction( hpkg, "CostInitialize"); + ok( r == ERROR_SUCCESS, "cost init failed\n"); + + size = MAX_PATH; + r = MsiGetProperty( hpkg, "SourceDir", source_dir, &size ); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrlenA(source_dir), "Expected emtpy source dir, got %s\n", source_dir); + + size = MAX_PATH; + r = MsiGetProperty( hpkg, "SOURCEDIR", source_dir, &size ); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrlenA(source_dir), "Expected emtpy source dir, got %s\n", source_dir); + + r = MsiDoAction( hpkg, "ResolveSource"); + ok( r == ERROR_SUCCESS, "file cost failed\n"); + + GetCurrentDirectory(MAX_PATH, path); + lstrcatA(path, "\\"); + + size = MAX_PATH; + r = MsiGetProperty( hpkg, "SourceDir", source_dir, &size ); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrcmpA(source_dir, path), "Expected %s, got %s\n", path, source_dir); + + size = MAX_PATH; + r = MsiGetProperty( hpkg, "SOURCEDIR", source_dir, &size ); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrcmpA(source_dir, path), "Expected %s, got %s\n", path, source_dir); + + size = MAX_PATH; + r = MsiGetProperty( hpkg, "SoUrCeDiR", source_dir, &size ); + ok( r == ERROR_SUCCESS, "failed to get property: %d\n", r); + ok( !lstrlenA(source_dir), "Expected emtpy source dir, got %s\n", source_dir); + + MsiCloseHandle(hpkg); + DeleteFileA(msifile); +} + +static void test_prop_path(void) +{ + MSIHANDLE hpkg, hdb; + char buffer[MAX_PATH], cwd[MAX_PATH]; + DWORD sz; + UINT r; + + GetCurrentDirectory(MAX_PATH, cwd); + strcat(cwd, "\\"); + + hdb = create_package_db(); + ok( hdb, "failed to create database\n"); + + r = add_directory_entry( hdb, "'TARGETDIR','','SourceDir'" ); + ok( r == ERROR_SUCCESS, "cannot add directory: %d\n", r ); + + r = add_directory_entry( hdb, "'foo','TARGETDIR','foosrc:footgt'" ); + ok( r == ERROR_SUCCESS, "cannot add directory: %d\n", r ); + + hpkg = package_from_db(hdb); + ok( hpkg, "failed to create package\n"); + + r = MsiGetSourcePath(hpkg, "SourceDir", buffer, &sz ); + ok( r == ERROR_DIRECTORY, "failed to get source path\n"); + + r = MsiGetSourcePath(hpkg, "SOURCEDIR", buffer, &sz ); + ok( r == ERROR_DIRECTORY, "failed to get source path\n"); + + r = MsiDoAction( hpkg, "CostInitialize"); + ok( r == ERROR_SUCCESS, "cost init failed\n"); + + sz = sizeof buffer; + buffer[0] = 0; + r = MsiGetProperty(hpkg, "SourceDir", buffer, &sz); + ok( r == ERROR_SUCCESS, "property not set\n"); + ok( !buffer[0], "SourceDir should be empty\n"); + + sz = sizeof buffer; + buffer[0] = 0; + r = MsiGetProperty(hpkg, "SOURCEDIR", buffer, &sz); + ok( r == ERROR_SUCCESS, "property not set\n"); + ok( !buffer[0], "SourceDir should be empty\n"); + + sz = sizeof buffer; + buffer[0] = 0; + r = MsiGetSourcePath(hpkg, "SourceDir", buffer, &sz ); + ok( r == ERROR_SUCCESS, "failed to get source path\n"); + ok( !lstrcmpi(cwd, buffer), "SourceDir (%s) should be current dir (%s)\n", buffer, cwd); + + sz = sizeof buffer; + buffer[0] = 0; + r = MsiGetProperty(hpkg, "SourceDir", buffer, &sz); + ok( r == ERROR_SUCCESS, "property not set\n"); + todo_wine { + ok( !lstrcmpi(cwd, buffer), "SourceDir (%s) should be current dir (%s)\n", buffer, cwd); + } + + sz = sizeof buffer; + buffer[0] = 0; + r = MsiGetSourcePath(hpkg, "SOURCEDIR", buffer, &sz ); + ok( r == ERROR_DIRECTORY, "failed to get source path\n"); + + sz = sizeof buffer; + buffer[0] = 0; + r = MsiGetProperty(hpkg, "SOURCEDIR", buffer, &sz); + ok( r == ERROR_SUCCESS, "property not set\n"); + todo_wine { + ok( !lstrcmpi(cwd, buffer), "SourceDir (%s) should be current dir (%s)\n", buffer, cwd); + } + + r = MsiSetProperty(hpkg, "SourceDir", "goo"); + ok( r == ERROR_SUCCESS, "property not set\n"); + + sz = sizeof buffer; + buffer[0] = 0; + r = MsiGetProperty(hpkg, "SourceDir", buffer, &sz); + ok( r == ERROR_SUCCESS, "property not set\n"); + ok( !lstrcmpi(buffer, "goo"), "SourceDir (%s) should be goo\n", buffer); + + sz = sizeof buffer; + buffer[0] = 0; + r = MsiGetSourcePath(hpkg, "SourceDir", buffer, &sz ); + ok( r == ERROR_SUCCESS, "failed to get source path\n"); + ok( !lstrcmpi(buffer, cwd), "SourceDir (%s) should be goo\n", buffer); + + sz = sizeof buffer; + buffer[0] = 0; + r = MsiGetProperty(hpkg, "SourceDir", buffer, &sz); + ok( r == ERROR_SUCCESS, "property not set\n"); + ok( !lstrcmpi(buffer, "goo"), "SourceDir (%s) should be goo\n", buffer); + + MsiCloseHandle( hpkg ); + DeleteFile(msifile); +} + +static void test_launchconditions(void) +{ + MSIHANDLE hpkg; + MSIHANDLE hdb; + UINT r; + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + hdb = create_package_db(); + ok( hdb, "failed to create package database\n" ); + + r = create_launchcondition_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create LaunchCondition table: %d\n", r ); + + r = add_launchcondition_entry( hdb, "'X = \"1\"', 'one'" ); + ok( r == ERROR_SUCCESS, "cannot add launch condition: %d\n", r ); + + /* invalid condition */ + r = add_launchcondition_entry( hdb, "'X != \"1\"', 'one'" ); + ok( r == ERROR_SUCCESS, "cannot add launch condition: %d\n", r ); + + hpkg = package_from_db( hdb ); + ok( hpkg, "failed to create package\n"); + + MsiCloseHandle( hdb ); + + r = MsiSetProperty( hpkg, "X", "1" ); + ok( r == ERROR_SUCCESS, "failed to set property\n" ); + + /* invalid conditions are ignored */ + r = MsiDoAction( hpkg, "LaunchConditions" ); + ok( r == ERROR_SUCCESS, "cost init failed\n" ); + + /* verify LaunchConditions still does some verification */ + r = MsiSetProperty( hpkg, "X", "2" ); + ok( r == ERROR_SUCCESS, "failed to set property\n" ); + + r = MsiDoAction( hpkg, "LaunchConditions" ); + ok( r == ERROR_INSTALL_FAILURE, "Expected ERROR_INSTALL_FAILURE, got %d\n", r ); + + MsiCloseHandle( hpkg ); + DeleteFile( msifile ); +} + +static void test_ccpsearch(void) +{ + MSIHANDLE hdb, hpkg; + CHAR prop[MAX_PATH]; + DWORD size = MAX_PATH; + UINT r; + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + hdb = create_package_db(); + ok(hdb, "failed to create package database\n"); + + r = create_ccpsearch_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_ccpsearch_entry(hdb, "'CCP_random'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_ccpsearch_entry(hdb, "'RMCCP_random'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = create_reglocator_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_reglocator_entry(hdb, "'CCP_random', 0, 'htmlfile\\shell\\open\\nonexistent', '', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = create_drlocator_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_drlocator_entry(hdb, "'RMCCP_random', '', 'C:\\', '0'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = create_signature_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + hpkg = package_from_db(hdb); + ok(hpkg, "failed to create package\n"); + + MsiCloseHandle(hdb); + + r = MsiDoAction(hpkg, "CCPSearch"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiGetPropertyA(hpkg, "CCP_Success", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "1"), "Expected 1, got %s\n", prop); + + MsiCloseHandle(hpkg); + DeleteFileA(msifile); +} + +static BOOL squash_guid(LPCWSTR in, LPWSTR out) +{ + DWORD i,n=1; + GUID guid; + + if (FAILED(CLSIDFromString((LPOLESTR)in, &guid))) + return FALSE; + + for(i=0; i<8; i++) + out[7-i] = in[n++]; + n++; + for(i=0; i<4; i++) + out[11-i] = in[n++]; + n++; + for(i=0; i<4; i++) + out[15-i] = in[n++]; + n++; + for(i=0; i<2; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + n++; + for( ; i<8; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + out[32]=0; + return TRUE; +} + +static void set_component_path(LPCSTR filename, LPCSTR guid) +{ + WCHAR guidW[MAX_PATH]; + WCHAR squashedW[MAX_PATH]; + CHAR squashed[MAX_PATH]; + CHAR substr[MAX_PATH]; + CHAR path[MAX_PATH]; + HKEY hkey; + + MultiByteToWideChar(CP_ACP, 0, guid, -1, guidW, MAX_PATH); + squash_guid(guidW, squashedW); + WideCharToMultiByte(CP_ACP, 0, squashedW, -1, squashed, MAX_PATH, NULL, NULL); + + lstrcpyA(substr, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\UserData\\S-1-5-18\\Components\\"); + lstrcatA(substr, squashed); + + RegCreateKeyA(HKEY_LOCAL_MACHINE, substr, &hkey); + + lstrcpyA(path, CURR_DIR); + lstrcatA(path, "\\"); + lstrcatA(path, filename); + + /* just using a random squashed product code */ + RegSetValueExA(hkey, "7D2F387510109040002000060BECB6AB", 0, + REG_SZ, (LPBYTE)path, lstrlenA(path)); + + RegCloseKey(hkey); + + lstrcpyA(substr, "SOFTWARE\\Classes\\Installer\\Products\\7D2F387510109040002000060BECB6AB"); + RegCreateKeyA(HKEY_LOCAL_MACHINE, substr, &hkey); +} + +static void delete_component_path(LPCSTR guid) +{ + WCHAR guidW[MAX_PATH]; + WCHAR squashedW[MAX_PATH]; + WCHAR substrW[MAX_PATH]; + CHAR squashed[MAX_PATH]; + CHAR substr[MAX_PATH]; + + MultiByteToWideChar(CP_ACP, 0, guid, -1, guidW, MAX_PATH); + squash_guid(guidW, squashedW); + WideCharToMultiByte(CP_ACP, 0, squashedW, -1, squashed, MAX_PATH, NULL, NULL); + + lstrcpyA(substr, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\UserData\\S-1-5-18\\Components\\"); + lstrcatA(substr, squashed); + + MultiByteToWideChar(CP_ACP, 0, substr, -1, substrW, MAX_PATH); + package_RegDeleteTreeW(HKEY_LOCAL_MACHINE, substrW); + + lstrcpyA(substr, "SOFTWARE\\Classes\\Installer\\Products\\7D2F387510109040002000060BECB6AB"); + MultiByteToWideChar(CP_ACP, 0, substr, -1, substrW, MAX_PATH); + package_RegDeleteTreeW(HKEY_LOCAL_MACHINE, substrW); +} + +static void test_complocator(void) +{ + MSIHANDLE hdb, hpkg; + UINT r; + CHAR prop[MAX_PATH]; + CHAR expected[MAX_PATH]; + DWORD size = MAX_PATH; + + hdb = create_package_db(); + ok(hdb, "failed to create package database\n"); + + r = create_appsearch_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'ABELISAURUS', 'abelisaurus'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'BACTROSAURUS', 'bactrosaurus'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'CAMELOTIA', 'camelotia'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'DICLONIUS', 'diclonius'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'ECHINODON', 'echinodon'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'FALCARIUS', 'falcarius'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'GALLIMIMUS', 'gallimimus'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'HAGRYPHUS', 'hagryphus'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'IGUANODON', 'iguanodon'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'JOBARIA', 'jobaria'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'KAKURU', 'kakuru'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'LABOCANIA', 'labocania'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'MEGARAPTOR', 'megaraptor'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'NEOSODON', 'neosodon'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'OLOROTITAN', 'olorotitan'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'PANTYDRACO', 'pantydraco'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = create_complocator_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'abelisaurus', '{E3619EED-305A-418C-B9C7-F7D7377F0934}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'bactrosaurus', '{D56B688D-542F-42Ef-90FD-B6DA76EE8119}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'camelotia', '{8211BE36-2466-47E3-AFB7-6AC72E51AED2}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'diclonius', '{5C767B20-A33C-45A4-B80B-555E512F01AE}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'echinodon', '{A19E16C5-C75D-4699-8111-C4338C40C3CB}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'falcarius', '{17762FA1-A7AE-4CC6-8827-62873C35361D}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'gallimimus', '{75EBF568-C959-41E0-A99E-9050638CF5FB}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'hagrphus', '{D4969B72-17D9-4AB6-BE49-78F2FEE857AC}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'iguanodon', '{8E0DA02E-F6A7-4A8F-B25D-6F564C492308}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'jobaria', '{243C22B1-8C51-4151-B9D1-1AE5265E079E}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'kakuru', '{5D0F03BA-50BC-44F2-ABB1-72C972F4E514}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'labocania', '{C7DDB60C-7828-4046-A6F8-699D5E92F1ED}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'megaraptor', '{8B1034B7-BD5E-41ac-B52C-0105D3DFD74D}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'neosodon', '{0B499649-197A-48EF-93D2-AF1C17ED6E90}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'olorotitan', '{54E9E91F-AED2-46D5-A25A-7E50AFA24513}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_complocator_entry(hdb, "'pantydraco', '{2A989951-5565-4FA7-93A7-E800A3E67D71}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = create_signature_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'abelisaurus', 'abelisaurus', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'bactrosaurus', 'bactrosaurus', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'camelotia', 'camelotia', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'diclonius', 'diclonius', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'iguanodon', 'iguanodon', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'jobaria', 'jobaria', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'kakuru', 'kakuru', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'labocania', 'labocania', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + hpkg = package_from_db(hdb); + ok(hpkg, "failed to create package\n"); + + MsiCloseHandle(hdb); + + create_test_file("abelisaurus"); + create_test_file("bactrosaurus"); + create_test_file("camelotia"); + create_test_file("diclonius"); + create_test_file("echinodon"); + create_test_file("falcarius"); + create_test_file("gallimimus"); + create_test_file("hagryphus"); + CreateDirectoryA("iguanodon", NULL); + CreateDirectoryA("jobaria", NULL); + CreateDirectoryA("kakuru", NULL); + CreateDirectoryA("labocania", NULL); + CreateDirectoryA("megaraptor", NULL); + CreateDirectoryA("neosodon", NULL); + CreateDirectoryA("olorotitan", NULL); + CreateDirectoryA("pantydraco", NULL); + + set_component_path("abelisaurus", "{E3619EED-305A-418C-B9C7-F7D7377F0934}"); + set_component_path("bactrosaurus", "{D56B688D-542F-42Ef-90FD-B6DA76EE8119}"); + set_component_path("echinodon", "{A19E16C5-C75D-4699-8111-C4338C40C3CB}"); + set_component_path("falcarius", "{17762FA1-A7AE-4CC6-8827-62873C35361D}"); + set_component_path("iguanodon", "{8E0DA02E-F6A7-4A8F-B25D-6F564C492308}"); + set_component_path("jobaria", "{243C22B1-8C51-4151-B9D1-1AE5265E079E}"); + set_component_path("megaraptor", "{8B1034B7-BD5E-41ac-B52C-0105D3DFD74D}"); + set_component_path("neosodon", "{0B499649-197A-48EF-93D2-AF1C17ED6E90}"); + + r = MsiDoAction(hpkg, "AppSearch"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "ABELISAURUS", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + lstrcpyA(expected, CURR_DIR); + lstrcatA(expected, "\\abelisaurus"); + ok(!lstrcmpA(prop, expected), "Expected %s, got %s\n", expected, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "BACTROSAURUS", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "CAMELOTIA", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "DICLONIUS", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "ECHINODON", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + lstrcpyA(expected, CURR_DIR); + lstrcatA(expected, "\\"); + ok(!lstrcmpA(prop, expected), "Expected %s, got %s\n", expected, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "FALCARIUS", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "GALLIMIMUS", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "HAGRYPHUS", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "IGUANODON", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "JOBARIA", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "KAKURU", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "LABOCANIA", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "MEGARAPTOR", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + lstrcpyA(expected, CURR_DIR); + lstrcatA(expected, "\\"); + ok(!lstrcmpA(prop, expected), "Expected %s, got %s\n", expected, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "NEOSODON", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + lstrcpyA(expected, CURR_DIR); + lstrcatA(expected, "\\neosodon\\"); + ok(!lstrcmpA(prop, expected), "Expected %s, got %s\n", expected, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "OLOROTITAN", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "PANTYDRACO", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected , got %s\n", prop); + + MsiCloseHandle(hpkg); + DeleteFileA("abelisaurus"); + DeleteFileA("bactrosaurus"); + DeleteFileA("camelotia"); + DeleteFileA("diclonius"); + DeleteFileA("echinodon"); + DeleteFileA("falcarius"); + DeleteFileA("gallimimus"); + DeleteFileA("hagryphus"); + RemoveDirectoryA("iguanodon"); + RemoveDirectoryA("jobaria"); + RemoveDirectoryA("kakuru"); + RemoveDirectoryA("labocania"); + RemoveDirectoryA("megaraptor"); + RemoveDirectoryA("neosodon"); + RemoveDirectoryA("olorotitan"); + RemoveDirectoryA("pantydraco"); + delete_component_path("{E3619EED-305A-418C-B9C7-F7D7377F0934}"); + delete_component_path("{D56B688D-542F-42Ef-90FD-B6DA76EE8119}"); + delete_component_path("{A19E16C5-C75D-4699-8111-C4338C40C3CB}"); + delete_component_path("{17762FA1-A7AE-4CC6-8827-62873C35361D}"); + delete_component_path("{8E0DA02E-F6A7-4A8F-B25D-6F564C492308}"); + delete_component_path("{243C22B1-8C51-4151-B9D1-1AE5265E079E}"); + delete_component_path("{8B1034B7-BD5E-41ac-B52C-0105D3DFD74D}"); + delete_component_path("{0B499649-197A-48EF-93D2-AF1C17ED6E90}"); + DeleteFileA(msifile); +} + START_TEST(package) { + GetCurrentDirectoryA(MAX_PATH, CURR_DIR); + test_createpackage(); test_getsourcepath_bad(); test_getsourcepath(); @@ -2319,6 +5419,7 @@ START_TEST(package) test_gettargetpath_bad(); test_settargetpath(); test_props(); + test_property_table(); test_condition(); test_msipackage(); test_formatrecord2(); @@ -2326,4 +5427,11 @@ START_TEST(package) test_getproperty(); test_removefiles(); test_appsearch(); + test_featureparents(); + test_installprops(); + test_sourcedirprop(); + test_prop_path(); + test_launchconditions(); + test_ccpsearch(); + test_complocator(); } diff --git a/rostests/winetests/msi/record.c b/rostests/winetests/msi/record.c index e95d66bbbd4..1f72d128617 100644 --- a/rostests/winetests/msi/record.c +++ b/rostests/winetests/msi/record.c @@ -30,11 +30,11 @@ static BOOL create_temp_file(char *name) unsigned char buffer[26], i; DWORD sz; HANDLE handle; - + r = GetTempFileName(".", "msitest",0,name); if(!r) return r; - handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE, + handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(handle==INVALID_HANDLE_VALUE) return 0; @@ -234,10 +234,26 @@ static void test_msirecord(void) ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); i = MsiRecordGetInteger(h, 0); ok(i == 1, "should get one\n"); + i = MsiRecordSetString(h,0,"foo"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get zero\n"); + i = MsiRecordSetString(h,0,""); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get zero\n"); + i = MsiRecordSetString(h,0,"+1"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get zero\n"); /* same record, try converting integers to strings */ r = MsiRecordSetInteger(h, 0, 32); ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n"); + sz = 1; + r = MsiRecordGetString(h, 0, NULL, &sz); + ok(r == ERROR_SUCCESS, "failed to get string from integer\n"); + ok(sz == 2, "length wrong\n"); buf[0]=0; sz = sizeof buf; r = MsiRecordGetString(h, 0, buf, &sz); @@ -246,10 +262,15 @@ static void test_msirecord(void) r = MsiRecordSetInteger(h, 0, -32); ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n"); buf[0]=0; + sz = 1; + r = MsiRecordGetString(h, 0, NULL, &sz); + ok(r == ERROR_SUCCESS, "failed to get string from integer\n"); + ok(sz == 3, "length wrong\n"); sz = sizeof buf; r = MsiRecordGetString(h, 0, buf, &sz); ok(r == ERROR_SUCCESS, "failed to get string from integer\n"); ok(0==strcmp(buf,"-32"), "failed to get string from integer\n"); + buf[0]=0; /* same record, now try streams */ r = MsiRecordSetStream(h, 0, NULL); @@ -268,7 +289,7 @@ static void test_msirecord(void) ok(r == ERROR_SUCCESS, "Failed to close handle\n"); /* now try streams in a new record - need to create a file to play with */ - r = create_temp_file(filename); + r = create_temp_file(filename); if(!r) return; diff --git a/rostests/winetests/msi/source.c b/rostests/winetests/msi/source.c new file mode 100644 index 00000000000..7407f5b8748 --- /dev/null +++ b/rostests/winetests/msi/source.c @@ -0,0 +1,725 @@ +/* + * Tests for MSI Source functions + * + * Copyright (C) 2006 James Hawkins + * + * 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 _WIN32_MSI 300 + +#include + +#include +#include +#include +#include +#include + +#include "wine/test.h" + +static BOOL (WINAPI *pConvertSidToStringSidA)(PSID, LPSTR*); +static UINT (WINAPI *pMsiSourceListGetInfoA) + (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, LPCSTR, LPSTR, LPDWORD); +static UINT (WINAPI *pMsiSourceListAddSourceExA) + (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, LPCSTR, DWORD); + +static void init_functionpointers(void) +{ + HMODULE hmsi = GetModuleHandleA("msi.dll"); + HMODULE hadvapi32 = GetModuleHandleA("advapi32.dll"); + +#define GET_PROC(dll, func) \ + p ## func = (void *)GetProcAddress(dll, #func); \ + if(!p ## func) \ + trace("GetProcAddress(%s) failed\n", #func); + + GET_PROC(hmsi, MsiSourceListAddSourceExA) + GET_PROC(hmsi, MsiSourceListGetInfoA) + + GET_PROC(hadvapi32, ConvertSidToStringSidA) + +#undef GET_PROC +} + +/* copied from dlls/msi/registry.c */ +static BOOL squash_guid(LPCWSTR in, LPWSTR out) +{ + DWORD i,n=1; + GUID guid; + + if (FAILED(CLSIDFromString((LPOLESTR)in, &guid))) + return FALSE; + + for(i=0; i<8; i++) + out[7-i] = in[n++]; + n++; + for(i=0; i<4; i++) + out[11-i] = in[n++]; + n++; + for(i=0; i<4; i++) + out[15-i] = in[n++]; + n++; + for(i=0; i<2; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + n++; + for( ; i<8; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + out[32]=0; + return TRUE; +} + +static void create_test_guid(LPSTR prodcode, LPSTR squashed) +{ + WCHAR guidW[MAX_PATH]; + WCHAR squashedW[MAX_PATH]; + GUID guid; + HRESULT hr; + int size; + + hr = CoCreateGuid(&guid); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + + size = StringFromGUID2(&guid, (LPOLESTR)guidW, MAX_PATH); + ok(size == 39, "Expected 39, got %d\n", hr); + + WideCharToMultiByte(CP_ACP, 0, guidW, size, prodcode, MAX_PATH, NULL, NULL); + squash_guid(guidW, squashedW); + WideCharToMultiByte(CP_ACP, 0, squashedW, -1, squashed, MAX_PATH, NULL, NULL); +} + +static void get_user_sid(LPSTR *usersid) +{ + HANDLE token; + BYTE buf[1024]; + DWORD size; + PTOKEN_USER user; + + OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token); + size = sizeof(buf); + GetTokenInformation(token, TokenUser, (void *)buf, size, &size); + user = (PTOKEN_USER)buf; + pConvertSidToStringSidA(user->User.Sid, usersid); +} + +static void test_MsiSourceListGetInfo(void) +{ + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + CHAR keypath[MAX_PATH*2]; + CHAR value[MAX_PATH]; + LPSTR usersid; + LPCSTR data; + LONG res; + UINT r; + HKEY userkey, hkey; + DWORD size; + + if (!pMsiSourceListGetInfoA) + { + skip("Skipping MsiSourceListGetInfoA tests\n"); + return; + } + + create_test_guid(prodcode, prod_squashed); + get_user_sid(&usersid); + + /* NULL szProductCodeOrPatchCode */ + r = pMsiSourceListGetInfoA(NULL, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* empty szProductCodeOrPatchCode */ + r = pMsiSourceListGetInfoA("", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* garbage szProductCodeOrPatchCode */ + r = pMsiSourceListGetInfoA("garbage", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* szProductCodeOrPatchCode */ + r = pMsiSourceListGetInfoA("garbage", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* guid without brackets */ + r = pMsiSourceListGetInfoA("51CD2AD5-0482-4C46-8DDD-0ED1022AA1AA", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* guid with brackets */ + r = pMsiSourceListGetInfoA("{51CD2AD5-0482-4C46-8DDD-0ED1022AA1AA}", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + /* same length as guid, but random */ + r = pMsiSourceListGetInfoA("ADKD-2KSDFF2-DKK1KNFJASD9GLKWME-1I3KAD", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* invalid context */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_NONE, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* another invalid context */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_ALLUSERMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* yet another invalid context */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_ALL, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* mix two valid contexts */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED | MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* invalid option */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + 4, INSTALLPROPERTY_PACKAGENAME, NULL, NULL); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + /* NULL property */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, NULL, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* empty property */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, "", NULL, NULL); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + /* value is non-NULL while size is NULL */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, value, NULL); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* size is non-NULL while value is NULL */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user product key exists */ + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_BAD_CONFIGURATION, "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + + res = RegCreateKeyA(userkey, "SourceList", &hkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* SourceList key exists */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(size == 0, "Expected 0, got %d\n", size); + + data = "msitest.msi"; + res = RegSetValueExA(hkey, "PackageName", 0, REG_SZ, (const BYTE *)data, lstrlenA(data) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* PackageName value exists */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(size == 11, "Expected 11, got %d\n", size); + + /* read the value, don't change size */ + lstrcpyA(value, "aaa"); + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, value, &size); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!lstrcmpA(value, "aaa"), "Expected 'aaa', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + /* read the value, fix size */ + size++; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAME, value, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(value, "msitest.msi"), "Expected 'msitest.msi', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + /* empty property now that product key exists */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, "", NULL, &size); + ok(r == ERROR_UNKNOWN_PROPERTY, "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r); + ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size); + + /* nonexistent property now that product key exists */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, "nonexistent", NULL, &size); + ok(r == ERROR_UNKNOWN_PROPERTY, "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r); + ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size); + + data = "tester"; + res = RegSetValueExA(hkey, "nonexistent", 0, REG_SZ, (const BYTE *)data, lstrlenA(data) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* nonexistent property now that nonexistent value exists */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, "nonexistent", NULL, &size); + ok(r == ERROR_UNKNOWN_PROPERTY, "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r); + ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size); + + /* invalid option now that product key exists */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + 4, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(size == 11, "Expected 11, got %d\n", size); + + RegDeleteValueA(hkey, "nonexistent"); + RegDeleteValueA(hkey, "PackageName"); + RegDeleteKeyA(hkey, ""); + RegDeleteKeyA(userkey, ""); + RegCloseKey(hkey); + RegCloseKey(userkey); + + /* try a patch */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PATCH, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Patches\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* patch key exists + * NOTE: using prodcode guid, but it really doesn't matter + */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PATCH, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_BAD_CONFIGURATION, "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(size == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", size); + + res = RegCreateKeyA(userkey, "SourceList", &hkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* SourceList key exists */ + size = 0xdeadbeef; + r = pMsiSourceListGetInfoA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PATCH, INSTALLPROPERTY_PACKAGENAME, NULL, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(size == 0, "Expected 0, got %d\n", size); + + RegDeleteKeyA(hkey, ""); + RegDeleteKeyA(userkey, ""); + RegCloseKey(hkey); + RegCloseKey(userkey); +} + +static void test_MsiSourceListAddSourceEx(void) +{ + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + CHAR keypath[MAX_PATH*2]; + CHAR value[MAX_PATH]; + LPSTR usersid; + LONG res; + UINT r; + HKEY prodkey, userkey, hkey; + HKEY url, net; + DWORD size; + + if (!pMsiSourceListAddSourceExA) + { + skip("Skipping MsiSourceListAddSourceExA tests\n"); + return; + } + + create_test_guid(prodcode, prod_squashed); + get_user_sid(&usersid); + + /* GetLastError is not set by the function */ + + /* NULL szProductCodeOrPatchCode */ + r = pMsiSourceListAddSourceExA(NULL, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* empty szProductCodeOrPatchCode */ + r = pMsiSourceListAddSourceExA("", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* garbage szProductCodeOrPatchCode */ + r = pMsiSourceListAddSourceExA("garbage", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* guid without brackets */ + r = pMsiSourceListAddSourceExA("51CD2AD5-0482-4C46-8DDD-0ED1022AA1AA", usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* guid with brackets */ + r = pMsiSourceListAddSourceExA("{51CD2AD5-0482-4C46-8DDD-0ED1022AA1AA}", usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + /* MSIINSTALLCONTEXT_USERUNMANAGED */ + + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + lstrcpyA(keypath, "Software\\Microsoft\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_CURRENT_USER, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user product key exists */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_BAD_CONFIGURATION, "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + + res = RegCreateKeyA(userkey, "SourceList", &url); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + RegCloseKey(url); + + /* SourceList key exists */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + res = RegOpenKeyA(userkey, "SourceList\\URL", &url); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + size = MAX_PATH; + res = RegQueryValueExA(url, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "C:\\source/"), "Expected 'C:\\source/', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + /* add another source, index 0 */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "another", 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + res = RegQueryValueExA(url, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "C:\\source/"), "Expected 'C:\\source/', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "2", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "another/"), "Expected 'another/', got %s\n", value); + ok(size == 9, "Expected 9, got %d\n", size); + + /* add another source, index 1 */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "third/", 1); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + res = RegQueryValueExA(url, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "third/"), "Expected 'third/', got %s\n", value); + ok(size == 7, "Expected 7, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "2", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "C:\\source/"), "Expected 'C:\\source/', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "3", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "another/"), "Expected 'another/', got %s\n", value); + ok(size == 9, "Expected 9, got %d\n", size); + + /* add another source, index > N */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "last/", 5); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + res = RegQueryValueExA(url, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "third/"), "Expected 'third/', got %s\n", value); + ok(size == 7, "Expected 7, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "2", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "C:\\source/"), "Expected 'C:\\source/', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "3", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "another/"), "Expected 'another/', got %s\n", value); + ok(size == 9, "Expected 9, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "4", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "last/"), "Expected 'last/', got %s\n", value); + ok(size == 6, "Expected 6, got %d\n", size); + + /* just MSISOURCETYPE_NETWORK */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSISOURCETYPE_NETWORK, "source", 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + res = RegOpenKeyA(userkey, "SourceList\\Net", &net); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + size = MAX_PATH; + res = RegQueryValueExA(net, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "source\\"), "Expected 'source\\', got %s\n", value); + ok(size == 8, "Expected 8, got %d\n", size); + + /* just MSISOURCETYPE_URL */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSISOURCETYPE_URL, "source", 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + res = RegQueryValueExA(url, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "third/"), "Expected 'third/', got %s\n", value); + ok(size == 7, "Expected 7, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "2", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "C:\\source/"), "Expected 'C:\\source/', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "3", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "another/"), "Expected 'another/', got %s\n", value); + ok(size == 9, "Expected 9, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "4", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "last/"), "Expected 'last/', got %s\n", value); + ok(size == 6, "Expected 6, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "5", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "source/"), "Expected 'source/', got %s\n", value); + ok(size == 8, "Expected 8, got %d\n", size); + + /* NULL szUserSid */ + r = pMsiSourceListAddSourceExA(prodcode, NULL, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSISOURCETYPE_NETWORK, "nousersid", 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + res = RegQueryValueExA(net, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "source\\"), "Expected 'source\\', got %s\n", value); + ok(size == 8, "Expected 8, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(net, "2", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "nousersid\\"), "Expected 'nousersid\\', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + /* invalid options, must have source type */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PRODUCT, "source", 0); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSICODE_PATCH, "source", 0); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* NULL szSource */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSISOURCETYPE_URL, NULL, 1); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* empty szSource */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + MSISOURCETYPE_URL, "", 1); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* MSIINSTALLCONTEXT_USERMANAGED, non-NULL szUserSid */ + + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Managed\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product key exists */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_BAD_CONFIGURATION, "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + + res = RegCreateKeyA(prodkey, "SourceList", &hkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + RegCloseKey(hkey); + + /* SourceList exists */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + res = RegOpenKeyA(prodkey, "SourceList\\URL", &url); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + size = MAX_PATH; + res = RegQueryValueExA(url, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "C:\\source/"), "Expected 'C:\\source/', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + RegCloseKey(url); + + /* MSIINSTALLCONTEXT_USERMANAGED, NULL szUserSid */ + + r = pMsiSourceListAddSourceExA(prodcode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "another", 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + res = RegOpenKeyA(prodkey, "SourceList\\URL", &url); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + size = MAX_PATH; + res = RegQueryValueExA(url, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "C:\\source/"), "Expected 'C:\\source/', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + size = MAX_PATH; + res = RegQueryValueExA(url, "2", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "another/"), "Expected 'another/', got %s\n", value); + ok(size == 9, "Expected 9, got %d\n", size); + + RegCloseKey(url); + RegCloseKey(prodkey); + + /* MSIINSTALLCONTEXT_MACHINE */ + + /* szUserSid must be NULL for MSIINSTALLCONTEXT_MACHINE */ + r = pMsiSourceListAddSourceExA(prodcode, usersid, + MSIINSTALLCONTEXT_MACHINE, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + r = pMsiSourceListAddSourceExA(prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_UNKNOWN_PRODUCT, "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + lstrcpyA(keypath, "Software\\Classes\\Installer\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &prodkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* product key exists */ + r = pMsiSourceListAddSourceExA(prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_BAD_CONFIGURATION, "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + + res = RegCreateKeyA(prodkey, "SourceList", &hkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + RegCloseKey(hkey); + + /* SourceList exists */ + r = pMsiSourceListAddSourceExA(prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + MSICODE_PRODUCT | MSISOURCETYPE_URL, "C:\\source", 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + res = RegOpenKeyA(prodkey, "SourceList\\URL", &url); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + size = MAX_PATH; + res = RegQueryValueExA(url, "1", NULL, NULL, (LPBYTE)value, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(value, "C:\\source/"), "Expected 'C:\\source/', got %s\n", value); + ok(size == 11, "Expected 11, got %d\n", size); + + RegCloseKey(url); + RegCloseKey(prodkey); + HeapFree(GetProcessHeap(), 0, usersid); +} + +START_TEST(source) +{ + init_functionpointers(); + + test_MsiSourceListGetInfo(); + test_MsiSourceListAddSourceEx(); +} diff --git a/rostests/winetests/msi/suminfo.c b/rostests/winetests/msi/suminfo.c index fd61fb096be..6ef98f7df77 100644 --- a/rostests/winetests/msi/suminfo.c +++ b/rostests/winetests/msi/suminfo.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "wine/test.h" @@ -62,7 +63,7 @@ #define PID_MSISOURCE PID_WORDCOUNT #define PID_MSIRESTRICT PID_CHARCOUNT -START_TEST(suminfo) +static void test_suminfo(void) { const char *msifile = "winetest.msi"; MSIHANDLE hdb = 0, hsuminfo; @@ -98,6 +99,12 @@ START_TEST(suminfo) r = MsiSummaryInfoGetProperty(hsuminfo, 0, NULL, NULL, NULL, 0, NULL); ok(r == ERROR_SUCCESS, "getpropcount failed\n"); + r = MsiSummaryInfoGetProperty(hsuminfo, -1, NULL, NULL, NULL, 0, NULL); + ok(r == ERROR_UNKNOWN_PROPERTY, "MsiSummaryInfoGetProperty wrong error\n"); + + r = MsiSummaryInfoGetProperty(hsuminfo, PID_SECURITY+1, NULL, NULL, NULL, 0, NULL); + ok(r == ERROR_UNKNOWN_PROPERTY, "MsiSummaryInfoGetProperty wrong error\n"); + type = -1; r = MsiSummaryInfoGetProperty(hsuminfo, 0, &type, NULL, NULL, 0, NULL); ok(r == ERROR_SUCCESS, "getpropcount failed\n"); @@ -235,3 +242,180 @@ START_TEST(suminfo) r = DeleteFile(msifile); ok(r, "DeleteFile failed\n"); } + +static const WCHAR tb[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */ +static const WCHAR sd[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */ +static const WCHAR sp[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */ + +#define LOSE_CONST(x) ((LPSTR)(UINT_PTR)(x)) + +static void test_create_database_binary(void) +{ + static const CLSID CLSID_MsiDatabase = + { 0xc1084, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46 } }; + static const CLSID IID_IPropertySetStorage = + { 0x13a, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46 } }; + static const CLSID FMTID_SummaryInformation = + { 0xf29f85e0, 0x4ff9, 0x1068, {0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9}}; + DWORD mode = STGM_CREATE | STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE; + static const WCHAR msifile[] = { + 'w','i','n','e','t','e','s','t','.','m','s','i',0 }; + IPropertySetStorage *pss = NULL; + IPropertyStorage *ps = NULL; + IStorage *stg = NULL; + IStream *stm = NULL; + HRESULT r; + PROPSPEC propspec[10]; + PROPVARIANT propvar[10]; + USHORT data[2] = { 0, 0 }; + + r = StgCreateDocfile( msifile, mode, 0, &stg ); + ok( r == S_OK, "failed to create database\n"); + + r = IStorage_SetClass( stg, &CLSID_MsiDatabase ); + ok( r == S_OK, "failed to set clsid\n"); + + /* create the _StringData stream */ + r = IStorage_CreateStream( stg, sd, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm ); + ok( r == S_OK, "failed to create stream\n"); + + IStream_Release( stm ); + + /* create the _StringPool stream */ + r = IStorage_CreateStream( stg, sp, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm ); + ok( r == S_OK, "failed to create stream\n"); + + r = IStream_Write( stm, data, sizeof data, NULL ); + ok( r == S_OK, "failed to write stream\n"); + + IStream_Release( stm ); + + /* create the _Tables stream */ + r = IStorage_CreateStream( stg, tb, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm ); + ok( r == S_OK, "failed to create stream\n"); + + IStream_Release( stm ); + + r = IStorage_QueryInterface( stg, &IID_IPropertySetStorage, (void**) &pss ); + ok( r == S_OK, "failed to set clsid\n"); + + r = IPropertySetStorage_Create( pss, &FMTID_SummaryInformation, NULL, 0, mode, &ps ); + ok( r == S_OK, "failed to create property set\n"); + + r = IPropertyStorage_SetClass( ps, &FMTID_SummaryInformation ); + ok( r == S_OK, "failed to set class\n"); + + propspec[0].ulKind = PRSPEC_PROPID; + U(propspec[0]).propid = PID_TITLE; + propvar[0].vt = VT_LPSTR; + U(propvar[0]).pszVal = LOSE_CONST("test title"); + + propspec[1].ulKind = PRSPEC_PROPID; + U(propspec[1]).propid = PID_SUBJECT; + propvar[1].vt = VT_LPSTR; + U(propvar[1]).pszVal = LOSE_CONST("msi suminfo / property storage test"); + + propspec[2].ulKind = PRSPEC_PROPID; + U(propspec[2]).propid = PID_AUTHOR; + propvar[2].vt = VT_LPSTR; + U(propvar[2]).pszVal = LOSE_CONST("mike_m"); + + propspec[3].ulKind = PRSPEC_PROPID; + U(propspec[3]).propid = PID_TEMPLATE; + propvar[3].vt = VT_LPSTR; + U(propvar[3]).pszVal = LOSE_CONST(";1033"); /* actually the string table's codepage */ + + propspec[4].ulKind = PRSPEC_PROPID; + U(propspec[4]).propid = PID_REVNUMBER; + propvar[4].vt = VT_LPSTR; + U(propvar[4]).pszVal = LOSE_CONST("{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}"); + + propspec[5].ulKind = PRSPEC_PROPID; + U(propspec[5]).propid = PID_PAGECOUNT; + propvar[5].vt = VT_I4; + U(propvar[5]).lVal = 100; + + propspec[6].ulKind = PRSPEC_PROPID; + U(propspec[6]).propid = PID_WORDCOUNT; + propvar[6].vt = VT_I4; + U(propvar[6]).lVal = 0; + + /* MSDN says that PID_LASTPRINTED should be a VT_FILETIME... */ + propspec[7].ulKind = PRSPEC_PROPID; + U(propspec[7]).propid = PID_LASTPRINTED; + propvar[7].vt = VT_LPSTR; + U(propvar[7]).pszVal = LOSE_CONST("7/1/1999 5:17"); + + r = IPropertyStorage_WriteMultiple( ps, 8, propspec, propvar, PID_FIRST_USABLE ); + ok( r == S_OK, "failed to write properties\n"); + + IPropertyStorage_Commit( ps, STGC_DEFAULT ); + + IPropertyStorage_Release( ps ); + IPropertySetStorage_Release( pss ); + + IStorage_Commit( stg, STGC_DEFAULT ); + IStorage_Release( stg ); +} + +static void test_summary_binary(void) +{ + const char *msifile = "winetest.msi"; + MSIHANDLE hdb = 0, hsuminfo = 0; + UINT r, type, count; + INT ival; + DWORD sz; + char sval[20]; + + DeleteFile( msifile ); + + test_create_database_binary(); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes(msifile), "file doesn't exist!\n"); + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + r = MsiGetSummaryInformation(hdb, NULL, 0, &hsuminfo); + ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n"); + + /* + * Check what reading PID_LASTPRINTED does... + * The string value is written to the msi file + * but it appears that we're not allowed to read it back again. + * We can still read its type though...? + */ + sz = sizeof sval; + sval[0] = 0; + type = 0; + r = MsiSummaryInfoGetProperty(hsuminfo, PID_LASTPRINTED, &type, NULL, NULL, sval, &sz); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoGetProperty failed\n"); + ok(!strcmp(sval, ""), "Expected empty string, got %s\n", sval); + todo_wine { + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %d\n", type); + ok(sz == 0, "Expected 0, got %d\n", sz); + } + + ival = -1; + r = MsiSummaryInfoGetProperty(hsuminfo, PID_WORDCOUNT, &type, &ival, NULL, NULL, NULL); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoGetProperty failed\n"); + todo_wine ok( ival == 0, "value incorrect\n"); + + /* looks like msi adds some of its own values in here */ + count = 0; + r = MsiSummaryInfoGetPropertyCount( hsuminfo, &count ); + ok(r == ERROR_SUCCESS, "getpropcount failed\n"); + todo_wine ok(count == 10, "prop count incorrect\n"); + + MsiCloseHandle( hsuminfo ); + MsiCloseHandle( hdb ); + + DeleteFile( msifile ); +} + +START_TEST(suminfo) +{ + test_suminfo(); + test_summary_binary(); +} diff --git a/rostests/winetests/msi/testlist.c b/rostests/winetests/msi/testlist.c index 71e56e9fc00..f8d7679f0d4 100644 --- a/rostests/winetests/msi/testlist.c +++ b/rostests/winetests/msi/testlist.c @@ -1,25 +1,31 @@ +/* Automatically generated file; DO NOT EDIT!! */ + #define WIN32_LEAN_AND_MEAN #include #define STANDALONE #include "wine/test.h" +extern void func_automation(void); extern void func_db(void); extern void func_format(void); extern void func_install(void); extern void func_msi(void); extern void func_package(void); extern void func_record(void); +extern void func_source(void); extern void func_suminfo(void); const struct test winetest_testlist[] = { + { "automation", func_automation }, { "db", func_db }, { "format", func_format }, { "install", func_install }, { "msi", func_msi }, { "package", func_package }, { "record", func_record }, + { "source", func_source }, { "suminfo", func_suminfo }, { 0, 0 } };