diff --git a/rostests/winetests/msi/automation.c b/rostests/winetests/msi/automation.c index 68b0f75c25c..9568fbf75f4 100644 --- a/rostests/winetests/msi/automation.c +++ b/rostests/winetests/msi/automation.c @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -31,6 +32,8 @@ #include "wine/test.h" +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); + 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 }; @@ -60,8 +63,7 @@ static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttribu "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"; + "component\t\tMSITESTDIR\t0\t1\tfile\n"; static const CHAR directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n" "s72\tS72\tl255\n" @@ -82,8 +84,7 @@ static const CHAR feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\t "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"; + "feature\t\t\t\t2\t1\tTARGETDIR\t0\n"; static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n" "s38\ts72\n" @@ -93,8 +94,7 @@ static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n" "One\tOne\n" "Three\tThree\n" "Two\tTwo\n" - "feature\tcomponent\n" - "service_feature\tservice_comp\n"; + "feature\tcomponent\n"; static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" @@ -104,8 +104,7 @@ static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tL "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"; + "file\tcomponent\tfilename\t100\t\t\t8192\t1\n"; static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n" "s72\tS255\tI2\n" @@ -115,7 +114,6 @@ static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\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" @@ -155,17 +153,6 @@ static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_ "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; @@ -185,9 +172,7 @@ static const msi_table tables[] = ADD_TABLE(install_exec_seq), ADD_TABLE(media), ADD_TABLE(property), - ADD_TABLE(registry), - ADD_TABLE(service_install), - ADD_TABLE(service_control) + ADD_TABLE(registry) }; typedef struct _msi_summary_info @@ -340,7 +325,6 @@ static void create_test_files(void) 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) @@ -365,7 +349,6 @@ 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\\cabout\\new"); RemoveDirectoryA("msitest\\cabout"); @@ -374,24 +357,6 @@ static void delete_test_files(void) 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 */ @@ -408,6 +373,15 @@ static CHAR string1[MAX_PATH], string2[MAX_PATH]; ok(0, format, string1, string2); \ } +#define ok_w2n(format, szString1, szString2, len) \ +\ + if (memcmp(szString1, szString2, len * sizeof(WCHAR)) != 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); \ @@ -801,7 +775,7 @@ static HRESULT Installer_RegistryValueI(HKEY hkey, LPCWSTR szKey, int iValue, LP V_I4(&vararg) = iValue; hr = Installer_RegistryValue(hkey, szKey, vararg, &varresult, vtResult); - if (vtResult == VT_BSTR) lstrcpyW(szString, V_BSTR(&varresult)); + if (SUCCEEDED(hr) && vtResult == VT_BSTR) lstrcpyW(szString, V_BSTR(&varresult)); VariantClear(&varresult); return hr; } @@ -1072,6 +1046,27 @@ static HRESULT Session_EvaluateCondition(IDispatch *pSession, LPCWSTR szConditio return hr; } +static HRESULT Session_Message(IDispatch *pSession, long kind, IDispatch *record, int *ret) +{ + VARIANT varresult; + VARIANTARG vararg[2]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&varresult); + V_VT(vararg) = VT_DISPATCH; + V_DISPATCH(vararg) = record; + V_VT(vararg+1) = VT_I4; + V_I4(vararg+1) = kind; + + hr = invoke(pSession, "Message", DISPATCH_METHOD, &dispparams, &varresult, VT_I4); + + ok(V_VT(&varresult) == VT_I4, "V_VT(varresult) = %d\n", V_VT(&varresult)); + *ret = V_I4(&varresult); + + return hr; +} + static HRESULT Session_SetInstallLevel(IDispatch *pSession, long iInstallLevel) { VARIANT varresult; @@ -1646,7 +1641,7 @@ static void test_Session(IDispatch *pSession) UINT len; BOOL bool; int myint; - IDispatch *pDatabase = NULL, *pInst = NULL; + IDispatch *pDatabase = NULL, *pInst = NULL, *record = NULL; HRESULT hr; /* Session::Installer */ @@ -1743,6 +1738,13 @@ static void test_Session(IDispatch *pSession) 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::Message */ + hr = Installer_CreateRecord(0, &record); + ok(hr == S_OK, "Installer_CreateRecord failed: %08x\n", hr); + hr = Session_Message(pSession, INSTALLMESSAGE_INFO, record, &myint); + ok(hr == S_OK, "Session_Message failed: %08x\n", hr); + ok(myint == 0, "Session_Message returned %x\n", myint); + /* Session::EvaluateCondition */ hr = Session_EvaluateCondition(pSession, szOneStateFalse, &myint); ok(hr == S_OK, "Session_EvaluateCondition failed, hresult 0x%08x\n", hr); @@ -1814,9 +1816,18 @@ static void test_Installer_RegistryValue(void) HKEY curr_user = (HKEY)1; HRESULT hr; BOOL bRet; + LONG lRet; /* Delete keys */ - if (!RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey )) delete_key( hkey ); + SetLastError(0xdeadbeef); + lRet = RegOpenKeyW( HKEY_CURRENT_USER, szKey, &hkey ); + if (!lRet && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + win_skip("Needed W-functions are not implemented\n"); + return; + } + if (!lRet) + delete_key( hkey ); /* Does our key exist? Shouldn't; check with all three possible value parameter types */ hr = Installer_RegistryValueE(curr_user, szKey, &bRet); @@ -1847,7 +1858,7 @@ static void test_Installer_RegistryValue(void) "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), + ok(!RegSetValueExW(hkey,szSeven,0,REG_NONE, NULL, 0), "RegSetValueExW failed\n"); ok(!RegSetValueExW(hkey,NULL,0,REG_SZ, (const BYTE *)szOne, sizeof(szOne)), @@ -1895,10 +1906,12 @@ static void test_Installer_RegistryValue(void) 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); + /* Vista does not NULL-terminate this case */ memset(szString, 0, sizeof(szString)); hr = Installer_RegistryValueW(curr_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); + ok_w2n("Registry value \"%s\" does not match expected \"%s\"\n", + szString, szFiveHi, lstrlenW(szFiveHi)); memset(szString, 0, sizeof(szString)); hr = Installer_RegistryValueW(curr_user, szKey, szSix, szString); @@ -2241,7 +2254,6 @@ static void test_Installer_InstallProduct(void) 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); @@ -2275,8 +2287,6 @@ static void test_Installer_InstallProduct(void) res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest"); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); - check_service_is_installed(); - /* Remove registry keys written by RegisterProduct standard action */ res = RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{F1C3AF50-8B56-4A69-A00C-00773FE42F30}"); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); diff --git a/rostests/winetests/msi/db.c b/rostests/winetests/msi/db.c index 5d5eb08f3c8..475b10b970a 100644 --- a/rostests/winetests/msi/db.c +++ b/rostests/winetests/msi/db.c @@ -144,6 +144,9 @@ static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec) MSIHANDLE hview = 0; UINT r, ret; + if (phrec) + *phrec = 0; + /* open a select query */ r = MsiDatabaseOpenView(hdb, query, &hview); if (r != ERROR_SUCCESS) @@ -190,6 +193,27 @@ static UINT create_component_table( MSIHANDLE hdb ) "PRIMARY KEY `Component`)" ); } +static UINT create_custom_action_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `CustomAction` ( " + "`Action` CHAR(72) NOT NULL, " + "`Type` SHORT NOT NULL, " + "`Source` CHAR(72), " + "`Target` CHAR(255) " + "PRIMARY KEY `Action`)" ); +} + +static UINT create_directory_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `Directory` ( " + "`Directory` CHAR(255) NOT NULL, " + "`Directory_Parent` CHAR(255), " + "`DefaultDir` CHAR(255) NOT NULL " + "PRIMARY KEY `Directory`)" ); +} + static UINT create_feature_components_table( MSIHANDLE hdb ) { return run_query( hdb, 0, @@ -217,69 +241,38 @@ static UINT create_binary_table( MSIHANDLE hdb ) "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; +#define make_add_entry(type, qtext) \ + static UINT add##_##type##_##entry( MSIHANDLE hdb, const char *values ) \ + { \ + char insert[] = qtext; \ + 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; \ + } - 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; -} +make_add_entry(component, + "INSERT INTO `Component` " + "(`Component`, `ComponentId`, `Directory_`, " + "`Attributes`, `Condition`, `KeyPath`) VALUES( %s )") -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; +make_add_entry(custom_action, + "INSERT INTO `CustomAction` " + "(`Action`, `Type`, `Source`, `Target`) VALUES( %s )") - 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; -} +make_add_entry(feature_components, + "INSERT INTO `FeatureComponents` " + "(`Feature_`, `Component_`) VALUES( %s )") -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; +make_add_entry(std_dlls, + "INSERT INTO `StdDlls` (`File`, `Binary_`) VALUES( %s )") - 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; -} +make_add_entry(binary, + "INSERT INTO `Binary` (`Name`, `Data`) VALUES( %s )") static void test_msiinsert(void) { @@ -1345,17 +1338,28 @@ static void test_longstrings(void) DeleteFile(msifile); } -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); + if (file == INVALID_HANDLE_VALUE) + return; + + WriteFile(file, data, strlen(data), &written, NULL); WriteFile(file, "\n", strlen("\n"), &written, NULL); + + if (size) + { + SetFilePointer(file, size, NULL, FILE_BEGIN); + SetEndOfFile(file); + } + CloseHandle(file); } + +#define create_file(name) create_file_data(name, name, 0) static void test_streamtable(void) { @@ -2331,12 +2335,7 @@ static MSIHANDLE create_package_db(LPCSTR filename) res = set_summary_info(hdb); - res = run_query( hdb, 0, - "CREATE TABLE `Directory` ( " - "`Directory` CHAR(255) NOT NULL, " - "`Directory_Parent` CHAR(255), " - "`DefaultDir` CHAR(255) NOT NULL " - "PRIMARY KEY `Directory`)" ); + res = create_directory_table(hdb); ok( res == ERROR_SUCCESS , "Failed to create directory table\n" ); return hdb; @@ -4695,7 +4694,7 @@ static const struct { {_StringPool, data14, sizeof data14}, }; -void enum_stream_names(IStorage *stg) +static void enum_stream_names(IStorage *stg) { IEnumSTATSTG *stgenum = NULL; IStream *stm; @@ -5996,11 +5995,47 @@ static void test_where_viewmodify(void) MsiCloseHandle(hdb); } +static BOOL create_storage(LPCSTR name) +{ + WCHAR nameW[MAX_PATH]; + IStorage *stg; + IStream *stm; + HRESULT hr; + DWORD count; + BOOL res = FALSE; + + MultiByteToWideChar(CP_ACP, 0, name, -1, nameW, MAX_PATH); + hr = StgCreateDocfile(nameW, STGM_CREATE | STGM_READWRITE | + STGM_DIRECT | STGM_SHARE_EXCLUSIVE, 0, &stg); + if (FAILED(hr)) + return FALSE; + + hr = IStorage_CreateStream(stg, nameW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &stm); + if (FAILED(hr)) + goto done; + + hr = IStream_Write(stm, "stgdata", 8, &count); + if (SUCCEEDED(hr)) + res = TRUE; + +done: + IStream_Release(stm); + IStorage_Release(stg); + + return res; +} + static void test_storages_table(void) { MSIHANDLE hdb, hview, hrec; + IStorage *stg, *inner; + IStream *stm; char file[MAX_PATH]; char buf[MAX_PATH]; + WCHAR name[MAX_PATH]; + LPCSTR query; + HRESULT hr; DWORD size; UINT r; @@ -6018,78 +6053,54 @@ static void test_storages_table(void) /* check the column types */ hrec = get_column_info(hdb, "SELECT * FROM `_Storages`", MSICOLINFO_TYPES); ok(hrec, "failed to get column info hrecord\n"); - todo_wine - { - ok(check_record(hrec, 1, "s62"), "wrong hrecord type\n"); - ok(check_record(hrec, 2, "V0"), "wrong hrecord type\n"); - } + ok(check_record(hrec, 1, "s62"), "wrong hrecord type\n"); + ok(check_record(hrec, 2, "V0"), "wrong hrecord type\n"); MsiCloseHandle(hrec); /* now try the names */ hrec = get_column_info(hdb, "SELECT * FROM `_Storages`", MSICOLINFO_NAMES); ok(hrec, "failed to get column info hrecord\n"); - todo_wine - { - ok(check_record(hrec, 1, "Name"), "wrong hrecord type\n"); - ok(check_record(hrec, 2, "Data"), "wrong hrecord type\n"); - } + ok(check_record(hrec, 1, "Name"), "wrong hrecord type\n"); + ok(check_record(hrec, 2, "Data"), "wrong hrecord type\n"); MsiCloseHandle(hrec); - /* insert a file into the _Storages table */ - create_file("test.txt"); + create_storage("storage.bin"); hrec = MsiCreateRecord(2); - MsiRecordSetString(hrec, 1, "data"); + MsiRecordSetString(hrec, 1, "stgname"); - r = MsiRecordSetStream(hrec, 2, "test.txt"); + r = MsiRecordSetStream(hrec, 2, "storage.bin"); ok(r == ERROR_SUCCESS, "Failed to add stream data to the hrecord: %d\n", r); - DeleteFile("test.txt"); + DeleteFileA("storage.bin"); - r = MsiDatabaseOpenView(hdb, - "INSERT INTO `_Storages` (`Name`, `Data`) VALUES (?, ?)", &hview); - todo_wine - { - ok(r == ERROR_SUCCESS, "Failed to open database hview: %d\n", r); - } + query = "INSERT INTO `_Storages` (`Name`, `Data`) VALUES (?, ?)"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Failed to open database hview: %d\n", r); r = MsiViewExecute(hview, hrec); - todo_wine - { - ok(r == ERROR_SUCCESS, "Failed to execute hview: %d\n", r); - } + ok(r == ERROR_SUCCESS, "Failed to execute hview: %d\n", r); MsiCloseHandle(hrec); + MsiViewClose(hview); MsiCloseHandle(hview); - r = MsiDatabaseOpenView(hdb, - "SELECT `Name`, `Data` FROM `_Storages`", &hview); - todo_wine - { - ok(r == ERROR_SUCCESS, "Failed to open database hview: %d\n", r); - } + query = "SELECT `Name`, `Data` FROM `_Storages`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Failed to open database hview: %d\n", r); r = MsiViewExecute(hview, 0); - todo_wine - { - ok(r == ERROR_SUCCESS, "Failed to execute hview: %d\n", r); - } + ok(r == ERROR_SUCCESS, "Failed to execute hview: %d\n", r); r = MsiViewFetch(hview, &hrec); - todo_wine - { - ok(r == ERROR_SUCCESS, "Failed to fetch hrecord: %d\n", r); - } + ok(r == ERROR_SUCCESS, "Failed to fetch hrecord: %d\n", r); size = MAX_PATH; r = MsiRecordGetString(hrec, 1, file, &size); - todo_wine - { - ok(r == ERROR_SUCCESS, "Failed to get string: %d\n", r); - ok(!lstrcmp(file, "data"), "Expected 'data', got %s\n", file); - } + ok(r == ERROR_SUCCESS, "Failed to get string: %d\n", r); + ok(!lstrcmp(file, "stgname"), "Expected \"stgname\", got \"%s\"\n", file); size = MAX_PATH; lstrcpyA(buf, "apple"); @@ -6097,21 +6108,827 @@ static void test_storages_table(void) ok(!lstrcmp(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf); todo_wine { - ok(r == ERROR_SUCCESS, "Failed to get stream: %d\n", r); + ok(r == ERROR_INVALID_DATA, "Expected ERROR_INVALID_DATA, got %d\n", r); ok(size == 0, "Expected 0, got %d\n", size); } MsiCloseHandle(hrec); r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + MsiDatabaseCommit(hdb); + MsiCloseHandle(hdb); + + MultiByteToWideChar(CP_ACP, 0, msifile, -1, name, MAX_PATH); + hr = StgOpenStorage(name, NULL, STGM_DIRECT | STGM_READ | + STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(stg != NULL, "Expected non-NULL storage\n"); + + MultiByteToWideChar(CP_ACP, 0, "stgname", -1, name, MAX_PATH); + hr = IStorage_OpenStorage(stg, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, + NULL, 0, &inner); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(inner != NULL, "Expected non-NULL storage\n"); + + MultiByteToWideChar(CP_ACP, 0, "storage.bin", -1, name, MAX_PATH); + hr = IStorage_OpenStream(inner, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(stm != NULL, "Expected non-NULL stream\n"); + + hr = IStream_Read(stm, buf, MAX_PATH, &size); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(size == 8, "Expected 8, got %d\n", size); + ok(!lstrcmpA(buf, "stgdata"), "Expected \"stgdata\", got \"%s\"\n", buf); + + IStream_Release(stm); + IStorage_Release(inner); + + IStorage_Release(stg); + DeleteFileA(msifile); +} + +static void test_dbtopackage(void) +{ + MSIHANDLE hdb, hpkg; + CHAR package[10]; + CHAR buf[MAX_PATH]; + DWORD size; + UINT r; + + /* create an empty database, transact mode */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Failed to create database\n"); + + set_summary_info(hdb); + + r = create_directory_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = create_custom_action_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_custom_action_entry(hdb, "'SetProp', 51, 'MYPROP', 'grape'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sprintf(package, "#%li", hdb); + r = MsiOpenPackage(package, &hpkg); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* property is not set yet */ + size = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiGetProperty(hpkg, "MYPROP", buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf); + ok(size == 0, "Expected 0, got %d\n", size); + + /* run the custom action to set the property */ + r = MsiDoAction(hpkg, "SetProp"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* property is now set */ + size = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiGetProperty(hpkg, "MYPROP", buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "grape"), "Expected \"grape\", got \"%s\"\n", buf); + ok(size == 5, "Expected 5, got %d\n", size); + + MsiCloseHandle(hpkg); + + /* reset the package */ + r = MsiOpenPackage(package, &hpkg); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* property is not set anymore */ + size = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiGetProperty(hpkg, "MYPROP", buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); todo_wine { - ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf); + ok(size == 0, "Expected 0, got %d\n", size); } - MsiCloseHandle(hview); MsiCloseHandle(hdb); - DeleteFile(msifile); + MsiCloseHandle(hpkg); + + /* create an empty database, direct mode */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATEDIRECT, &hdb); + ok(r == ERROR_SUCCESS, "Failed to create database\n"); + + set_summary_info(hdb); + + r = create_directory_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = create_custom_action_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_custom_action_entry(hdb, "'SetProp', 51, 'MYPROP', 'grape'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sprintf(package, "#%li", hdb); + r = MsiOpenPackage(package, &hpkg); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* property is not set yet */ + size = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiGetProperty(hpkg, "MYPROP", buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf); + ok(size == 0, "Expected 0, got %d\n", size); + + /* run the custom action to set the property */ + r = MsiDoAction(hpkg, "SetProp"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* property is now set */ + size = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiGetProperty(hpkg, "MYPROP", buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "grape"), "Expected \"grape\", got \"%s\"\n", buf); + ok(size == 5, "Expected 5, got %d\n", size); + + MsiCloseHandle(hpkg); + + /* reset the package */ + r = MsiOpenPackage(package, &hpkg); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* property is not set anymore */ + size = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiGetProperty(hpkg, "MYPROP", buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf); + ok(size == 0, "Expected 0, got %d\n", size); + } + + MsiCloseHandle(hdb); + MsiCloseHandle(hpkg); + DeleteFileA(msifile); +} + +static void test_droptable(void) +{ + MSIHANDLE hdb, hview, hrec; + CHAR buf[MAX_PATH]; + LPCSTR query; + DWORD size; + UINT r; + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "SELECT * FROM `_Tables` WHERE `Name` = 'One'"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'One'"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 3, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "A"), "Expected \"A\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, + "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "DROP `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + hview = 0; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_FUNCTION_FAILED, + "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `IDontExist`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE One"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `_Tables` WHERE `Name` = 'One'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'One'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "CREATE TABLE `One` ( `B` INT, `C` INT PRIMARY KEY `B` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "SELECT * FROM `_Tables` WHERE `Name` = 'One'"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'One'"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 3, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "B"), "Expected \"B\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 3, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "C"), "Expected \"C\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, + "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "DROP TABLE One"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `_Tables` WHERE `Name` = 'One'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'One'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void test_dbmerge(void) +{ + MSIHANDLE hdb, href, hview, hrec; + CHAR buf[MAX_PATH]; + LPCSTR query; + DWORD size; + UINT r; + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiOpenDatabase("refdb.msi", MSIDBOPEN_CREATE, &href); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* hDatabase is invalid */ + r = MsiDatabaseMergeA(0, href, "MergeErrors"); + ok(r == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", r); + + /* hDatabaseMerge is invalid */ + r = MsiDatabaseMergeA(hdb, 0, "MergeErrors"); + ok(r == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", r); + + /* szTableName is NULL */ + r = MsiDatabaseMergeA(hdb, href, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* szTableName is empty */ + r = MsiDatabaseMergeA(hdb, href, ""); + ok(r == ERROR_INVALID_TABLE, "Expected ERROR_INVALID_TABLE, got %d\n", r); + + /* both DBs empty, szTableName is valid */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` CHAR(72) PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* column types don't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_DATATYPE_MISMATCH, + "Expected ERROR_DATATYPE_MISMATCH, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `C` INT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* column names don't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_DATATYPE_MISMATCH, + "Expected ERROR_DATATYPE_MISMATCH, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `B` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* primary keys don't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_DATATYPE_MISMATCH, + "Expected ERROR_DATATYPE_MISMATCH, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A`, `B` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* number of primary keys doesn't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_DATATYPE_MISMATCH, + "Expected ERROR_DATATYPE_MISMATCH, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT, `C` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 2 )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* number of columns doesn't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 3); + ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r); + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT, `C` INT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B`, `C` ) VALUES ( 1, 2, 3 )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* number of columns doesn't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 3); + ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r); + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 1 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 2, 2 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 2 )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 2, 3 )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* primary keys match, rows do not */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_FUNCTION_FAILED, + "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + MsiCloseHandle(hrec); + + r = MsiDatabaseOpenViewA(hdb, "SELECT * FROM `MergeErrors`", &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(hview, MSICOLINFO_NAMES, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 1, buf, &size); + ok(!lstrcmpA(buf, "Table"), "Expected \"Table\", got \"%s\"\n", buf); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 2, buf, &size); + ok(!lstrcmpA(buf, "NumRowMergeConflicts"), + "Expected \"NumRowMergeConflicts\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewGetColumnInfo(hview, MSICOLINFO_TYPES, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 1, buf, &size); + ok(!lstrcmpA(buf, "s255"), "Expected \"s255\", got \"%s\"\n", buf); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 2, buf, &size); + ok(!lstrcmpA(buf, "i2"), "Expected \"i2\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "DROP TABLE `MergeErrors`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + create_file_data("codepage.idt", "\r\n\r\n850\t_ForceCodepage\r\n", 0); + + GetCurrentDirectoryA(MAX_PATH, buf); + r = MsiDatabaseImportA(hdb, buf, "codepage.idt"); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( " + "`A` INT, `B` CHAR(72) LOCALIZABLE PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( " + "`A` INT, `B` CHAR(72) LOCALIZABLE PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 'hi' )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* code page does not match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "hi"), "Expected \"hi\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` OBJECT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` OBJECT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + create_file("binary.dat"); + hrec = MsiCreateRecord(1); + MsiRecordSetStreamA(hrec, 1, "binary.dat"); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, ? )"; + r = run_query(href, hrec, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + + /* binary data to merge */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + ZeroMemory(buf, MAX_PATH); + r = MsiRecordReadStream(hrec, 2, buf, &size); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "binary.dat\n"), + "Expected \"binary.dat\\n\", got \"%s\"\n", buf); + } + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + MsiCloseHandle(hdb); + MsiCloseHandle(href); + DeleteFileA(msifile); + DeleteFileA("refdb.msi"); + DeleteFileA("codepage.idt"); + DeleteFileA("binary.dat"); } START_TEST(db) @@ -6152,4 +6969,7 @@ START_TEST(db) test_viewmodify_refresh(); test_where_viewmodify(); test_storages_table(); + test_dbtopackage(); + test_droptable(); + test_dbmerge(); } diff --git a/rostests/winetests/msi/format.c b/rostests/winetests/msi/format.c index 00d3856c820..c4be4294650 100644 --- a/rostests/winetests/msi/format.c +++ b/rostests/winetests/msi/format.c @@ -108,97 +108,46 @@ static UINT create_custom_action_table( MSIHANDLE hdb ) "PRIMARY KEY `Action`)" ); } -static UINT add_feature_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `Feature` (`Feature`, `Feature_Parent`, " - "`Title`, `Description`, `Display`, `Level`, `Directory_`, `Attributes`) VALUES( %s )"; - char *query; - UINT sz, r; +#define make_add_entry(type, qtext) \ + static UINT add##_##type##_##entry( MSIHANDLE hdb, const char *values ) \ + { \ + char insert[] = qtext; \ + 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; \ + } - 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; -} +make_add_entry(feature, + "INSERT INTO `Feature` " + "(`Feature`, `Feature_Parent`, `Title`, `Description`, " + "`Display`, `Level`, `Directory_`, `Attributes`) VALUES( %s )") -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; +make_add_entry(component, + "INSERT INTO `Component` " + "(`Component`, `ComponentId`, `Directory_`, " + "`Attributes`, `Condition`, `KeyPath`) VALUES( %s )") - 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; -} +make_add_entry(feature_components, + "INSERT INTO `FeatureComponents` " + "(`Feature_`, `Component_`) VALUES( %s )") -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; +make_add_entry(file, + "INSERT INTO `File` " + "(`File`, `Component_`, `FileName`, `FileSize`, " + "`Version`, `Language`, `Attributes`, `Sequence`) VALUES( %s )") - 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; -} +make_add_entry(directory, + "INSERT INTO `Directory` " + "(`Directory`,`Directory_Parent`,`DefaultDir`) VALUES( %s )") -static UINT add_file_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `File` " - "(`File`, `Component_`, `FileName`, `FileSize`, `Version`, `Language`, `Attributes`, `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_directory_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `Directory` (`Directory`,`Directory_Parent`,`DefaultDir`) 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_custom_action_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `CustomAction` (`Action`, `Type`, `Source`, " - "`Target`) 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; -} +make_add_entry(custom_action, + "INSERT INTO `CustomAction` " + "(`Action`, `Type`, `Source`, `Target`) VALUES( %s )") static UINT set_summary_info(MSIHANDLE hdb) { diff --git a/rostests/winetests/msi/install.c b/rostests/winetests/msi/install.c index 8fa729a0833..56d36e1108f 100644 --- a/rostests/winetests/msi/install.c +++ b/rostests/winetests/msi/install.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "wine/test.h" @@ -39,6 +40,12 @@ static UINT (WINAPI *pMsiSourceListEnumSourcesA) static UINT (WINAPI *pMsiSourceListGetInfoA) (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, DWORD, LPCSTR, LPSTR, LPDWORD); +static HMODULE hsrclient = 0; +static BOOL (WINAPI *pSRRemoveRestorePoint)(DWORD); +static BOOL (WINAPI *pSRSetRestorePointA)(RESTOREPOINTINFOA*, STATEMGRSTATUS*); + +static BOOL on_win9x = FALSE; + static const char *msifile = "msitest.msi"; static const char *msifile2 = "winetest2.msi"; static const char *mstfile = "winetest.mst"; @@ -439,6 +446,30 @@ static const CHAR pp_install_exec_seq_dat[] = "Action\tCondition\tSequence\n" "PublishProduct\tPUBLISH_PRODUCT=1 Or FULL=1\t6400\n" "InstallFinalize\t\t6600"; +static const CHAR ppc_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" + "augustus\t{5AD3C142-CEF8-490D-B569-784D80670685}\tMSITESTDIR\t1\t\taugustus\n"; + +static const CHAR ppc_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\n" + "augustus\taugustus\taugustus\t500\t\t\t8192\t2"; + +static const CHAR ppc_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n" + "i2\ti4\tL64\tS255\tS32\tS72\n" + "Media\tDiskId\n" + "1\t2\t\t\tDISK1\t\n"; + +static const CHAR ppc_feature_comp_dat[] = "Feature_\tComponent_\n" + "s38\ts72\n" + "FeatureComponents\tFeature_\tComponent_\n" + "feature\tmaximus\n" + "feature\taugustus\n" + "montecristo\tmaximus"; + static const CHAR tp_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" "s72\tS38\ts72\ti2\tS255\tS72\n" "Component\tComponent\n" @@ -475,7 +506,7 @@ static const CHAR adm_admin_exec_seq_dat[] = "Action\tCondition\tSequence\n" 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"; + "augustus\t\tMSITESTDIR\t0\tMYPROP=2718 and MyProp=42\taugustus\n"; static const CHAR rem_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" "s72\tS38\ts72\ti2\tS255\tS72\n" @@ -527,7 +558,8 @@ static const CHAR rem_remove_files_dat[] = "FileKey\tComponent_\tFileName\tDirPr "attoparsec\tlithium\tattoparsec\tMSITESTDIR\t2\n" "storeys\thydrogen\tstoreys\tMSITESTDIR\t3\n" "block\thelium\tblock\tMSITESTDIR\t3\n" - "siriometer\tlithium\tsiriometer\tMSITESTDIR\t3\n"; + "siriometer\tlithium\tsiriometer\tMSITESTDIR\t3\n" + "nanoacre\thydrogen\t\tCABOUTDIR\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" @@ -545,6 +577,7 @@ static const CHAR mov_move_file_dat[] = "FileKey\tComponent_\tSourceName\tDestNa "kazakhstan\taugustus\t\tkiribati\tFILEPATHGOOD\tMSITESTDIR\t1\n" "laos\taugustus\tlatvia\tlebanon\tSourceDir\tMSITESTDIR\t1\n" "namibia\taugustus\tnauru\tkiribati\tSourceDir\tMSITESTDIR\t1\n" + "pakistan\taugustus\tperu\tsfn|poland\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" @@ -617,7 +650,9 @@ static const CHAR ca51_install_exec_seq_dat[] = "Action\tCondition\tSequence\n" "GoodSetProperty\t\t725\n" "BadSetProperty\t\t750\n" "CostInitialize\t\t800\n" + "ResolveSource\t\t810\n" "FileCost\t\t900\n" + "SetSourceDir\tSRCDIR\t910\n" "CostFinalize\t\t1000\n" "InstallValidate\t\t1400\n" "InstallInitialize\t\t1500\n" @@ -628,7 +663,8 @@ static const CHAR ca51_custom_action_dat[] = "Action\tType\tSource\tTarget\n" "s72\ti2\tS64\tS0\n" "CustomAction\tAction\n" "GoodSetProperty\t51\tMYPROP\t42\n" - "BadSetProperty\t51\t\tMYPROP\n"; + "BadSetProperty\t51\t\tMYPROP\n" + "SetSourceDir\t51\tSourceDir\t[SRCDIR]\n"; static const CHAR is_feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n" "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n" @@ -691,6 +727,60 @@ static const CHAR is_media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tV "Media\tDiskId\n" "1\t12\t\t\tDISK1\t\n"; +static const CHAR sp_component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n" + "s72\tS38\ts72\ti2\tS255\tS72\n" + "Component\tComponent\n" + "augustus\t\tTWODIR\t0\t\taugustus\n"; + +static const CHAR sp_directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n" + "s72\tS72\tl255\n" + "Directory\tDirectory\n" + "TARGETDIR\t\tSourceDir\n" + "ProgramFilesFolder\tTARGETDIR\t.\n" + "MSITESTDIR\tProgramFilesFolder\tmsitest:.\n" + "ONEDIR\tMSITESTDIR\t.:shortone|longone\n" + "TWODIR\tONEDIR\t.:shorttwo|longtwo"; + +static const CHAR mcp_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\t2\t\thydrogen\n" + "helium\t{5AD3C142-CEF8-490D-B569-784D80670685}\tMSITESTDIR\t2\t\thelium\n" + "lithium\t{4AF28FFC-71C7-4307-BDE4-B77C5338F56F}\tMSITESTDIR\t2\tPROPVAR=42\tlithium\n"; + +static const CHAR mcp_feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n" + "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n" + "Feature\tFeature\n" + "hydroxyl\t\thydroxyl\thydroxyl\t2\t1\tTARGETDIR\t0\n" + "heliox\t\theliox\theliox\t2\t5\tTARGETDIR\t0\n" + "lithia\t\tlithia\tlithia\t2\t10\tTARGETDIR\t0"; + +static const CHAR mcp_feature_comp_dat[] = "Feature_\tComponent_\n" + "s38\ts72\n" + "FeatureComponents\tFeature_\tComponent_\n" + "hydroxyl\thydrogen\n" + "heliox\thelium\n" + "lithia\tlithium"; + +static const CHAR mcomp_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\n" + "beryllium\tmissingcomp\tberyllium\t0\t\t\t8192\t1"; + +static const CHAR ai_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\t16384\t5\n" + "four.txt\tFour\tfour.txt\t1000\t\t\t16384\t4\n" + "one.txt\tOne\tone.txt\t1000\t\t\t16384\t1\n" + "three.txt\tThree\tthree.txt\t1000\t\t\t16384\t3\n" + "two.txt\tTwo\ttwo.txt\t1000\t\t\t16384\t2\n" + "file\tcomponent\tfilename\t100\t\t\t8192\t1\n" + "service_file\tservice_comp\tservice.exe\t100\t\t\t8192\t1"; + typedef struct _msi_table { const CHAR *filename; @@ -889,6 +979,18 @@ static const msi_table pp_tables[] = ADD_TABLE(property), }; +static const msi_table ppc_tables[] = +{ + ADD_TABLE(ppc_component), + ADD_TABLE(directory), + ADD_TABLE(rof_feature), + ADD_TABLE(ppc_feature_comp), + ADD_TABLE(ppc_file), + ADD_TABLE(pp_install_exec_seq), + ADD_TABLE(ppc_media), + ADD_TABLE(property), +}; + static const msi_table tp_tables[] = { ADD_TABLE(tp_component), @@ -1041,6 +1143,66 @@ static const msi_table is_tables[] = ADD_TABLE(property), }; +static const msi_table sp_tables[] = +{ + ADD_TABLE(sp_component), + ADD_TABLE(sp_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 mcp_tables[] = +{ + ADD_TABLE(mcp_component), + ADD_TABLE(directory), + ADD_TABLE(mcp_feature), + ADD_TABLE(mcp_feature_comp), + ADD_TABLE(rem_file), + ADD_TABLE(rem_install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), +}; + +static const msi_table mcomp_tables[] = +{ + ADD_TABLE(mcp_component), + ADD_TABLE(directory), + ADD_TABLE(mcp_feature), + ADD_TABLE(mcp_feature_comp), + ADD_TABLE(mcomp_file), + ADD_TABLE(rem_install_exec_seq), + ADD_TABLE(rof_media), + ADD_TABLE(property), +}; + +static const msi_table ai_tables[] = +{ + ADD_TABLE(component), + ADD_TABLE(directory), + ADD_TABLE(feature), + ADD_TABLE(feature_comp), + ADD_TABLE(ai_file), + ADD_TABLE(install_exec_seq), + ADD_TABLE(media), + ADD_TABLE(property) +}; + +static const msi_table pc_tables[] = +{ + ADD_TABLE(ca51_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) +}; + /* cabinet definitions */ /* make the max size large so there is only one cab file */ @@ -1155,18 +1317,35 @@ static void init_functionpointers(void) { HMODULE hmsi = GetModuleHandleA("msi.dll"); -#define GET_PROC(func) \ - p ## func = (void*)GetProcAddress(hmsi, #func); \ +#define GET_PROC(mod, func) \ + p ## func = (void*)GetProcAddress(mod, #func); \ if(!p ## func) \ trace("GetProcAddress(%s) failed\n", #func); - GET_PROC(MsiQueryComponentStateA); - GET_PROC(MsiSourceListEnumSourcesA); - GET_PROC(MsiSourceListGetInfoA); + GET_PROC(hmsi, MsiQueryComponentStateA); + GET_PROC(hmsi, MsiSourceListEnumSourcesA); + GET_PROC(hmsi, MsiSourceListGetInfoA); + + hsrclient = LoadLibraryA("srclient.dll"); + GET_PROC(hsrclient, SRRemoveRestorePoint); + GET_PROC(hsrclient, SRSetRestorePointA); #undef GET_PROC } +static BOOL check_win9x(void) +{ + SC_HANDLE scm; + + scm = OpenSCManager(NULL, NULL, GENERIC_ALL); + if (!scm && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)) + return TRUE; + + CloseServiceHandle(scm); + + return FALSE; +} + static void get_user_sid(LPSTR *usersid) { HANDLE token; @@ -1334,7 +1513,9 @@ static void create_file_data(LPCSTR name, LPCSTR data, DWORD size) 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); + if (file == INVALID_HANDLE_VALUE) + return; + WriteFile(file, data, strlen(data), &written, NULL); if (size) @@ -1421,7 +1602,7 @@ static void write_file(const CHAR *filename, const char *data, int data_size) CloseHandle(hf); } -static void write_msi_summary_info(MSIHANDLE db) +static void write_msi_summary_info(MSIHANDLE db, INT wordcount) { MSIHANDLE summary; UINT r; @@ -1439,7 +1620,7 @@ static void write_msi_summary_info(MSIHANDLE db) r = MsiSummaryInfoSetPropertyA(summary, PID_PAGECOUNT, VT_I4, 100, NULL, NULL); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - r = MsiSummaryInfoSetPropertyA(summary, PID_WORDCOUNT, VT_I4, 0, NULL, NULL); + r = MsiSummaryInfoSetPropertyA(summary, PID_WORDCOUNT, VT_I4, wordcount, NULL, NULL); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); r = MsiSummaryInfoSetPropertyA(summary, PID_TITLE, VT_LPSTR, 0, NULL, "MSITEST"); @@ -1452,7 +1633,11 @@ static void write_msi_summary_info(MSIHANDLE db) MsiCloseHandle(summary); } -static void create_database(const CHAR *name, const msi_table *tables, int num_tables) +#define create_database(name, tables, num_tables) \ + create_database_wordcount(name, tables, num_tables, 0); + +static void create_database_wordcount(const CHAR *name, const msi_table *tables, + int num_tables, INT wordcount) { MSIHANDLE db; UINT r; @@ -1474,7 +1659,7 @@ static void create_database(const CHAR *name, const msi_table *tables, int num_t DeleteFileA(table->filename); } - write_msi_summary_info(db); + write_msi_summary_info(db, wordcount); r = MsiDatabaseCommit(db); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); @@ -1500,6 +1685,27 @@ static void check_service_is_installed(void) CloseServiceHandle(scm); } +static BOOL notify_system_change(DWORD event_type, STATEMGRSTATUS *status) +{ + RESTOREPOINTINFOA spec; + + spec.dwEventType = event_type; + spec.dwRestorePtType = APPLICATION_INSTALL; + spec.llSequenceNumber = status->llSequenceNumber; + lstrcpyA(spec.szDescription, "msitest restore point"); + + return pSRSetRestorePointA(&spec, status); +} + +static void remove_restore_point(DWORD seq_number) +{ + DWORD res; + + res = pSRRemoveRestorePoint(seq_number); + if (res != ERROR_SUCCESS) + trace("Failed to remove the restore point : %08x\n", res); +} + static void test_MsiInstallProduct(void) { UINT r; @@ -1507,15 +1713,12 @@ static void test_MsiInstallProduct(void) LONG res; HKEY hkey; DWORD num, size, type; - SC_HANDLE scm; - scm = OpenSCManager(NULL, NULL, GENERIC_ALL); - if (!scm && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)) + if (on_win9x) { - skip("Services are not implemented, we are most likely on win9x\n"); + win_skip("Services are not implemented on Win9x and WinMe\n"); return; } - CloseServiceHandle(scm); create_test_files(); create_database(msifile, tables, sizeof(tables) / sizeof(msi_table)); @@ -1658,6 +1861,8 @@ static void test_packagecoltypes(void) MsiCloseHandle(rec); MsiCloseHandle(view); MsiCloseHandle(hdb); + CoUninitialize(); + DeleteFile(msifile); } @@ -1958,7 +2163,7 @@ static void test_readonlyfile(void) file = CreateFile(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_READONLY, NULL); - WriteFile(file, "readonlyfile", 20, &size, NULL); + WriteFile(file, "readonlyfile", strlen("readonlyfile"), &size, NULL); CloseHandle(file); r = MsiInstallProductA(msifile, NULL); @@ -2917,10 +3122,11 @@ static void test_publish_processcomponents(void) LPSTR usersid; CHAR val[MAX_PATH]; CHAR keypath[MAX_PATH]; + CHAR program_files_maximus[MAX_PATH]; static const CHAR keyfmt[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\" - "UserData\\%s\\Components\\CBABC2FDCCB35E749A8944D8C1C098B5"; + "UserData\\%s\\Components\\%s"; static const CHAR compkey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Components"; @@ -2934,7 +3140,7 @@ static void test_publish_processcomponents(void) CreateDirectoryA("msitest", NULL); create_file("msitest\\maximus", 500); - create_database(msifile, pp_tables, sizeof(pp_tables) / sizeof(msi_table)); + create_database(msifile, ppc_tables, sizeof(ppc_tables) / sizeof(msi_table)); MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL); @@ -2944,7 +3150,7 @@ static void test_publish_processcomponents(void) ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n"); ok(delete_pf("msitest", FALSE), "File not installed\n"); - sprintf(keypath, keyfmt, usersid); + sprintf(keypath, keyfmt, usersid, "CBABC2FDCCB35E749A8944D8C1C098B5"); res = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &comp); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); @@ -2953,8 +3159,31 @@ static void test_publish_processcomponents(void) res = RegQueryValueExA(comp, "84A88FD7F6998CE40A22FB59F6B9C2BB", NULL, NULL, (LPBYTE)val, &size); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); - ok(!lstrcmpA(val, "C:\\Program Files\\msitest\\maximus"), - "Expected \"%s\", got \"%s\"\n", "C:\\Program Files\\msitest\\maximus", val); + + lstrcpyA(program_files_maximus,PROG_FILES_DIR); + lstrcatA(program_files_maximus,"\\msitest\\maximus"); + + ok(!lstrcmpA(val, program_files_maximus), + "Expected \"%s\", got \"%s\"\n", program_files_maximus, val); + + res = RegOpenKeyA(HKEY_LOCAL_MACHINE, compkey, &hkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + RegDeleteValueA(comp, "84A88FD7F6998CE40A22FB59F6B9C2BB"); + RegDeleteKeyA(comp, ""); + RegCloseKey(comp); + + sprintf(keypath, keyfmt, usersid, "241C3DA58FECD0945B9687D408766058"); + + res = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &comp); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + size = MAX_PATH; + res = RegQueryValueExA(comp, "84A88FD7F6998CE40A22FB59F6B9C2BB", + NULL, NULL, (LPBYTE)val, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(val, "01\\msitest\\augustus"), + "Expected \"01\\msitest\\augustus\", got \"%s\"\n", val); res = RegOpenKeyA(HKEY_LOCAL_MACHINE, compkey, &hkey); ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); @@ -2969,7 +3198,7 @@ static void test_publish_processcomponents(void) ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n"); ok(delete_pf("msitest", FALSE), "File not installed\n"); - sprintf(keypath, keyfmt, "S-1-5-18"); + sprintf(keypath, keyfmt, "S-1-5-18", "CBABC2FDCCB35E749A8944D8C1C098B5"); res = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &comp); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); @@ -2978,8 +3207,27 @@ static void test_publish_processcomponents(void) res = RegQueryValueExA(comp, "84A88FD7F6998CE40A22FB59F6B9C2BB", NULL, NULL, (LPBYTE)val, &size); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); - ok(!lstrcmpA(val, "C:\\Program Files\\msitest\\maximus"), - "Expected \"%s\", got \"%s\"\n", "C:\\Program Files\\msitest\\maximus", val); + ok(!lstrcmpA(val, program_files_maximus), + "Expected \"%s\", got \"%s\"\n", program_files_maximus, val); + + res = RegOpenKeyA(HKEY_LOCAL_MACHINE, compkey, &hkey); + ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); + + RegDeleteValueA(comp, "84A88FD7F6998CE40A22FB59F6B9C2BB"); + RegDeleteKeyA(comp, ""); + RegCloseKey(comp); + + sprintf(keypath, keyfmt, "S-1-5-18", "241C3DA58FECD0945B9687D408766058"); + + res = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &comp); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + size = MAX_PATH; + res = RegQueryValueExA(comp, "84A88FD7F6998CE40A22FB59F6B9C2BB", + NULL, NULL, (LPBYTE)val, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!lstrcmpA(val, "01\\msitest\\augustus"), + "Expected \"01\\msitest\\augustus\", got \"%s\"\n", val); res = RegOpenKeyA(HKEY_LOCAL_MACHINE, compkey, &hkey); ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", res); @@ -3193,9 +3441,9 @@ static void test_publish(void) /* no UnpublishFeatures */ r = MsiInstallProductA(msifile, "REMOVE=ALL"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!pf_exists("msitest\\maximus"), "File deleted\n"); todo_wine { - ok(!pf_exists("msitest\\maximus"), "File deleted\n"); ok(!pf_exists("msitest"), "File deleted\n"); } @@ -3271,7 +3519,7 @@ static void test_publish(void) /* 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"); + todo_wine ok(pf_exists("msitest\\maximus"), "File deleted\n"); ok(pf_exists("msitest"), "File deleted\n"); state = MsiQueryProductState("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}"); @@ -3375,9 +3623,9 @@ static void test_publish(void) /* UnpublishFeatures, both features removed */ r = MsiInstallProductA(msifile, "UNPUBLISH_FEATURES=1 REMOVE=feature,montecristo"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!pf_exists("msitest\\maximus"), "File not deleted\n"); todo_wine { - ok(!pf_exists("msitest\\maximus"), "File not deleted\n"); ok(!pf_exists("msitest"), "File not deleted\n"); } @@ -3453,9 +3701,9 @@ static void test_publish(void) /* 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 not deleted\n"); todo_wine { - ok(!pf_exists("msitest\\maximus"), "File not deleted\n"); ok(!pf_exists("msitest"), "File not deleted\n"); } @@ -3654,9 +3902,9 @@ static void test_publishsourcelist(void) /* 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 not deleted\n"); todo_wine { - ok(!pf_exists("msitest\\maximus"), "File not deleted\n"); ok(!pf_exists("msitest"), "File not deleted\n"); } @@ -4004,7 +4252,8 @@ static void set_admin_property_stream(LPCSTR file) /* AdminProperties */ static const WCHAR stmname[] = {0x41ca,0x4330,0x3e71,0x44b5,0x4233,0x45f5,0x422c,0x4836,0}; - static const WCHAR data[] = {'M','Y','P','R','O','P','=','2','7','1','8',0}; + static const WCHAR data[] = {'M','Y','P','R','O','P','=','2','7','1','8',' ', + 'M','y','P','r','o','p','=','4','2',0}; MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, MAX_PATH); @@ -4084,13 +4333,10 @@ static void test_removefiles(void) r = MsiInstallProductA(msifile, "REMOVE=ALL"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(!pf_exists("msitest\\hydrogen"), "File not deleted\n"); ok(!pf_exists("msitest\\helium"), "File not deleted\n"); ok(delete_pf("msitest\\lithium", TRUE), "File deleted\n"); - todo_wine - { - ok(!pf_exists("msitest\\hydrogen"), "File not deleted\n"); - ok(delete_pf("msitest", FALSE), "File deleted\n"); - } + ok(delete_pf("msitest", FALSE), "File deleted\n"); create_pf("msitest", FALSE); create_pf("msitest\\hydrogen", TRUE); @@ -4106,13 +4352,10 @@ static void test_removefiles(void) r = MsiInstallProductA(msifile, "REMOVE=ALL"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(!pf_exists("msitest\\hydrogen"), "File not deleted\n"); ok(delete_pf("msitest\\helium", TRUE), "File deleted\n"); ok(delete_pf("msitest\\lithium", TRUE), "File deleted\n"); - todo_wine - { - ok(!pf_exists("msitest\\hydrogen"), "File not deleted\n"); - ok(delete_pf("msitest", FALSE), "File deleted\n"); - } + ok(delete_pf("msitest", FALSE), "File deleted\n"); create_pf("msitest", FALSE); create_pf("msitest\\furlong", TRUE); @@ -4124,25 +4367,25 @@ static void test_removefiles(void) create_pf("msitest\\storeys", TRUE); create_pf("msitest\\block", TRUE); create_pf("msitest\\siriometer", TRUE); + create_pf("msitest\\cabout", FALSE); + create_pf("msitest\\cabout\\blocker", TRUE); r = MsiInstallProductA(msifile, NULL); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); ok(pf_exists("msitest\\hydrogen"), "File not installed\n"); ok(!pf_exists("msitest\\helium"), "File installed\n"); ok(pf_exists("msitest\\lithium"), "File not installed\n"); + ok(!pf_exists("msitest\\furlong"), "File not deleted\n"); + ok(!pf_exists("msitest\\firkin"), "File not deleted\n"); + ok(!pf_exists("msitest\\fortnight"), "File not deleted\n"); ok(pf_exists("msitest\\becquerel"), "File not installed\n"); ok(pf_exists("msitest\\dioptre"), "File not installed\n"); ok(pf_exists("msitest\\attoparsec"), "File not installed\n"); + ok(!pf_exists("msitest\\storeys"), "File not deleted\n"); + ok(!pf_exists("msitest\\block"), "File not deleted\n"); + ok(!pf_exists("msitest\\siriometer"), "File not deleted\n"); + ok(pf_exists("msitest\\cabout"), "Directory removed\n"); ok(pf_exists("msitest"), "File not installed\n"); - todo_wine - { - ok(!pf_exists("msitest\\firkin"), "File not deleted\n"); - ok(!pf_exists("msitest\\fortnight"), "File not deleted\n"); - ok(!pf_exists("msitest\\furlong"), "File not deleted\n"); - ok(!pf_exists("msitest\\storeys"), "File not deleted\n"); - ok(!pf_exists("msitest\\block"), "File not deleted\n"); - ok(!pf_exists("msitest\\siriometer"), "File not deleted\n"); - } create_pf("msitest\\furlong", TRUE); create_pf("msitest\\firkin", TRUE); @@ -4153,22 +4396,35 @@ static void test_removefiles(void) r = MsiInstallProductA(msifile, "REMOVE=ALL"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(!delete_pf("msitest\\hydrogen", TRUE), "File not deleted\n"); ok(!delete_pf("msitest\\helium", TRUE), "File not deleted\n"); ok(delete_pf("msitest\\lithium", TRUE), "File deleted\n"); ok(delete_pf("msitest\\furlong", TRUE), "File deleted\n"); ok(delete_pf("msitest\\firkin", TRUE), "File deleted\n"); ok(delete_pf("msitest\\fortnight", TRUE), "File deleted\n"); + ok(!delete_pf("msitest\\becquerel", TRUE), "File not deleted\n"); + ok(!delete_pf("msitest\\dioptre", TRUE), "File not deleted\n"); ok(delete_pf("msitest\\attoparsec", TRUE), "File deleted\n"); + ok(!delete_pf("msitest\\storeys", TRUE), "File not deleted\n"); + ok(!delete_pf("msitest\\block", TRUE), "File not deleted\n"); ok(delete_pf("msitest\\siriometer", TRUE), "File deleted\n"); - todo_wine - { - ok(!delete_pf("msitest\\hydrogen", TRUE), "File not deleted\n"); - ok(!delete_pf("msitest\\becquerel", TRUE), "File not deleted\n"); - ok(!delete_pf("msitest\\dioptre", TRUE), "File not deleted\n"); - ok(!delete_pf("msitest\\storeys", TRUE), "File not deleted\n"); - ok(!delete_pf("msitest\\block", TRUE), "File not deleted\n"); - } - ok(delete_pf("msitest", FALSE), "File deleted\n"); + ok(pf_exists("msitest\\cabout"), "Directory deleted\n"); + ok(pf_exists("msitest"), "Directory deleted\n"); + + r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_pf("msitest\\hydrogen", TRUE), "File not installed\n"); + ok(!delete_pf("msitest\\helium", TRUE), "File installed\n"); + ok(delete_pf("msitest\\lithium", TRUE), "File not installed\n"); + ok(pf_exists("msitest\\cabout"), "Directory deleted\n"); + ok(pf_exists("msitest"), "Directory deleted\n"); + + delete_pf("msitest\\cabout\\blocker", TRUE); + + r = MsiInstallProductA(msifile, "REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(!delete_pf("msitest\\cabout", FALSE), "Directory not deleted\n"); + ok(delete_pf("msitest", FALSE), "Directory deleted\n"); DeleteFile(msifile); DeleteFile("msitest\\hydrogen"); @@ -4195,6 +4451,7 @@ static void test_movefiles(void) create_file("kenya", 100); CreateDirectoryA("latvia", NULL); create_file("nauru", 100); + create_file("peru", 100); create_file("apple", 100); create_file("application", 100); create_file("ape", 100); @@ -4235,6 +4492,7 @@ static void test_movefiles(void) ok(delete_pf("msitest\\kiribati", TRUE), "File not moved\n"); ok(!delete_pf("msitest\\lebanon", TRUE), "File moved\n"); ok(!delete_pf("msitest\\lebanon", FALSE), "Directory moved\n"); + ok(delete_pf("msitest\\poland", TRUE), "File not moved\n"); /* either apple or application will be moved depending on directory order */ if (!delete_pf("msitest\\apple", TRUE)) ok(delete_pf("msitest\\application", TRUE), "File not moved\n"); @@ -4267,6 +4525,7 @@ static void test_movefiles(void) ok(!DeleteFileA("kenya"), "File not moved\n"); ok(RemoveDirectoryA("latvia"), "Directory moved\n"); ok(!DeleteFileA("nauru"), "File not moved\n"); + ok(!DeleteFileA("peru"), "File not moved\n"); ok(!DeleteFileA("apple"), "File not moved\n"); ok(!DeleteFileA("application"), "File not moved\n"); ok(DeleteFileA("ape"), "File moved\n"); @@ -4411,22 +4670,22 @@ static void test_sourcefolder(void) MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_INSTALL_FAILURE, + "Expected ERROR_INSTALL_FAILURE, got %u\n", r); ok(!delete_pf("msitest\\augustus", TRUE), "File installed\n"); todo_wine { - ok(r == ERROR_INSTALL_FAILURE, - "Expected ERROR_INSTALL_FAILURE, got %u\n", r); ok(!delete_pf("msitest", FALSE), "File installed\n"); } RemoveDirectoryA("msitest"); r = MsiInstallProductA(msifile, NULL); + ok(r == ERROR_INSTALL_FAILURE, + "Expected ERROR_INSTALL_FAILURE, got %u\n", r); + ok(!delete_pf("msitest\\augustus", TRUE), "File installed\n"); todo_wine { - ok(r == ERROR_INSTALL_FAILURE, - "Expected ERROR_INSTALL_FAILURE, got %u\n", r); - ok(!delete_pf("msitest\\augustus", TRUE), "File installed\n"); ok(!delete_pf("msitest", FALSE), "File installed\n"); } @@ -4557,13 +4816,812 @@ static void test_installstate(void) RemoveDirectory("msitest"); } +struct sourcepathmap +{ + BOOL sost; /* shortone\shorttwo */ + BOOL solt; /* shortone\longtwo */ + BOOL lost; /* longone\shorttwo */ + BOOL lolt; /* longone\longtwo */ + BOOL soste; /* shortone\shorttwo source exists */ + BOOL solte; /* shortone\longtwo source exists */ + BOOL loste; /* longone\shorttwo source exists */ + BOOL lolte; /* longone\longtwo source exists */ + UINT err; + DWORD size; +} spmap[256] = +{ + {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, ERROR_SUCCESS, 200}, + {FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, ERROR_INSTALL_FAILURE, 0}, + {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, ERROR_INSTALL_FAILURE, 0}, +}; + +static DWORD get_pf_file_size(LPCSTR file) +{ + CHAR path[MAX_PATH]; + HANDLE hfile; + DWORD size; + + lstrcpyA(path, PROG_FILES_DIR); + lstrcatA(path, "\\"); + lstrcatA(path, file); + + hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hfile == INVALID_HANDLE_VALUE) + return 0; + + size = GetFileSize(hfile, NULL); + CloseHandle(hfile); + return size; +} + +static void test_sourcepath(void) +{ + UINT r, i; + + if (!winetest_interactive) + { + skip("Run in interactive mode to run source path tests.\n"); + return; + } + + create_database(msifile, sp_tables, sizeof(sp_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + for (i = 0; i < sizeof(spmap) / sizeof(spmap[0]); i++) + { + if (spmap[i].sost) + { + CreateDirectoryA("shortone", NULL); + CreateDirectoryA("shortone\\shorttwo", NULL); + } + + if (spmap[i].solt) + { + CreateDirectoryA("shortone", NULL); + CreateDirectoryA("shortone\\longtwo", NULL); + } + + if (spmap[i].lost) + { + CreateDirectoryA("longone", NULL); + CreateDirectoryA("longone\\shorttwo", NULL); + } + + if (spmap[i].lolt) + { + CreateDirectoryA("longone", NULL); + CreateDirectoryA("longone\\longtwo", NULL); + } + + if (spmap[i].soste) + create_file("shortone\\shorttwo\\augustus", 50); + if (spmap[i].solte) + create_file("shortone\\longtwo\\augustus", 100); + if (spmap[i].loste) + create_file("longone\\shorttwo\\augustus", 150); + if (spmap[i].lolte) + create_file("longone\\longtwo\\augustus", 200); + + r = MsiInstallProductA(msifile, NULL); + ok(r == spmap[i].err, "%d: Expected %d, got %d\n", i, spmap[i].err, r); + ok(get_pf_file_size("msitest\\augustus") == spmap[i].size, + "%d: Expected %d, got %d\n", i, spmap[i].size, + get_pf_file_size("msitest\\augustus")); + + if (r == ERROR_SUCCESS) + { + ok(delete_pf("msitest\\augustus", TRUE), "%d: File not installed\n", i); + ok(delete_pf("msitest", FALSE), "%d: File not installed\n", i); + } + else + { + ok(!delete_pf("msitest\\augustus", TRUE), "%d: File installed\n", i); + todo_wine ok(!delete_pf("msitest", FALSE), "%d: File installed\n", i); + } + + DeleteFileA("shortone\\shorttwo\\augustus"); + DeleteFileA("shortone\\longtwo\\augustus"); + DeleteFileA("longone\\shorttwo\\augustus"); + DeleteFileA("longone\\longtwo\\augustus"); + RemoveDirectoryA("shortone\\shorttwo"); + RemoveDirectoryA("shortone\\longtwo"); + RemoveDirectoryA("longone\\shorttwo"); + RemoveDirectoryA("longone\\longtwo"); + RemoveDirectoryA("shortone"); + RemoveDirectoryA("longone"); + } + + DeleteFileA(msifile); +} + +static void test_MsiConfigureProductEx(void) +{ + UINT r; + LONG res; + DWORD type, size; + HKEY props, source; + CHAR keypath[MAX_PATH * 2]; + CHAR localpack[MAX_PATH]; + + if (on_win9x) + { + win_skip("Different registry keys on Win9x and WinMe\n"); + return; + } + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\hydrogen", 500); + create_file("msitest\\helium", 500); + create_file("msitest\\lithium", 500); + + create_database(msifile, mcp_tables, sizeof(mcp_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + /* NULL szProduct */ + r = MsiConfigureProductExA(NULL, INSTALLLEVEL_DEFAULT, + INSTALLSTATE_DEFAULT, "PROPVAR=42"); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* empty szProduct */ + r = MsiConfigureProductExA("", INSTALLLEVEL_DEFAULT, + INSTALLSTATE_DEFAULT, "PROPVAR=42"); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* garbage szProduct */ + r = MsiConfigureProductExA("garbage", INSTALLLEVEL_DEFAULT, + INSTALLSTATE_DEFAULT, "PROPVAR=42"); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* guid without brackets */ + r = MsiConfigureProductExA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, + "PROPVAR=42"); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + + /* guid with brackets */ + r = MsiConfigureProductExA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, + "PROPVAR=42"); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + /* same length as guid, but random */ + r = MsiConfigureProductExA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, + "PROPVAR=42"); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + /* product not installed yet */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, + "PROPVAR=42"); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + + /* install the product, per-user unmanaged */ + r = MsiInstallProductA(msifile, "INSTALLLEVEL=10 PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\hydrogen"), "File not installed\n"); + ok(pf_exists("msitest\\helium"), "File not installed\n"); + ok(pf_exists("msitest\\lithium"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + /* product is installed per-user managed, remove it */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, + "PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!delete_pf("msitest\\hydrogen", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\helium", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\lithium", TRUE), "File not removed\n"); + todo_wine + { + ok(!delete_pf("msitest", FALSE), "File not removed\n"); + } + + /* product has been removed */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, + "PROPVAR=42"); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %u\n", r); + + /* install the product, machine */ + r = MsiInstallProductA(msifile, "ALLUSERS=1 INSTALLLEVEL=10 PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\hydrogen"), "File not installed\n"); + ok(pf_exists("msitest\\helium"), "File not installed\n"); + ok(pf_exists("msitest\\lithium"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + /* product is installed machine, remove it */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, + "PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!delete_pf("msitest\\hydrogen", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\helium", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\lithium", TRUE), "File not removed\n"); + todo_wine + { + ok(!delete_pf("msitest", FALSE), "File not removed\n"); + } + + /* product has been removed */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, + "PROPVAR=42"); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %u\n", r); + + /* install the product, machine */ + r = MsiInstallProductA(msifile, "ALLUSERS=1 INSTALLLEVEL=10 PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\hydrogen"), "File not installed\n"); + ok(pf_exists("msitest\\helium"), "File not installed\n"); + ok(pf_exists("msitest\\lithium"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + DeleteFileA(msifile); + + /* local msifile is removed */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, + "PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!delete_pf("msitest\\hydrogen", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\helium", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\lithium", TRUE), "File not removed\n"); + todo_wine + { + ok(!delete_pf("msitest", FALSE), "File not removed\n"); + } + + create_database(msifile, mcp_tables, sizeof(mcp_tables) / sizeof(msi_table)); + + /* install the product, machine */ + r = MsiInstallProductA(msifile, "ALLUSERS=1 INSTALLLEVEL=10 PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\hydrogen"), "File not installed\n"); + ok(pf_exists("msitest\\helium"), "File not installed\n"); + ok(pf_exists("msitest\\lithium"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + DeleteFileA(msifile); + + lstrcpyA(keypath, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, "84A88FD7F6998CE40A22FB59F6B9C2BB\\InstallProperties"); + + res = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(props, "LocalPackage", 0, REG_SZ, + (const BYTE *)"C:\\idontexist.msi", 18); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* LocalPackage is used to find the cached msi package */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, + "PROPVAR=42"); + ok(r == ERROR_INSTALL_SOURCE_ABSENT, + "Expected ERROR_INSTALL_SOURCE_ABSENT, got %d\n", r); + ok(pf_exists("msitest\\hydrogen"), "File not installed\n"); + ok(pf_exists("msitest\\helium"), "File not installed\n"); + ok(pf_exists("msitest\\lithium"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + RegCloseKey(props); + create_database(msifile, mcp_tables, sizeof(mcp_tables) / sizeof(msi_table)); + + /* LastUsedSource (local msi package) can be used as a last resort */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, + "PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!delete_pf("msitest\\hydrogen", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\helium", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\lithium", TRUE), "File not removed\n"); + todo_wine + { + ok(!delete_pf("msitest", FALSE), "File not removed\n"); + } + + /* install the product, machine */ + r = MsiInstallProductA(msifile, "ALLUSERS=1 INSTALLLEVEL=10 PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\hydrogen"), "File not installed\n"); + ok(pf_exists("msitest\\helium"), "File not installed\n"); + ok(pf_exists("msitest\\lithium"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + lstrcpyA(keypath, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, "84A88FD7F6998CE40A22FB59F6B9C2BB\\InstallProperties"); + + res = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(props, "LocalPackage", 0, REG_SZ, + (const BYTE *)"C:\\idontexist.msi", 18); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + lstrcpyA(keypath, "SOFTWARE\\Classes\\Installer\\Products\\"); + lstrcatA(keypath, "84A88FD7F6998CE40A22FB59F6B9C2BB\\SourceList"); + + res = RegOpenKeyA(HKEY_LOCAL_MACHINE, keypath, &source); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + type = REG_SZ; + size = MAX_PATH; + res = RegQueryValueExA(source, "PackageName", NULL, &type, + (LPBYTE)localpack, &size); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(source, "PackageName", 0, REG_SZ, + (const BYTE *)"idontexist.msi", 15); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* SourceList is altered */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, + "PROPVAR=42"); + ok(r == ERROR_INSTALL_SOURCE_ABSENT, + "Expected ERROR_INSTALL_SOURCE_ABSENT, got %d\n", r); + ok(pf_exists("msitest\\hydrogen"), "File not installed\n"); + ok(pf_exists("msitest\\helium"), "File not installed\n"); + ok(pf_exists("msitest\\lithium"), "File not installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + /* restore the SourceList */ + res = RegSetValueExA(source, "PackageName", 0, REG_SZ, + (const BYTE *)localpack, lstrlenA(localpack) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* finally remove the product */ + r = MsiConfigureProductExA("{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}", + INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, + "PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!delete_pf("msitest\\hydrogen", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\helium", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\lithium", TRUE), "File not removed\n"); + todo_wine + { + ok(!delete_pf("msitest", FALSE), "File not removed\n"); + } + + DeleteFileA(msifile); + RegCloseKey(source); + RegCloseKey(props); + DeleteFileA("msitest\\hydrogen"); + DeleteFileA("msitest\\helium"); + DeleteFileA("msitest\\lithium"); + RemoveDirectoryA("msitest"); +} + +static void test_missingcomponent(void) +{ + UINT r; + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\hydrogen", 500); + create_file("msitest\\helium", 500); + create_file("msitest\\lithium", 500); + create_file("beryllium", 500); + + create_database(msifile, mcomp_tables, sizeof(mcomp_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + r = MsiInstallProductA(msifile, "INSTALLLEVEL=10 PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(pf_exists("msitest\\hydrogen"), "File not installed\n"); + ok(pf_exists("msitest\\helium"), "File not installed\n"); + ok(pf_exists("msitest\\lithium"), "File not installed\n"); + ok(!pf_exists("msitest\\beryllium"), "File installed\n"); + ok(pf_exists("msitest"), "File not installed\n"); + + r = MsiInstallProductA(msifile, "REMOVE=ALL INSTALLLEVEL=10 PROPVAR=42"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(!delete_pf("msitest\\hydrogen", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\helium", TRUE), "File not removed\n"); + ok(!delete_pf("msitest\\lithium", TRUE), "File not removed\n"); + ok(!pf_exists("msitest\\beryllium"), "File installed\n"); + todo_wine + { + ok(!delete_pf("msitest", FALSE), "File not removed\n"); + } + + DeleteFileA(msifile); + DeleteFileA("msitest\\hydrogen"); + DeleteFileA("msitest\\helium"); + DeleteFileA("msitest\\lithium"); + DeleteFileA("beryllium"); + RemoveDirectoryA("msitest"); +} + +static void test_sourcedirprop(void) +{ + UINT r; + CHAR props[MAX_PATH]; + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\augustus", 500); + + create_database(msifile, ca51_tables, sizeof(ca51_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\\augustus", TRUE), "File installed\n"); + ok(delete_pf("msitest", FALSE), "File installed\n"); + + DeleteFile("msitest\\augustus"); + RemoveDirectory("msitest"); + + CreateDirectoryA("altsource", NULL); + CreateDirectoryA("altsource\\msitest", NULL); + create_file("altsource\\msitest\\augustus", 500); + + sprintf(props, "SRCDIR=%s\\altsource\\", CURR_DIR); + + r = MsiInstallProductA(msifile, props); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(delete_pf("msitest\\augustus", TRUE), "File installed\n"); + ok(delete_pf("msitest", FALSE), "File installed\n"); + + DeleteFile(msifile); + DeleteFile("altsource\\msitest\\augustus"); + RemoveDirectory("altsource\\msitest"); + RemoveDirectory("altsource"); +} + +static void test_adminimage(void) +{ + UINT r; + + CreateDirectoryA("msitest", NULL); + CreateDirectoryA("msitest\\first", NULL); + CreateDirectoryA("msitest\\second", NULL); + CreateDirectoryA("msitest\\cabout", NULL); + CreateDirectoryA("msitest\\cabout\\new", NULL); + create_file("msitest\\one.txt", 100); + create_file("msitest\\first\\two.txt", 100); + create_file("msitest\\second\\three.txt", 100); + create_file("msitest\\cabout\\four.txt", 100); + create_file("msitest\\cabout\\new\\five.txt", 100); + create_file("msitest\\filename", 100); + create_file("msitest\\service.exe", 100); + + create_database_wordcount(msifile, ai_tables, + sizeof(ai_tables) / sizeof(msi_table), + msidbSumInfoSourceTypeAdminImage); + + 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"); + + DeleteFileA("msitest.msi"); + 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 test_propcase(void) +{ + UINT r; + + CreateDirectoryA("msitest", NULL); + create_file("msitest\\augustus", 500); + + create_database(msifile, pc_tables, sizeof(pc_tables) / sizeof(msi_table)); + + MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); + + r = MsiInstallProductA(msifile, "MyProp=42"); + 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", FALSE), "File not installed\n"); + + DeleteFile(msifile); + DeleteFile("msitest\\augustus"); + RemoveDirectory("msitest"); +} + START_TEST(install) { DWORD len; char temp_path[MAX_PATH], prev_path[MAX_PATH]; + STATEMGRSTATUS status; + BOOL ret = FALSE; init_functionpointers(); + on_win9x = check_win9x(); + GetCurrentDirectoryA(MAX_PATH, prev_path); GetTempPath(MAX_PATH, temp_path); SetCurrentDirectoryA(temp_path); @@ -4576,6 +5634,20 @@ START_TEST(install) get_program_files_dir(PROG_FILES_DIR, COMMON_FILES_DIR); + /* Create a restore point ourselves so we circumvent the multitude of restore points + * that would have been created by all the installation and removal tests. + */ + if (pSRSetRestorePointA) + { + memset(&status, 0, sizeof(status)); + ret = notify_system_change(BEGIN_NESTED_SYSTEM_CHANGE, &status); + } + + /* Create only one log file and don't append. We have to pass something + * for the log mode for this to work. + */ + MsiEnableLogA(INSTALLLOGMODE_FATALEXIT, "msitest.log", 0); + test_MsiInstallProduct(); test_MsiSetComponentState(); test_packagecoltypes(); @@ -4608,6 +5680,22 @@ START_TEST(install) test_sourcefolder(); test_customaction51(); test_installstate(); + test_sourcepath(); + test_MsiConfigureProductEx(); + test_missingcomponent(); + test_sourcedirprop(); + test_adminimage(); + test_propcase(); + + DeleteFileA("msitest.log"); + + if (pSRSetRestorePointA && ret) + { + ret = notify_system_change(END_NESTED_SYSTEM_CHANGE, &status); + if (ret) + remove_restore_point(status.llSequenceNumber); + } + FreeLibrary(hsrclient); SetCurrentDirectoryA(prev_path); } diff --git a/rostests/winetests/msi/msi.c b/rostests/winetests/msi/msi.c index 143c2bdeefe..3396f5c5bd4 100644 --- a/rostests/winetests/msi/msi.c +++ b/rostests/winetests/msi/msi.c @@ -24,10 +24,13 @@ #include #include #include +#include #include #include "wine/test.h" +static const char msifile[] = "winetest.msi"; + static BOOL (WINAPI *pConvertSidToStringSidA)(PSID, LPSTR*); static INSTALLSTATE (WINAPI *pMsiGetComponentPathA) @@ -40,10 +43,15 @@ static UINT (WINAPI *pMsiOpenPackageExA) (LPCSTR, DWORD, MSIHANDLE*); static UINT (WINAPI *pMsiOpenPackageExW) (LPCWSTR, DWORD, MSIHANDLE*); +static UINT (WINAPI *pMsiEnumPatchesExA) + (LPCSTR, LPCSTR, DWORD, DWORD, DWORD, LPSTR, LPSTR, + MSIINSTALLCONTEXT*, LPSTR, LPDWORD); static UINT (WINAPI *pMsiQueryComponentStateA) (LPCSTR, LPCSTR, MSIINSTALLCONTEXT, LPCSTR, INSTALLSTATE*); static INSTALLSTATE (WINAPI *pMsiUseFeatureExA) - (LPCSTR, LPCSTR ,DWORD, DWORD ); + (LPCSTR, LPCSTR ,DWORD, DWORD); +static UINT (WINAPI *pMsiGetPatchInfoExA) + (LPCSTR, LPCSTR, LPCSTR, MSIINSTALLCONTEXT, LPCSTR, LPSTR, DWORD *); static void init_functionpointers(void) { @@ -60,14 +68,122 @@ static void init_functionpointers(void) GET_PROC(hmsi, MsiGetProductInfoExA) GET_PROC(hmsi, MsiOpenPackageExA) GET_PROC(hmsi, MsiOpenPackageExW) + GET_PROC(hmsi, MsiEnumPatchesExA) GET_PROC(hmsi, MsiQueryComponentStateA) GET_PROC(hmsi, MsiUseFeatureExA) + GET_PROC(hmsi, MsiGetPatchInfoExA) GET_PROC(hadvapi32, ConvertSidToStringSidA) #undef GET_PROC } +static UINT run_query(MSIHANDLE hdb, const char *query) +{ + MSIHANDLE hview = 0; + UINT r; + + r = MsiDatabaseOpenView(hdb, query, &hview); + if (r != ERROR_SUCCESS) + return r; + + r = MsiViewExecute(hview, 0); + if (r == ERROR_SUCCESS) + r = MsiViewClose(hview); + MsiCloseHandle(hview); + return r; +} + +static UINT set_summary_info(MSIHANDLE hdb, LPSTR prodcode) +{ + UINT res; + MSIHANDLE suminfo; + + /* build summary info */ + res = MsiGetSummaryInformation(hdb, NULL, 7, &suminfo); + ok(res == ERROR_SUCCESS, "Failed to open summaryinfo\n"); + + res = MsiSummaryInfoSetProperty(suminfo, 2, VT_LPSTR, 0, NULL, + "Installation Database"); + ok(res == ERROR_SUCCESS, "Failed to set summary info\n"); + + res = MsiSummaryInfoSetProperty(suminfo, 3, VT_LPSTR, 0, NULL, + "Installation Database"); + ok(res == ERROR_SUCCESS, "Failed to set summary info\n"); + + res = MsiSummaryInfoSetProperty(suminfo, 4, VT_LPSTR, 0, NULL, + "Wine Hackers"); + ok(res == ERROR_SUCCESS, "Failed to set summary info\n"); + + res = MsiSummaryInfoSetProperty(suminfo, 7, VT_LPSTR, 0, NULL, + ";1033"); + ok(res == ERROR_SUCCESS, "Failed to set summary info\n"); + + res = MsiSummaryInfoSetProperty(suminfo, PID_REVNUMBER, VT_LPSTR, 0, NULL, + "{A2078D65-94D6-4205-8DEE-F68D6FD622AA}"); + ok(res == ERROR_SUCCESS, "Failed to set summary info\n"); + + res = MsiSummaryInfoSetProperty(suminfo, 14, VT_I4, 100, NULL, NULL); + ok(res == ERROR_SUCCESS, "Failed to set summary info\n"); + + res = MsiSummaryInfoSetProperty(suminfo, 15, VT_I4, 0, NULL, NULL); + ok(res == ERROR_SUCCESS, "Failed to set summary info\n"); + + res = MsiSummaryInfoPersist(suminfo); + ok(res == ERROR_SUCCESS, "Failed to make summary info persist\n"); + + res = MsiCloseHandle(suminfo); + ok(res == ERROR_SUCCESS, "Failed to close suminfo\n"); + + return res; +} + +static MSIHANDLE create_package_db(LPSTR prodcode) +{ + MSIHANDLE hdb = 0; + CHAR query[MAX_PATH]; + UINT res; + + DeleteFile(msifile); + + /* create an empty database */ + res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok( res == ERROR_SUCCESS , "Failed to create database\n" ); + if (res != ERROR_SUCCESS) + return hdb; + + res = MsiDatabaseCommit(hdb); + ok(res == ERROR_SUCCESS, "Failed to commit database\n"); + + set_summary_info(hdb, prodcode); + + res = run_query(hdb, + "CREATE TABLE `Directory` ( " + "`Directory` CHAR(255) NOT NULL, " + "`Directory_Parent` CHAR(255), " + "`DefaultDir` CHAR(255) NOT NULL " + "PRIMARY KEY `Directory`)"); + ok(res == ERROR_SUCCESS , "Failed to create directory table\n"); + + res = run_query(hdb, + "CREATE TABLE `Property` ( " + "`Property` CHAR(72) NOT NULL, " + "`Value` CHAR(255) " + "PRIMARY KEY `Property`)"); + ok(res == ERROR_SUCCESS , "Failed to create directory table\n"); + + sprintf(query, "INSERT INTO `Property` " + "(`Property`, `Value`) " + "VALUES( 'ProductCode', '%s' )", prodcode); + res = run_query(hdb, query); + ok(res == ERROR_SUCCESS , "Failed\n"); + + res = MsiDatabaseCommit(hdb); + ok(res == ERROR_SUCCESS, "Failed to commit database\n"); + + return hdb; +} + static void test_usefeature(void) { INSTALLSTATE r; @@ -326,6 +442,8 @@ static void test_MsiGetFileHash(void) for (i = 0; i < sizeof(hash_data) / sizeof(hash_data[0]); i++) { + int ret; + create_file(name, hash_data[i].data, hash_data[i].size); memset(&hash, 0, sizeof(MSIFILEHASHINFO)); @@ -333,7 +451,11 @@ static void test_MsiGetFileHash(void) 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"); + + ret = memcmp(&hash, &hash_data[i].hash, HASHSIZE); + ok(ret == 0 || + broken(ret != 0), /* win95 */ + "Hash incorrect\n"); DeleteFile(name); } @@ -811,6 +933,35 @@ static void test_MsiQueryFeatureState(void) res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"apple", 1); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + /* INSTALLSTATE_LOCAL */ + 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 *)"01\\", 4); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* INSTALLSTATE_SOURCE */ + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"01", 3); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* bad INSTALLSTATE_SOURCE */ + 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 *)"01a", 4); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* INSTALLSTATE_SOURCE */ + state = MsiQueryFeatureStateA(prodcode, "feature"); + ok(state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"01", 3); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* bad INSTALLSTATE_SOURCE */ state = MsiQueryFeatureStateA(prodcode, "feature"); ok(state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); @@ -1128,6 +1279,43 @@ static void test_MsiQueryComponentState(void) res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"hi", 2); ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + /* INSTALLSTATE_LOCAL */ + 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); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"01\\", 4); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* INSTALLSTATE_SOURCE */ + 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_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"01", 3); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* bad INSTALLSTATE_SOURCE */ + 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); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"01a", 4); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* INSTALLSTATE_SOURCE */ + 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_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + res = RegSetValueExA(compkey, prod_squashed, 0, REG_SZ, (const BYTE *)"01", 3); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* bad INSTALLSTATE_SOURCE */ state = MAGIC_ERROR; r = pMsiQueryComponentStateA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, component, &state); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); @@ -6901,9 +7089,3634 @@ static void test_MsiGetUserInfo(void) RegCloseKey(prodkey); } +static void test_MsiOpenProduct(void) +{ + MSIHANDLE hprod, hdb; + CHAR val[MAX_PATH]; + CHAR path[MAX_PATH]; + CHAR keypath[MAX_PATH*2]; + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + HKEY prodkey, userkey, props; + LPSTR usersid; + DWORD size; + LONG res; + UINT r; + + GetCurrentDirectoryA(MAX_PATH, path); + lstrcatA(path, "\\"); + + create_test_guid(prodcode, prod_squashed); + get_user_sid(&usersid); + + hdb = create_package_db(prodcode); + MsiCloseHandle(hdb); + + /* NULL szProduct */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(NULL, &hprod); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + /* empty szProduct */ + hprod = 0xdeadbeef; + r = MsiOpenProductA("", &hprod); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + /* garbage szProduct */ + hprod = 0xdeadbeef; + r = MsiOpenProductA("garbage", &hprod); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + /* guid without brackets */ + hprod = 0xdeadbeef; + r = MsiOpenProductA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D", &hprod); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + /* guid with brackets */ + hprod = 0xdeadbeef; + r = MsiOpenProductA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}", &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + /* same length as guid, but random */ + hprod = 0xdeadbeef; + r = MsiOpenProductA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93", &hprod); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + /* hProduct is NULL */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, NULL); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + /* MSIINSTALLCONTEXT_USERMANAGED */ + + 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); + + /* managed product key exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user product key exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + res = RegCreateKeyA(userkey, "InstallProperties", &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* InstallProperties key exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + lstrcpyA(val, path); + lstrcatA(val, "\\winetest.msi"); + res = RegSetValueExA(props, "ManagedLocalPackage", 0, REG_SZ, + (const BYTE *)val, lstrlenA(val) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* ManagedLocalPackage value exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(hprod != 0 && hprod != 0xdeadbeef, "Expected a valid product handle\n"); + + size = MAX_PATH; + r = MsiGetPropertyA(hprod, "ProductCode", val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, prodcode), "Expected \"%s\", got \"%s\"\n", prodcode, val); + ok(size == lstrlenA(prodcode), "Expected %d, got %d\n", lstrlenA(prodcode), size); + + MsiCloseHandle(hprod); + + RegDeleteValueA(props, "ManagedLocalPackage"); + RegDeleteKeyA(props, ""); + RegCloseKey(props); + RegDeleteKeyA(userkey, ""); + RegCloseKey(userkey); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* MSIINSTALLCONTEXT_USERUNMANAGED */ + + 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); + + /* unmanaged product key exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user product key exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + res = RegCreateKeyA(userkey, "InstallProperties", &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* InstallProperties key exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + lstrcpyA(val, path); + lstrcatA(val, "\\winetest.msi"); + res = RegSetValueExA(props, "LocalPackage", 0, REG_SZ, + (const BYTE *)val, lstrlenA(val) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* LocalPackage value exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(hprod != 0 && hprod != 0xdeadbeef, "Expected a valid product handle\n"); + + size = MAX_PATH; + r = MsiGetPropertyA(hprod, "ProductCode", val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, prodcode), "Expected \"%s\", got \"%s\"\n", prodcode, val); + ok(size == lstrlenA(prodcode), "Expected %d, got %d\n", lstrlenA(prodcode), size); + + MsiCloseHandle(hprod); + + RegDeleteValueA(props, "LocalPackage"); + RegDeleteKeyA(props, ""); + RegCloseKey(props); + RegDeleteKeyA(userkey, ""); + RegCloseKey(userkey); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* MSIINSTALLCONTEXT_MACHINE */ + + 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); + + /* managed product key exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* user product key exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + res = RegCreateKeyA(userkey, "InstallProperties", &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* InstallProperties key exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + lstrcpyA(val, path); + lstrcatA(val, "\\winetest.msi"); + res = RegSetValueExA(props, "LocalPackage", 0, REG_SZ, + (const BYTE *)val, lstrlenA(val) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* LocalPackage value exists */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(hprod != 0 && hprod != 0xdeadbeef, "Expected a valid product handle\n"); + + size = MAX_PATH; + r = MsiGetPropertyA(hprod, "ProductCode", val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, prodcode), "Expected \"%s\", got \"%s\"\n", prodcode, val); + ok(size == lstrlenA(prodcode), "Expected %d, got %d\n", lstrlenA(prodcode), size); + + MsiCloseHandle(hprod); + + res = RegSetValueExA(props, "LocalPackage", 0, REG_SZ, + (const BYTE *)"winetest.msi", 13); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* LocalPackage has just the package name */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_INSTALL_PACKAGE_OPEN_FAILED, + "Expected ERROR_INSTALL_PACKAGE_OPEN_FAILED, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + lstrcpyA(val, path); + lstrcatA(val, "\\winetest.msi"); + res = RegSetValueExA(props, "LocalPackage", 0, REG_SZ, + (const BYTE *)val, lstrlenA(val) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + DeleteFileA(msifile); + + /* local package does not exist */ + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(hprod == 0xdeadbeef, "Expected hprod to be unchanged\n"); + + RegDeleteValueA(props, "LocalPackage"); + RegDeleteKeyA(props, ""); + RegCloseKey(props); + RegDeleteKeyA(userkey, ""); + RegCloseKey(userkey); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + DeleteFileA(msifile); +} + +static void test_MsiEnumPatchesEx(void) +{ + CHAR keypath[MAX_PATH], patch[MAX_PATH]; + CHAR patch_squashed[MAX_PATH], patchcode[MAX_PATH]; + CHAR targetsid[MAX_PATH], targetprod[MAX_PATH]; + CHAR prodcode[MAX_PATH], prod_squashed[MAX_PATH]; + HKEY prodkey, patches, udprod, udpatch; + HKEY userkey, hpatch; + MSIINSTALLCONTEXT context; + DWORD size, data; + LPSTR usersid; + LONG res; + UINT r; + + if (!pMsiEnumPatchesExA) + { + win_skip("MsiEnumPatchesExA not implemented\n"); + return; + } + + create_test_guid(prodcode, prod_squashed); + create_test_guid(patch, patch_squashed); + get_user_sid(&usersid); + + /* empty szProductCode */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA("", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_ALL, 0, patchcode, targetprod, &context, + targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* garbage szProductCode */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA("garbage", usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_ALL, 0, patchcode, targetprod, &context, + targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* guid without brackets */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D", usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, MSIPATCHSTATE_ALL, + 0, patchcode, targetprod, &context, + targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* guid with brackets */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA("{6700E8CF-95AB-4D9C-BC2C-15840DDA7A5D}", usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, MSIPATCHSTATE_ALL, + 0, patchcode, targetprod, &context, + targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, + "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* szUserSid is S-1-5-18 */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, "S-1-5-18", + MSIINSTALLCONTEXT_USERUNMANAGED, MSIPATCHSTATE_ALL, + 0, patchcode, targetprod, &context, + targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* dwContext is MSIINSTALLCONTEXT_MACHINE, but szUserSid is non-NULL */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_ALL, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* dwContext is out of bounds */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, 0, + MSIPATCHSTATE_ALL, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* dwContext is out of bounds */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_ALL + 1, + MSIPATCHSTATE_ALL, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* dwFilter is out of bounds */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_INVALID, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* dwFilter is out of bounds */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_ALL + 1, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* pcchTargetUserSid is NULL while szTargetUserSid is non-NULL */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_ALL, 0, patchcode, targetprod, + &context, targetsid, NULL); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + + /* MSIINSTALLCONTEXT_USERMANAGED */ + + /* MSIPATCHSTATE_APPLIED */ + + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + 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); + + /* managed product key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(prodkey, "Patches", &patches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* patches key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not REG_MULTI_SZ */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)"a\0b\0c\0\0", 7); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not a squashed guid */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, patch_squashed, 0, REG_SZ, + (const BYTE *)"whatever", 9); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* patch squashed value exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* increase the index */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 1, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* increase again */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 2, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* szPatchCode is NULL */ + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, NULL, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* szTargetProductCode is NULL */ + lstrcpyA(patchcode, "apple"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, NULL, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* pdwTargetProductContext is NULL */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + NULL, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* szTargetUserSid is NULL */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, NULL, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + ok(size == lstrlenA(usersid) * sizeof(WCHAR), + "Got %d\n", size); + + /* pcchTargetUserSid is exactly the length of szTargetUserSid */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = lstrlenA(usersid); + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + ok(!strncmp(targetsid, usersid, lstrlenA(usersid) - 1), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid) * sizeof(WCHAR), + "Got %d\n", size); + + /* pcchTargetUserSid has enough room for NULL terminator */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = lstrlenA(usersid) + 1; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* both szTargetuserSid and pcchTargetUserSid are NULL */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + + /* MSIPATCHSTATE_SUPERSEDED */ + + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udprod); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* UserData product key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udprod, "Patches", &udpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* UserData patches key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udpatch, patch_squashed, &hpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* specific UserData patch key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + data = MSIPATCHSTATE_SUPERSEDED; + res = RegSetValueExA(hpatch, "State", 0, REG_DWORD, + (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* MSIPATCHSTATE_OBSOLETED */ + + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_OBSOLETED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + data = MSIPATCHSTATE_OBSOLETED; + res = RegSetValueExA(hpatch, "State", 0, REG_DWORD, + (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value is obsoleted */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_OBSOLETED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* MSIPATCHSTATE_REGISTERED */ + /* FIXME */ + + /* MSIPATCHSTATE_ALL */ + + /* 1st */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_ALL, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERMANAGED, + "Expected MSIINSTALLCONTEXT_USERMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* same patch in multiple places, only one is enumerated */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERMANAGED, + MSIPATCHSTATE_ALL, 1, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + RegDeleteValueA(hpatch, "State"); + RegDeleteKeyA(hpatch, ""); + RegCloseKey(hpatch); + RegDeleteKeyA(udpatch, ""); + RegCloseKey(udpatch); + RegDeleteKeyA(udprod, ""); + RegCloseKey(udprod); + RegDeleteValueA(patches, "Patches"); + RegDeleteKeyA(patches, ""); + RegCloseKey(patches); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* MSIINSTALLCONTEXT_USERUNMANAGED */ + + /* MSIPATCHSTATE_APPLIED */ + + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + 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); + + /* current user product key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(prodkey, "Patches", &patches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not REG_MULTI_SZ */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)"a\0b\0c\0\0", 7); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not a squashed guid */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, patch_squashed, 0, REG_SZ, + (const BYTE *)"whatever", 9); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* patch code value exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Patches\\"); + lstrcatA(keypath, patch_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* userdata patch key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERUNMANAGED, + "Expected MSIINSTALLCONTEXT_USERUNMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* MSIPATCHSTATE_SUPERSEDED */ + + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udprod); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* UserData product key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udprod, "Patches", &udpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* UserData patches key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udpatch, patch_squashed, &hpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* specific UserData patch key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + data = MSIPATCHSTATE_SUPERSEDED; + res = RegSetValueExA(hpatch, "State", 0, REG_DWORD, + (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERUNMANAGED, + "Expected MSIINSTALLCONTEXT_USERUNMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* MSIPATCHSTATE_OBSOLETED */ + + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_OBSOLETED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + data = MSIPATCHSTATE_OBSOLETED; + res = RegSetValueExA(hpatch, "State", 0, REG_DWORD, + (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value is obsoleted */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_OBSOLETED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERUNMANAGED, + "Expected MSIINSTALLCONTEXT_USERUNMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* MSIPATCHSTATE_REGISTERED */ + /* FIXME */ + + /* MSIPATCHSTATE_ALL */ + + /* 1st */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_ALL, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_USERUNMANAGED, + "Expected MSIINSTALLCONTEXT_USERUNMANAGED, got %d\n", context); + ok(!lstrcmpA(targetsid, usersid), + "Expected \"%s\", got \"%s\"\n", usersid, targetsid); + ok(size == lstrlenA(usersid), + "Expected %d, got %d\n", lstrlenA(usersid), size); + + /* same patch in multiple places, only one is enumerated */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, usersid, MSIINSTALLCONTEXT_USERUNMANAGED, + MSIPATCHSTATE_ALL, 1, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + RegDeleteValueA(hpatch, "State"); + RegDeleteKeyA(hpatch, ""); + RegCloseKey(hpatch); + RegDeleteKeyA(udpatch, ""); + RegCloseKey(udpatch); + RegDeleteKeyA(userkey, ""); + RegCloseKey(userkey); + RegDeleteValueA(patches, patch_squashed); + RegDeleteValueA(patches, "Patches"); + RegDeleteKeyA(patches, ""); + RegCloseKey(patches); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* MSIINSTALLCONTEXT_MACHINE */ + + /* MSIPATCHSTATE_APPLIED */ + + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + 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 product key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(prodkey, "Patches", &patches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not REG_MULTI_SZ */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)"a\0b\0c\0\0", 7); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not a squashed guid */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, patch_squashed, 0, REG_SZ, + (const BYTE *)"whatever", 9); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* patch code value exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_MACHINE, + "Expected MSIINSTALLCONTEXT_MACHINE, got %d\n", context); + ok(!lstrcmpA(targetsid, ""), "Expected \"\", got \"%s\"\n", targetsid); + ok(size == 0, "Expected 0, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udprod); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local UserData product key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_MACHINE, + "Expected MSIINSTALLCONTEXT_MACHINE, got %d\n", context); + ok(!lstrcmpA(targetsid, ""), + "Expected \"\", got \"%s\"\n", targetsid); + ok(size == 0, "Expected 0, got %d\n", size); + + res = RegCreateKeyA(udprod, "Patches", &udpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local UserData Patches key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_MACHINE, + "Expected MSIINSTALLCONTEXT_MACHINE, got %d\n", context); + ok(!lstrcmpA(targetsid, ""), + "Expected \"\", got \"%s\"\n", targetsid); + ok(size == 0, "Expected 0, got %d\n", size); + + res = RegCreateKeyA(udpatch, patch_squashed, &hpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local UserData Product patch key exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + data = MSIPATCHSTATE_APPLIED; + res = RegSetValueExA(hpatch, "State", 0, REG_DWORD, + (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value exists */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_APPLIED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_MACHINE, + "Expected MSIINSTALLCONTEXT_MACHINE, got %d\n", context); + ok(!lstrcmpA(targetsid, ""), + "Expected \"\", got \"%s\"\n", targetsid); + ok(size == 0, "Expected 0, got %d\n", size); + + /* MSIPATCHSTATE_SUPERSEDED */ + + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + data = MSIPATCHSTATE_SUPERSEDED; + res = RegSetValueExA(hpatch, "State", 0, REG_DWORD, + (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value is MSIPATCHSTATE_SUPERSEDED */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_SUPERSEDED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_MACHINE, + "Expected MSIINSTALLCONTEXT_MACHINE, got %d\n", context); + ok(!lstrcmpA(targetsid, ""), "Expected \"\", got \"%s\"\n", targetsid); + ok(size == 0, "Expected 0, got %d\n", size); + + /* MSIPATCHSTATE_OBSOLETED */ + + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_OBSOLETED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + data = MSIPATCHSTATE_OBSOLETED; + res = RegSetValueExA(hpatch, "State", 0, REG_DWORD, + (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value is obsoleted */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_OBSOLETED, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_MACHINE, + "Expected MSIINSTALLCONTEXT_MACHINE, got %d\n", context); + ok(!lstrcmpA(targetsid, ""), "Expected \"\", got \"%s\"\n", targetsid); + ok(size == 0, "Expected 0, got %d\n", size); + + /* MSIPATCHSTATE_REGISTERED */ + /* FIXME */ + + /* MSIPATCHSTATE_ALL */ + + /* 1st */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_ALL, 0, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patchcode, patch), + "Expected \"%s\", got \"%s\"\n", patch, patchcode); + ok(!lstrcmpA(targetprod, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, targetprod); + ok(context == MSIINSTALLCONTEXT_MACHINE, + "Expected MSIINSTALLCONTEXT_MACHINE, got %d\n", context); + ok(!lstrcmpA(targetsid, ""), "Expected \"\", got \"%s\"\n", targetsid); + ok(size == 0, "Expected 0, got %d\n", size); + + /* same patch in multiple places, only one is enumerated */ + lstrcpyA(patchcode, "apple"); + lstrcpyA(targetprod, "banana"); + context = 0xdeadbeef; + lstrcpyA(targetsid, "kiwi"); + size = MAX_PATH; + r = pMsiEnumPatchesExA(prodcode, NULL, MSIINSTALLCONTEXT_MACHINE, + MSIPATCHSTATE_ALL, 1, patchcode, targetprod, + &context, targetsid, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patchcode, "apple"), + "Expected patchcode to be unchanged, got %s\n", patchcode); + ok(!lstrcmpA(targetprod, "banana"), + "Expected targetprod to be unchanged, got %s\n", targetprod); + ok(context == 0xdeadbeef, + "Expected context to be unchanged, got %d\n", context); + ok(!lstrcmpA(targetsid, "kiwi"), + "Expected targetsid to be unchanged, got %s\n", targetsid); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + RegDeleteValueA(patches, patch_squashed); + RegDeleteValueA(patches, "Patches"); + RegDeleteKeyA(patches, ""); + RegCloseKey(patches); + RegDeleteValueA(hpatch, "State"); + RegDeleteKeyA(hpatch, ""); + RegCloseKey(hpatch); + RegDeleteKeyA(udpatch, ""); + RegCloseKey(udpatch); + RegDeleteKeyA(udprod, ""); + RegCloseKey(udprod); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); +} + +static void test_MsiEnumPatches(void) +{ + CHAR keypath[MAX_PATH], patch[MAX_PATH]; + CHAR patchcode[MAX_PATH], patch_squashed[MAX_PATH]; + CHAR prodcode[MAX_PATH], prod_squashed[MAX_PATH]; + CHAR transforms[MAX_PATH]; + HKEY prodkey, patches, udprod; + HKEY userkey, hpatch, udpatch; + DWORD size, data; + LPSTR usersid; + LONG res; + UINT r; + + create_test_guid(prodcode, prod_squashed); + create_test_guid(patchcode, patch_squashed); + get_user_sid(&usersid); + + /* NULL szProduct */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(NULL, 0, patch, transforms, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* empty szProduct */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA("", 0, patch, transforms, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* garbage szProduct */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA("garbage", 0, patch, transforms, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* guid without brackets */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D", 0, patch, + transforms, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* guid with brackets */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}", 0, patch, + transforms, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* same length as guid, but random */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93", 0, patch, + transforms, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* MSIINSTALLCONTEXT_USERMANAGED */ + + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + 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); + + /* managed product key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(prodkey, "Patches", &patches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* patches key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not REG_MULTI_SZ */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)"a\0b\0c\0\0", 7); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not a squashed guid */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, patch_squashed, 0, REG_SZ, + (const BYTE *)"whatever", 9); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* patch squashed value exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patch, patchcode), + "Expected \"%s\", got \"%s\"\n", patchcode, patch); + ok(!lstrcmpA(transforms, "whatever"), + "Expected \"whatever\", got \"%s\"\n", transforms); + ok(size == 8, "Expected 8, got %d\n", size); + + /* lpPatchBuf is NULL */ + size = MAX_PATH; + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, NULL, transforms, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* lpTransformsBuf is NULL, pcchTransformsBuf is not */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + r = MsiEnumPatchesA(prodcode, 0, patch, NULL, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* pcchTransformsBuf is NULL, lpTransformsBuf is not */ + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, NULL); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + + /* pcchTransformsBuf is too small */ + size = 6; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!lstrcmpA(patch, patchcode), + "Expected \"%s\", got \"%s\"\n", patchcode, patch); + ok(!lstrcmpA(transforms, "whate"), + "Expected \"whate\", got \"%s\"\n", transforms); + ok(size == 16, "Expected 16, got %d\n", size); + + /* increase the index */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 1, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* increase again */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 2, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + RegDeleteValueA(patches, "Patches"); + RegDeleteKeyA(patches, ""); + RegCloseKey(patches); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* MSIINSTALLCONTEXT_USERUNMANAGED */ + + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + 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); + + /* current user product key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(prodkey, "Patches", &patches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not REG_MULTI_SZ */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)"a\0b\0c\0\0", 7); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not a squashed guid */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, patch_squashed, 0, REG_SZ, + (const BYTE *)"whatever", 9); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* patch code value exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Patches\\"); + lstrcatA(keypath, patch_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* userdata patch key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patch, patchcode), + "Expected \"%s\", got \"%s\"\n", patchcode, patch); + ok(!lstrcmpA(transforms, "whatever"), + "Expected \"whatever\", got \"%s\"\n", transforms); + ok(size == 8, "Expected 8, got %d\n", size); + + RegDeleteKeyA(userkey, ""); + RegCloseKey(userkey); + RegDeleteValueA(patches, patch_squashed); + RegDeleteValueA(patches, "Patches"); + RegDeleteKeyA(patches, ""); + RegCloseKey(patches); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* MSIINSTALLCONTEXT_MACHINE */ + + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + 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 product key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(prodkey, "Patches", &patches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not REG_MULTI_SZ */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)"a\0b\0c\0\0", 7); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists, is not a squashed guid */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_BAD_CONFIGURATION, + "Expected ERROR_BAD_CONFIGURATION, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, "Patches", 0, REG_MULTI_SZ, + (const BYTE *)patch_squashed, + lstrlenA(patch_squashed) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches value exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(patches, patch_squashed, 0, REG_SZ, + (const BYTE *)"whatever", 9); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* patch code value exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patch, patchcode), + "Expected \"%s\", got \"%s\"\n", patchcode, patch); + ok(!lstrcmpA(transforms, "whatever"), + "Expected \"whatever\", got \"%s\"\n", transforms); + ok(size == 8, "Expected 8, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udprod); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local UserData product key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patch, patchcode), + "Expected \"%s\", got \"%s\"\n", patchcode, patch); + ok(!lstrcmpA(transforms, "whatever"), + "Expected \"whatever\", got \"%s\"\n", transforms); + ok(size == 8, "Expected 8, got %d\n", size); + + res = RegCreateKeyA(udprod, "Patches", &udpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local UserData Patches key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patch, patchcode), + "Expected \"%s\", got \"%s\"\n", patchcode, patch); + ok(!lstrcmpA(transforms, "whatever"), + "Expected \"whatever\", got \"%s\"\n", transforms); + ok(size == 8, "Expected 8, got %d\n", size); + + res = RegCreateKeyA(udpatch, patch_squashed, &hpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local UserData Product patch key exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + ok(!lstrcmpA(patch, "apple"), + "Expected lpPatchBuf to be unchanged, got \"%s\"\n", patch); + ok(!lstrcmpA(transforms, "banana"), + "Expected lpTransformsBuf to be unchanged, got \"%s\"\n", transforms); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + data = MSIPATCHSTATE_APPLIED; + res = RegSetValueExA(hpatch, "State", 0, REG_DWORD, + (const BYTE *)&data, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value exists */ + size = MAX_PATH; + lstrcpyA(patch, "apple"); + lstrcpyA(transforms, "banana"); + r = MsiEnumPatchesA(prodcode, 0, patch, transforms, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(patch, patchcode), + "Expected \"%s\", got \"%s\"\n", patchcode, patch); + ok(!lstrcmpA(transforms, "whatever"), + "Expected \"whatever\", got \"%s\"\n", transforms); + ok(size == 8, "Expected 8, got %d\n", size); + + RegDeleteValueA(patches, patch_squashed); + RegDeleteValueA(patches, "Patches"); + RegDeleteKeyA(patches, ""); + RegCloseKey(patches); + RegDeleteValueA(hpatch, "State"); + RegDeleteKeyA(hpatch, ""); + RegCloseKey(hpatch); + RegDeleteKeyA(udpatch, ""); + RegCloseKey(udpatch); + RegDeleteKeyA(udprod, ""); + RegCloseKey(udprod); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); +} + +static void test_MsiGetPatchInfoEx(void) +{ + CHAR keypath[MAX_PATH], val[MAX_PATH]; + CHAR patchcode[MAX_PATH], patch_squashed[MAX_PATH]; + CHAR prodcode[MAX_PATH], prod_squashed[MAX_PATH]; + HKEY prodkey, patches, udprod, props; + HKEY hpatch, udpatch, prodpatches; + LPSTR usersid; + DWORD size; + LONG res; + UINT r; + + if (!pMsiGetPatchInfoExA) + { + win_skip("MsiGetPatchInfoEx not implemented\n"); + return; + } + + create_test_guid(prodcode, prod_squashed); + create_test_guid(patchcode, patch_squashed); + get_user_sid(&usersid); + + /* NULL szPatchCode */ + lstrcpyA(val, "apple"); + size = MAX_PATH; + r = pMsiGetPatchInfoExA(NULL, prodcode, NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* empty szPatchCode */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA("", prodcode, NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* garbage szPatchCode */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA("garbage", prodcode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* guid without brackets */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA("6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D", prodcode, + NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* guid with brackets */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA("{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}", prodcode, + NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* same length as guid, but random */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA("A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93", prodcode, + NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* NULL szProductCode */ + lstrcpyA(val, "apple"); + size = MAX_PATH; + r = pMsiGetPatchInfoExA(patchcode, NULL, NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* empty szProductCode */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, "", NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* garbage szProductCode */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, "garbage", NULL, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* guid without brackets */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, "6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D", + NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* guid with brackets */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, "{6700E8CF-95AB-4D9C-BC2C-15840DEA7A5D}", + NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* same length as guid, but random */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, "A938G02JF-2NF3N93-VN3-2NNF-3KGKALDNF93", + NULL, MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* szUserSid cannot be S-1-5-18 for MSIINSTALLCONTEXT_USERMANAGED */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, "S-1-5-18", + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* szUserSid cannot be S-1-5-18 for MSIINSTALLCONTEXT_USERUNMANAGED */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, "S-1-5-18", + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* szUserSid cannot be S-1-5-18 for MSIINSTALLCONTEXT_MACHINE */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, "S-1-5-18", + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* szUserSid must be NULL for MSIINSTALLCONTEXT_MACHINE */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* dwContext is out of range */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_NONE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* dwContext is out of range */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_ALL, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* dwContext is invalid */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, 3, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* MSIINSTALLCONTEXT_USERMANAGED */ + + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udprod); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local UserData product key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udprod, "InstallProperties", &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* InstallProperties key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udprod, "Patches", &patches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(patches, patch_squashed, &hpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + 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); + + /* managed product key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(prodkey, "Patches", &prodpatches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(prodpatches, patch_squashed, 0, REG_SZ, + (const BYTE *)"transforms", 11); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* specific patch value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Patches\\"); + lstrcatA(keypath, patch_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* UserData Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, ""), "Expected \"\", got \"%s\"\n", val); + ok(size == 0, "Expected 0, got %d\n", size); + + res = RegSetValueExA(udpatch, "ManagedLocalPackage", 0, REG_SZ, + (const BYTE *)"pack", 5); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* ManagedLocalPatch value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "pack"), "Expected \"pack\", got \"%s\"\n", val); + ok(size == 4, "Expected 4, got %d\n", size); + + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_TRANSFORMS, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "transforms"), "Expected \"transforms\", got \"%s\"\n", val); + ok(size == 10, "Expected 10, got %d\n", size); + + res = RegSetValueExA(hpatch, "Installed", 0, REG_SZ, + (const BYTE *)"mydate", 7); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Installed value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_INSTALLDATE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "mydate"), "Expected \"mydate\", got \"%s\"\n", val); + ok(size == 6, "Expected 6, got %d\n", size); + + res = RegSetValueExA(hpatch, "Uninstallable", 0, REG_SZ, + (const BYTE *)"yes", 4); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Uninstallable value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_UNINSTALLABLE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "yes"), "Expected \"yes\", got \"%s\"\n", val); + ok(size == 3, "Expected 3, got %d\n", size); + + res = RegSetValueExA(hpatch, "State", 0, REG_SZ, + (const BYTE *)"good", 5); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_PATCHSTATE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "good"), "Expected \"good\", got \"%s\"\n", val); + ok(size == 4, "Expected 4, got %d\n", size); + + size = 1; + res = RegSetValueExA(hpatch, "State", 0, REG_DWORD, + (const BYTE *)&size, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* State value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_PATCHSTATE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine ok(!lstrcmpA(val, "1"), "Expected \"1\", got \"%s\"\n", val); + ok(size == 1, "Expected 1, got %d\n", size); + + res = RegSetValueExA(hpatch, "DisplayName", 0, REG_SZ, + (const BYTE *)"display", 8); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* DisplayName value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_DISPLAYNAME, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "display"), "Expected \"display\", got \"%s\"\n", val); + ok(size == 7, "Expected 7, got %d\n", size); + + res = RegSetValueExA(hpatch, "MoreInfoURL", 0, REG_SZ, + (const BYTE *)"moreinfo", 9); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* MoreInfoURL value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_MOREINFOURL, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "moreinfo"), "Expected \"moreinfo\", got \"%s\"\n", val); + ok(size == 8, "Expected 8, got %d\n", size); + + /* szProperty is invalid */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + "IDontExist", val, &size); + ok(r == ERROR_UNKNOWN_PROPERTY, + "Expected ERROR_UNKNOWN_PROPERTY, got %d\n", r); + ok(!lstrcmpA(val, "apple"), "Expected \"apple\", got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected MAX_PATH, got %d\n", size); + + /* lpValue is NULL, while pcchValue is non-NULL */ + size = MAX_PATH; + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_MOREINFOURL, NULL, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(size == 16, "Expected 16, got %d\n", size); + + /* pcchValue is NULL, while lpValue is non-NULL */ + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_MOREINFOURL, val, NULL); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), "Expected \"apple\", got \"%s\"\n", val); + + /* both lpValue and pcchValue are NULL */ + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_MOREINFOURL, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* pcchValue doesn't have enough room for NULL terminator */ + size = 8; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_MOREINFOURL, val, &size); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!lstrcmpA(val, "moreinf"), + "Expected \"moreinf\", got \"%s\"\n", val); + ok(size == 16, "Expected 16, got %d\n", size); + + /* pcchValue has exactly enough room for NULL terminator */ + size = 9; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_MOREINFOURL, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "moreinfo"), + "Expected \"moreinfo\", got \"%s\"\n", val); + ok(size == 8, "Expected 8, got %d\n", size); + + /* pcchValue is too small, lpValue is NULL */ + size = 0; + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_MOREINFOURL, NULL, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(size == 16, "Expected 16, got %d\n", size); + + RegDeleteValueA(prodpatches, patch_squashed); + RegDeleteKeyA(prodpatches, ""); + RegCloseKey(prodpatches); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* UserData is sufficient for all properties + * except INSTALLPROPERTY_TRANSFORMS + */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "pack"), "Expected \"pack\", got \"%s\"\n", val); + ok(size == 4, "Expected 4, got %d\n", size); + + /* UserData is sufficient for all properties + * except INSTALLPROPERTY_TRANSFORMS + */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERMANAGED, + INSTALLPROPERTY_TRANSFORMS, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), "Expected \"apple\", got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected MAX_PATH, got %d\n", size); + + RegDeleteValueA(hpatch, "MoreInfoURL"); + RegDeleteValueA(hpatch, "Display"); + RegDeleteValueA(hpatch, "State"); + RegDeleteValueA(hpatch, "Uninstallable"); + RegDeleteValueA(hpatch, "Installed"); + RegDeleteValueA(udpatch, "ManagedLocalPackage"); + RegDeleteKeyA(udpatch, ""); + RegCloseKey(udpatch); + RegDeleteKeyA(hpatch, ""); + RegCloseKey(hpatch); + RegDeleteKeyA(patches, ""); + RegCloseKey(patches); + RegDeleteKeyA(props, ""); + RegCloseKey(props); + RegDeleteKeyA(udprod, ""); + RegCloseKey(udprod); + + /* MSIINSTALLCONTEXT_USERUNMANAGED */ + + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udprod); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local UserData product key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udprod, "InstallProperties", &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* InstallProperties key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udprod, "Patches", &patches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(patches, patch_squashed, &hpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + 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); + + /* current user product key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(prodkey, "Patches", &prodpatches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(prodpatches, patch_squashed, 0, REG_SZ, + (const BYTE *)"transforms", 11); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* specific patch value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"); + lstrcatA(keypath, usersid); + lstrcatA(keypath, "\\Patches\\"); + lstrcatA(keypath, patch_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* UserData Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, ""), "Expected \"\", got \"%s\"\n", val); + ok(size == 0, "Expected 0, got %d\n", size); + + res = RegSetValueExA(udpatch, "LocalPackage", 0, REG_SZ, + (const BYTE *)"pack", 5); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* LocalPatch value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "pack"), "Expected \"pack\", got \"%s\"\n", val); + ok(size == 4, "Expected 4, got %d\n", size); + + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_TRANSFORMS, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "transforms"), "Expected \"transforms\", got \"%s\"\n", val); + ok(size == 10, "Expected 10, got %d\n", size); + + RegDeleteValueA(prodpatches, patch_squashed); + RegDeleteKeyA(prodpatches, ""); + RegCloseKey(prodpatches); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* UserData is sufficient for all properties + * except INSTALLPROPERTY_TRANSFORMS + */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "pack"), "Expected \"pack\", got \"%s\"\n", val); + ok(size == 4, "Expected 4, got %d\n", size); + + /* UserData is sufficient for all properties + * except INSTALLPROPERTY_TRANSFORMS + */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, usersid, + MSIINSTALLCONTEXT_USERUNMANAGED, + INSTALLPROPERTY_TRANSFORMS, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), "Expected \"apple\", got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected MAX_PATH, got %d\n", size); + + RegDeleteValueA(udpatch, "LocalPackage"); + RegDeleteKeyA(udpatch, ""); + RegCloseKey(udpatch); + RegDeleteKeyA(hpatch, ""); + RegCloseKey(hpatch); + RegDeleteKeyA(patches, ""); + RegCloseKey(patches); + RegDeleteKeyA(props, ""); + RegCloseKey(props); + RegDeleteKeyA(udprod, ""); + RegCloseKey(udprod); + + /* MSIINSTALLCONTEXT_MACHINE */ + + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer"); + lstrcatA(keypath, "\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udprod); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* local UserData product key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PRODUCT, + "Expected ERROR_UNKNOWN_PRODUCT, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udprod, "InstallProperties", &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* InstallProperties key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(udprod, "Patches", &patches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(patches, patch_squashed, &hpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + 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 product key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegCreateKeyA(prodkey, "Patches", &prodpatches); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + res = RegSetValueExA(prodpatches, patch_squashed, 0, REG_SZ, + (const BYTE *)"transforms", 11); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* specific patch value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer"); + lstrcatA(keypath, "\\UserData\\S-1-5-18\\Patches\\"); + lstrcatA(keypath, patch_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &udpatch); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* UserData Patches key exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, ""), "Expected \"\", got \"%s\"\n", val); + ok(size == 0, "Expected 0, got %d\n", size); + + res = RegSetValueExA(udpatch, "LocalPackage", 0, REG_SZ, + (const BYTE *)"pack", 5); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + /* LocalPatch value exists */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "pack"), "Expected \"pack\", got \"%s\"\n", val); + ok(size == 4, "Expected 4, got %d\n", size); + + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_TRANSFORMS, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "transforms"), "Expected \"transforms\", got \"%s\"\n", val); + ok(size == 10, "Expected 10, got %d\n", size); + + RegDeleteValueA(prodpatches, patch_squashed); + RegDeleteKeyA(prodpatches, ""); + RegCloseKey(prodpatches); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); + + /* UserData is sufficient for all properties + * except INSTALLPROPERTY_TRANSFORMS + */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_LOCALPACKAGE, val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, "pack"), "Expected \"pack\", got \"%s\"\n", val); + ok(size == 4, "Expected 4, got %d\n", size); + + /* UserData is sufficient for all properties + * except INSTALLPROPERTY_TRANSFORMS + */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = pMsiGetPatchInfoExA(patchcode, prodcode, NULL, + MSIINSTALLCONTEXT_MACHINE, + INSTALLPROPERTY_TRANSFORMS, val, &size); + ok(r == ERROR_UNKNOWN_PATCH, "Expected ERROR_UNKNOWN_PATCH, got %d\n", r); + ok(!lstrcmpA(val, "apple"), "Expected \"apple\", got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected MAX_PATH, got %d\n", size); + + RegDeleteValueA(udpatch, "LocalPackage"); + RegDeleteKeyA(udpatch, ""); + RegCloseKey(udpatch); + RegDeleteKeyA(hpatch, ""); + RegCloseKey(hpatch); + RegDeleteKeyA(patches, ""); + RegCloseKey(patches); + RegDeleteKeyA(props, ""); + RegCloseKey(props); + RegDeleteKeyA(udprod, ""); + RegCloseKey(udprod); +} + START_TEST(msi) { - init_functionpointers(); test_usefeature(); @@ -6925,6 +10738,10 @@ START_TEST(msi) test_MsiGetProductInfo(); test_MsiGetProductInfoEx(); test_MsiGetUserInfo(); + test_MsiOpenProduct(); + test_MsiEnumPatchesEx(); + test_MsiEnumPatches(); + test_MsiGetPatchInfoEx(); } test_MsiGetFileVersion(); diff --git a/rostests/winetests/msi/package.c b/rostests/winetests/msi/package.c index 5c8741bee7b..5ad1d385bd8 100644 --- a/rostests/winetests/msi/package.c +++ b/rostests/winetests/msi/package.c @@ -32,8 +32,29 @@ static const char msifile[] = "winetest.msi"; char CURR_DIR[MAX_PATH]; +static void get_user_sid(LPSTR *usersid) +{ + HANDLE token; + BYTE buf[1024]; + DWORD size; + PTOKEN_USER user; + HMODULE hadvapi32 = GetModuleHandleA("advapi32.dll"); + static BOOL (WINAPI *pConvertSidToStringSidA)(PSID, LPSTR*); + + *usersid = NULL; + pConvertSidToStringSidA = (void *)GetProcAddress(hadvapi32, "ConvertSidToStringSidA"); + if (!pConvertSidToStringSidA) + return; + + OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token); + size = sizeof(buf); + GetTokenInformation(token, TokenUser, (void *)buf, size, &size); + user = (PTOKEN_USER)buf; + pConvertSidToStringSidA(user->User.Sid, usersid); +} + /* RegDeleteTreeW from dlls/advapi32/registry.c */ -LSTATUS WINAPI package_RegDeleteTreeW(HKEY hKey, LPCWSTR lpszSubKey) +static LSTATUS package_RegDeleteTreeW(HKEY hKey, LPCWSTR lpszSubKey) { LONG ret; DWORD dwMaxSubkeyLen, dwMaxValueLen; @@ -96,6 +117,169 @@ cleanup: return ret; } +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 set_component_path(LPCSTR filename, MSIINSTALLCONTEXT context, + LPCSTR guid, LPSTR usersid, BOOL dir) +{ + WCHAR guidW[MAX_PATH]; + WCHAR squashedW[MAX_PATH]; + CHAR squashed[MAX_PATH]; + CHAR comppath[MAX_PATH]; + CHAR prodpath[MAX_PATH]; + CHAR path[MAX_PATH]; + LPCSTR prod = NULL; + 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); + + if (context == MSIINSTALLCONTEXT_MACHINE) + { + prod = "3D0DAE300FACA1300AD792060BCDAA92"; + sprintf(comppath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\UserData\\S-1-5-18\\Components\\%s", squashed); + lstrcpyA(prodpath, + "SOFTWARE\\Classes\\Installer\\" + "Products\\3D0DAE300FACA1300AD792060BCDAA92"); + } + else if (context == MSIINSTALLCONTEXT_USERUNMANAGED) + { + prod = "7D2F387510109040002000060BECB6AB"; + sprintf(comppath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\UserData\\%s\\Components\\%s", usersid, squashed); + sprintf(prodpath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\%s\\Installer\\Products\\" + "7D2F387510109040002000060BECB6AB", usersid); + } + else if (context == MSIINSTALLCONTEXT_USERMANAGED) + { + prod = "7D2F387510109040002000060BECB6AB"; + sprintf(comppath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\UserData\\%s\\Components\\%s", usersid, squashed); + sprintf(prodpath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\Managed\\%s\\Installer\\Products\\" + "7D2F387510109040002000060BECB6AB", usersid); + } + + RegCreateKeyA(HKEY_LOCAL_MACHINE, comppath, &hkey); + + lstrcpyA(path, CURR_DIR); + lstrcatA(path, "\\"); + if (!dir) lstrcatA(path, filename); + + RegSetValueExA(hkey, prod, 0, REG_SZ, (LPBYTE)path, lstrlenA(path)); + RegCloseKey(hkey); + + RegCreateKeyA(HKEY_LOCAL_MACHINE, prodpath, &hkey); + RegCloseKey(hkey); +} + +static void delete_component_path(LPCSTR guid, MSIINSTALLCONTEXT context, LPSTR usersid) +{ + WCHAR guidW[MAX_PATH]; + WCHAR squashedW[MAX_PATH]; + WCHAR substrW[MAX_PATH]; + CHAR squashed[MAX_PATH]; + CHAR comppath[MAX_PATH]; + CHAR prodpath[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); + + if (context == MSIINSTALLCONTEXT_MACHINE) + { + sprintf(comppath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\UserData\\S-1-5-18\\Components\\%s", squashed); + lstrcpyA(prodpath, + "SOFTWARE\\Classes\\Installer\\" + "Products\\3D0DAE300FACA1300AD792060BCDAA92"); + } + else if (context == MSIINSTALLCONTEXT_USERUNMANAGED) + { + sprintf(comppath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\UserData\\%s\\Components\\%s", usersid, squashed); + sprintf(prodpath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\%s\\Installer\\Products\\" + "7D2F387510109040002000060BECB6AB", usersid); + } + else if (context == MSIINSTALLCONTEXT_USERMANAGED) + { + sprintf(comppath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\UserData\\%s\\Components\\%s", usersid, squashed); + sprintf(prodpath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" + "Installer\\Managed\\%s\\Installer\\Products\\" + "7D2F387510109040002000060BECB6AB", usersid); + } + + MultiByteToWideChar(CP_ACP, 0, comppath, -1, substrW, MAX_PATH); + package_RegDeleteTreeW(HKEY_LOCAL_MACHINE, substrW); + + MultiByteToWideChar(CP_ACP, 0, prodpath, -1, substrW, MAX_PATH); + package_RegDeleteTreeW(HKEY_LOCAL_MACHINE, substrW); +} + static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec) { MSIHANDLE hview = 0; @@ -305,227 +489,101 @@ static UINT create_complocator_table( MSIHANDLE hdb ) "PRIMARY KEY `Signature_`)" ); } -static UINT add_component_entry( MSIHANDLE hdb, const char *values ) +static UINT create_inilocator_table( MSIHANDLE hdb ) { - 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, query ); - HeapFree(GetProcessHeap(), 0, query); - return r; + return run_query( hdb, + "CREATE TABLE `IniLocator` (" + "`Signature_` CHAR(72) NOT NULL, " + "`FileName` CHAR(255) NOT NULL, " + "`Section` CHAR(96)NOT NULL, " + "`Key` CHAR(128)NOT NULL, " + "`Field` SHORT, " + "`Type` SHORT " + "PRIMARY KEY `Signature_`)" ); } -static UINT add_feature_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `Feature` (`Feature`, `Feature_Parent`, " - "`Title`, `Description`, `Display`, `Level`, `Directory_`, `Attributes`) VALUES( %s )"; - char *query; - UINT sz, r; +#define make_add_entry(type, qtext) \ + static UINT add##_##type##_##entry( MSIHANDLE hdb, const char *values ) \ + { \ + char insert[] = qtext; \ + 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; \ + } - 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; -} +make_add_entry(component, + "INSERT INTO `Component` " + "(`Component`, `ComponentId`, `Directory_`, " + "`Attributes`, `Condition`, `KeyPath`) VALUES( %s )") -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; +make_add_entry(directory, + "INSERT INTO `Directory` " + "(`Directory`,`Directory_Parent`,`DefaultDir`) VALUES( %s )") - 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; -} +make_add_entry(feature, + "INSERT INTO `Feature` " + "(`Feature`, `Feature_Parent`, `Title`, `Description`, " + "`Display`, `Level`, `Directory_`, `Attributes`) VALUES( %s )") -static UINT add_file_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `File` " - "(`File`, `Component_`, `FileName`, `FileSize`, `Version`, `Language`, `Attributes`, `Sequence`) " - "VALUES( %s )"; - char *query; - UINT sz, r; +make_add_entry(feature_components, + "INSERT INTO `FeatureComponents` " + "(`Feature_`, `Component_`) VALUES( %s )") - 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; -} +make_add_entry(file, + "INSERT INTO `File` " + "(`File`, `Component_`, `FileName`, `FileSize`, " + "`Version`, `Language`, `Attributes`, `Sequence`) VALUES( %s )") -static UINT add_appsearch_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `AppSearch` " - "(`Property`, `Signature_`) " - "VALUES( %s )"; - char *query; - UINT sz, r; +make_add_entry(appsearch, + "INSERT INTO `AppSearch` " + "(`Property`, `Signature_`) VALUES( %s )") - 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; -} +make_add_entry(reglocator, + "INSERT INTO `RegLocator` " + "(`Signature_`, `Root`, `Key`, `Name`, `Type`) VALUES( %s )") -static UINT add_reglocator_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `RegLocator` " - "(`Signature_`, `Root`, `Key`, `Name`, `Type`) " - "VALUES( %s )"; - char *query; - UINT sz, r; +make_add_entry(signature, + "INSERT INTO `Signature` " + "(`Signature`, `FileName`, `MinVersion`, `MaxVersion`," + " `MinSize`, `MaxSize`, `MinDate`, `MaxDate`, `Languages`) " + "VALUES( %s )") - 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; -} +make_add_entry(launchcondition, + "INSERT INTO `LaunchCondition` " + "(`Condition`, `Description`) VALUES( %s )") -static UINT add_signature_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `Signature` " - "(`Signature`, `FileName`, `MinVersion`, `MaxVersion`," - " `MinSize`, `MaxSize`, `MinDate`, `MaxDate`, `Languages`) " - "VALUES( %s )"; - char *query; - UINT sz, r; +make_add_entry(property, + "INSERT INTO `Property` (`Property`, `Value`) VALUES( %s )") - 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; -} +make_add_entry(install_execute_sequence, + "INSERT INTO `InstallExecuteSequence` " + "(`Action`, `Condition`, `Sequence`) VALUES( %s )") -static UINT add_launchcondition_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `LaunchCondition` " - "(`Condition`, `Description`) " - "VALUES( %s )"; - char *query; - UINT sz, r; +make_add_entry(media, + "INSERT INTO `Media` " + "(`DiskId`, `LastSequence`, `DiskPrompt`, " + "`Cabinet`, `VolumeLabel`, `Source`) VALUES( %s )") - 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; -} +make_add_entry(ccpsearch, + "INSERT INTO `CCPSearch` (`Signature_`) VALUES( %s )") -static UINT add_property_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `Property` " - "(`Property`, `Value`) " - "VALUES( %s )"; - char *query; - UINT sz, r; +make_add_entry(drlocator, + "INSERT INTO `DrLocator` " + "(`Signature_`, `Parent`, `Path`, `Depth`) VALUES( %s )") - 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; -} +make_add_entry(complocator, + "INSERT INTO `CompLocator` " + "(`Signature_`, `ComponentId`, `Type`) VALUES( %s )") -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; -} +make_add_entry(inilocator, + "INSERT INTO `IniLocator` " + "(`Signature_`, `FileName`, `Section`, `Key`, `Field`, `Type`) " + "VALUES( %s )") static UINT set_summary_info(MSIHANDLE hdb) { @@ -631,6 +689,68 @@ static void create_test_file(const CHAR *name) CloseHandle(file); } +typedef struct _tagVS_VERSIONINFO +{ + WORD wLength; + WORD wValueLength; + WORD wType; + WCHAR szKey[1]; + WORD wPadding1[1]; + VS_FIXEDFILEINFO Value; + WORD wPadding2[1]; + WORD wChildren[1]; +} VS_VERSIONINFO; + +#define roundoffs(a, b, r) (((BYTE *)(b) - (BYTE *)(a) + ((r) - 1)) & ~((r) - 1)) +#define roundpos(a, b, r) (((BYTE *)(a)) + roundoffs(a, b, r)) + +static BOOL create_file_with_version(const CHAR *name, LONG ms, LONG ls) +{ + VS_VERSIONINFO *pVerInfo; + VS_FIXEDFILEINFO *pFixedInfo; + LPBYTE buffer, ofs; + CHAR path[MAX_PATH]; + DWORD handle, size; + HANDLE resource; + BOOL ret = FALSE; + + GetSystemDirectory(path, MAX_PATH); + lstrcatA(path, "\\kernel32.dll"); + + CopyFileA(path, name, FALSE); + + size = GetFileVersionInfoSize(path, &handle); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + + GetFileVersionInfoA(path, 0, size, buffer); + + pVerInfo = (VS_VERSIONINFO *)buffer; + ofs = (BYTE *)&pVerInfo->szKey[lstrlenW(pVerInfo->szKey) + 1]; + pFixedInfo = (VS_FIXEDFILEINFO *)roundpos(pVerInfo, ofs, 4); + + pFixedInfo->dwFileVersionMS = ms; + pFixedInfo->dwFileVersionLS = ls; + pFixedInfo->dwProductVersionMS = ms; + pFixedInfo->dwProductVersionLS = ls; + + resource = BeginUpdateResource(name, FALSE); + if (!resource) + goto done; + + if (!UpdateResource(resource, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), buffer, size)) + goto done; + + if (!EndUpdateResource(resource, FALSE)) + goto done; + + ret = TRUE; + +done: + HeapFree(GetProcessHeap(), 0, buffer); + return ret; +} + static void test_createpackage(void) { MSIHANDLE hPackage = 0; @@ -644,124 +764,6 @@ static void test_createpackage(void) DeleteFile(msifile); } -static void test_getsourcepath_bad( void ) -{ - static const char str[] = { 0 }; - char buffer[0x80]; - DWORD sz; - UINT r; - - r = MsiGetSourcePath( -1, NULL, NULL, NULL ); - ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n"); - - sz = 0; - r = MsiGetSourcePath( -1, NULL, buffer, &sz ); - ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n"); - - sz = 0; - r = MsiGetSourcePath( -1, str, NULL, &sz ); - ok( r == ERROR_INVALID_HANDLE, "return value wrong\n"); - - sz = 0; - r = MsiGetSourcePath( -1, str, NULL, NULL ); - ok( r == ERROR_INVALID_HANDLE, "return value wrong\n"); - - sz = 0; - r = MsiGetSourcePath( -1, str, buffer, &sz ); - ok( r == ERROR_INVALID_HANDLE, "return value wrong\n"); -} - -static UINT add_directory_entry( MSIHANDLE hdb, const char *values ) -{ - char insert[] = "INSERT INTO `Directory` (`Directory`,`Directory_Parent`,`DefaultDir`) 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 void test_getsourcepath( void ) -{ - static const char str[] = { 0 }; - char buffer[0x80]; - DWORD sz; - UINT r; - MSIHANDLE hpkg, hdb; - - hpkg = package_from_db(create_package_db()); - ok( hpkg, "failed to create package\n"); - - sz = 0; - buffer[0] = 'x'; - r = MsiGetSourcePath( hpkg, str, buffer, &sz ); - ok( r == ERROR_DIRECTORY, "return value wrong\n"); - ok( buffer[0] == 'x', "buffer modified\n"); - - sz = 1; - buffer[0] = 'x'; - r = MsiGetSourcePath( hpkg, str, buffer, &sz ); - ok( r == ERROR_DIRECTORY, "return value wrong\n"); - ok( buffer[0] == 'x', "buffer modified\n"); - - MsiCloseHandle( hpkg ); - - - /* another test but try create a directory this time */ - hdb = create_package_db(); - ok( hdb, "failed to create database\n"); - - r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'"); - ok( r == S_OK, "failed\n"); - - hpkg = package_from_db(hdb); - ok( hpkg, "failed to create package\n"); - - sz = sizeof buffer -1; - strcpy(buffer,"x bad"); - r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz ); - ok( r == ERROR_DIRECTORY, "return value wrong\n"); - - r = MsiDoAction( hpkg, "CostInitialize"); - ok( r == ERROR_SUCCESS, "cost init failed\n"); - r = MsiDoAction( hpkg, "CostFinalize"); - ok( r == ERROR_SUCCESS, "cost finalize failed\n"); - - sz = sizeof buffer -1; - buffer[0] = 'x'; - r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz ); - ok( r == ERROR_SUCCESS, "return value wrong\n"); - ok( sz == strlen(buffer), "returned length wrong\n"); - - sz = 0; - 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"); - - 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"); - - r = MsiGetSourcePath( hpkg, "targetdir", NULL, NULL ); - ok( r == ERROR_DIRECTORY, "return value wrong\n"); - - r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, NULL ); - ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n"); - - r = MsiGetSourcePath( hpkg, "TARGETDIR", NULL, &sz ); - ok( r == ERROR_SUCCESS, "return value wrong\n"); - - MsiCloseHandle( hpkg ); - DeleteFile(msifile); -} - static void test_doaction( void ) { MSIHANDLE hpkg; @@ -2240,9 +2242,6 @@ 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; @@ -2250,6 +2249,9 @@ static void test_states(void) MSIHANDLE hdb; INSTALLSTATE state, action; + static const CHAR msifile2[] = "winetest2.msi"; + static const CHAR msifile3[] = "winetest3.msi"; + hdb = create_package_db(); ok ( hdb, "failed to create package database\n" ); @@ -2554,6 +2556,9 @@ static void test_states(void) MsiCloseHandle(hdb); + CopyFileA(msifile, msifile2, FALSE); + CopyFileA(msifile, msifile3, FALSE); + state = 0xdeadbee; action = 0xdeadbee; r = MsiGetFeatureState(hpkg, "one", &state, &action); @@ -3128,20 +3133,14 @@ static void test_states(void) 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); - } + 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); - } + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); state = 0xdeadbee; action = 0xdeadbee; @@ -3218,80 +3217,56 @@ static void test_states(void) 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); - } + 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); - } + 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); - } + 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); - } + 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); - } + 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); - } + 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); - } + 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); - } + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); MsiCloseHandle( hpkg ); @@ -3855,40 +3830,28 @@ 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_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); - } + 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( 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( 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( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); state = 0xdeadbee; @@ -3903,109 +3866,76 @@ static void test_states(void) 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); - } + 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); - } + 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( 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( 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( 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( 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( 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); - } + 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); - } + 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( 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( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action); state = 0xdeadbee; @@ -4020,80 +3950,56 @@ static void test_states(void) 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); - } + 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); - } + 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); - } + 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); - } + 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); - } + 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); - } + 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); - } + 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); - } + ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action); MsiCloseHandle(hpkg); @@ -4101,7 +4007,1467 @@ static void test_states(void) r = MsiInstallProduct(msifile, "REMOVE=ALL"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + /* all features installed locally */ + r = MsiInstallProduct(msifile2, "ADDLOCAL=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiOpenDatabase(msifile2, 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,two,three,four,five,six,seven'"); + ok( r == ERROR_SUCCESS, "cannot add property: %d\n", r ); + + hpkg = package_from_db( hdb ); + ok( hpkg, "failed to create package\n"); + + 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 ); + 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 ); + 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, "three", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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 ); + 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_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, "seven", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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 = MsiGetComponentState(hpkg, "alpha", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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 ); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "gamma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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 ); + 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 ); + 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 ); + 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, "zeta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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, "iota", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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 ); + 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_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "nu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "xi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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, "rho", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "sigma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + MsiCloseHandle(hpkg); + + /* uninstall the product */ + r = MsiInstallProduct(msifile2, "REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* all features installed from source */ + r = MsiInstallProduct(msifile3, "ADDSOURCE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiOpenDatabase(msifile3, MSIDBOPEN_DIRECT, &hdb); + ok(r == ERROR_SUCCESS, "failed to open database: %d\n", r); + + /* this property must not be in the saved msi file */ + r = add_property_entry( hdb, "'ADDSOURCE', 'one,two,three,four,five,six,seven'"); + ok( r == ERROR_SUCCESS, "cannot add property: %d\n", r ); + + hpkg = package_from_db( hdb ); + ok( hpkg, "failed to create package\n"); + + 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 ); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "two", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "three", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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 ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + 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_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetFeatureState(hpkg, "seven", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + ok( state == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "alpha", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "beta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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, "gamma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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, "theta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "delta", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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 ); + 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 ); + 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 ); + 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 ); + 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_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "mu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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, "nu", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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, "xi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "omicron", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + state = 0xdeadbee; + action = 0xdeadbee; + r = MsiGetComponentState(hpkg, "pi", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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, "rho", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + 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, "sigma", &state, &action); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r ); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + ok( state == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", state); + + MsiCloseHandle(hpkg); + + /* uninstall the product */ + r = MsiInstallProduct(msifile3, "REMOVE=ALL"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + DeleteFileA(msifile); + DeleteFileA(msifile2); + DeleteFileA(msifile3); } static void test_getproperty(void) @@ -4331,12 +5697,1455 @@ static void test_appsearch(void) r = MsiGetPropertyA( hpkg, "WEBBROWSERPROG", prop, &size ); ok( r == ERROR_SUCCESS, "get property failed: %d\n", r); - ok( lstrlenA(prop) != 0, "Expected non-zero length\n"); + todo_wine + { + ok( lstrlenA(prop) != 0, "Expected non-zero length\n"); + } MsiCloseHandle( hpkg ); DeleteFileA(msifile); } +static void test_appsearch_complocator(void) +{ + MSIHANDLE hpkg, hdb; + CHAR path[MAX_PATH]; + CHAR prop[MAX_PATH]; + LPSTR usersid; + DWORD size; + UINT r; + + get_user_sid(&usersid); + if (!usersid) + { + skip("ConvertSidToStringSidA is not available\n"); + return; + } + + create_test_file("FileName1"); + create_test_file("FileName4"); + set_component_path("FileName1", MSIINSTALLCONTEXT_MACHINE, + "{A8AE6692-96BA-4198-8399-145D7D1D0D0E}", NULL, FALSE); + + create_test_file("FileName2"); + set_component_path("FileName2", MSIINSTALLCONTEXT_USERUNMANAGED, + "{1D2CE6F3-E81C-4949-AB81-78D7DAD2AF2E}", usersid, FALSE); + + create_test_file("FileName3"); + set_component_path("FileName3", MSIINSTALLCONTEXT_USERMANAGED, + "{19E0B999-85F5-4973-A61B-DBE4D66ECB1D}", usersid, FALSE); + + create_test_file("FileName5"); + set_component_path("FileName5", MSIINSTALLCONTEXT_MACHINE, + "{F0CCA976-27A3-4808-9DDD-1A6FD50A0D5A}", NULL, TRUE); + + create_test_file("FileName6"); + set_component_path("FileName6", MSIINSTALLCONTEXT_MACHINE, + "{C0ECD96F-7898-4410-9667-194BD8C1B648}", NULL, TRUE); + + create_test_file("FileName7"); + set_component_path("FileName7", MSIINSTALLCONTEXT_MACHINE, + "{DB20F535-9C26-4127-9C2B-CC45A8B51DA1}", NULL, FALSE); + + /* dir is FALSE, but we're pretending it's a directory */ + set_component_path("IDontExist\\", MSIINSTALLCONTEXT_MACHINE, + "{91B7359B-07F2-4221-AA8D-DE102BB87A5F}", NULL, FALSE); + + create_file_with_version("FileName8.dll", MAKELONG(2, 1), MAKELONG(4, 3)); + set_component_path("FileName8.dll", MSIINSTALLCONTEXT_MACHINE, + "{4A2E1B5B-4034-4177-833B-8CC35F1B3EF1}", NULL, FALSE); + + create_file_with_version("FileName9.dll", MAKELONG(1, 2), MAKELONG(3, 4)); + set_component_path("FileName9.dll", MSIINSTALLCONTEXT_MACHINE, + "{A204DF48-7346-4635-BA2E-66247DBAC9DF}", NULL, FALSE); + + create_file_with_version("FileName10.dll", MAKELONG(2, 1), MAKELONG(4, 3)); + set_component_path("FileName10.dll", MSIINSTALLCONTEXT_MACHINE, + "{EC30CE73-4CF9-4908-BABD-1ED82E1515FD}", NULL, FALSE); + + hdb = create_package_db(); + ok(hdb, "Expected a valid database handle\n"); + + r = create_appsearch_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP1', 'NewSignature1'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP2', 'NewSignature2'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP3', 'NewSignature3'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP4', 'NewSignature4'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP5', 'NewSignature5'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP6', 'NewSignature6'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP7', 'NewSignature7'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP8', 'NewSignature8'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP9', 'NewSignature9'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP10', 'NewSignature10'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP11', 'NewSignature11'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP12', 'NewSignature12'"); + 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); + + /* published component, machine, file, signature, misdbLocatorTypeFile */ + r = add_complocator_entry(hdb, "'NewSignature1', '{A8AE6692-96BA-4198-8399-145D7D1D0D0E}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, user-unmanaged, file, signature, misdbLocatorTypeFile */ + r = add_complocator_entry(hdb, "'NewSignature2', '{1D2CE6F3-E81C-4949-AB81-78D7DAD2AF2E}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, user-managed, file, signature, misdbLocatorTypeFile */ + r = add_complocator_entry(hdb, "'NewSignature3', '{19E0B999-85F5-4973-A61B-DBE4D66ECB1D}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, machine, file, signature, misdbLocatorTypeDirectory */ + r = add_complocator_entry(hdb, "'NewSignature4', '{A8AE6692-96BA-4198-8399-145D7D1D0D0E}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, machine, dir, signature, misdbLocatorTypeDirectory */ + r = add_complocator_entry(hdb, "'NewSignature5', '{F0CCA976-27A3-4808-9DDD-1A6FD50A0D5A}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, machine, dir, no signature, misdbLocatorTypeDirectory */ + r = add_complocator_entry(hdb, "'NewSignature6', '{C0ECD96F-7898-4410-9667-194BD8C1B648}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, machine, file, no signature, misdbLocatorTypeFile */ + r = add_complocator_entry(hdb, "'NewSignature7', '{DB20F535-9C26-4127-9C2B-CC45A8B51DA1}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* unpublished component, no signature, misdbLocatorTypeDir */ + r = add_complocator_entry(hdb, "'NewSignature8', '{FB671D5B-5083-4048-90E0-481C48D8F3A5}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, no signature, dir does not exist misdbLocatorTypeDir */ + r = add_complocator_entry(hdb, "'NewSignature9', '{91B7359B-07F2-4221-AA8D-DE102BB87A5F}', 0"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, signature w/ ver, misdbLocatorTypeFile */ + r = add_complocator_entry(hdb, "'NewSignature10', '{4A2E1B5B-4034-4177-833B-8CC35F1B3EF1}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, signature w/ ver, ver > max, misdbLocatorTypeFile */ + r = add_complocator_entry(hdb, "'NewSignature11', '{A204DF48-7346-4635-BA2E-66247DBAC9DF}', 1"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* published component, signature w/ ver, sig->name ignored, misdbLocatorTypeFile */ + r = add_complocator_entry(hdb, "'NewSignature12', '{EC30CE73-4CF9-4908-BABD-1ED82E1515FD}', 1"); + 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, "'NewSignature1', 'FileName1', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature2', 'FileName2', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature3', 'FileName3', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature4', 'FileName4', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature5', 'FileName5', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature10', 'FileName8.dll', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature11', 'FileName9.dll', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature12', 'ignored', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + hpkg = package_from_db(hdb); + ok(hpkg, "Expected a valid package handle\n"); + + r = MsiSetPropertyA(hpkg, "SIGPROP8", "october"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDoAction(hpkg, "AppSearch"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + sprintf(path, "%s\\FileName1", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP1", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName2", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP2", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName3", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP3", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName4", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP4", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName5", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP5", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP6", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP7", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP8", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "october"), "Expected \"october\", got \"%s\"\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP9", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName8.dll", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP10", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP11", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName10.dll", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP12", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + delete_component_path("{A8AE6692-96BA-4198-8399-145D7D1D0D0E}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{1D2CE6F3-E81C-4949-AB81-78D7DAD2AF2E}", + MSIINSTALLCONTEXT_USERUNMANAGED, usersid); + delete_component_path("{19E0B999-85F5-4973-A61B-DBE4D66ECB1D}", + MSIINSTALLCONTEXT_USERMANAGED, usersid); + delete_component_path("{F0CCA976-27A3-4808-9DDD-1A6FD50A0D5A}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{C0ECD96F-7898-4410-9667-194BD8C1B648}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{DB20F535-9C26-4127-9C2B-CC45A8B51DA1}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{91B7359B-07F2-4221-AA8D-DE102BB87A5F}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{4A2E1B5B-4034-4177-833B-8CC35F1B3EF1}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{A204DF48-7346-4635-BA2E-66247DBAC9DF}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{EC30CE73-4CF9-4908-BABD-1ED82E1515FD}", + MSIINSTALLCONTEXT_MACHINE, NULL); + + DeleteFileA("FileName1"); + DeleteFileA("FileName2"); + DeleteFileA("FileName3"); + DeleteFileA("FileName4"); + DeleteFileA("FileName5"); + DeleteFileA("FileName6"); + DeleteFileA("FileName7"); + DeleteFileA("FileName8.dll"); + DeleteFileA("FileName9.dll"); + DeleteFileA("FileName10.dll"); + MsiCloseHandle(hpkg); + DeleteFileA(msifile); +} + +static void test_appsearch_reglocator(void) +{ + MSIHANDLE hpkg, hdb; + CHAR path[MAX_PATH]; + CHAR prop[MAX_PATH]; + DWORD binary[2]; + DWORD size, val; + BOOL space, version; + HKEY hklm, classes; + HKEY hkcu, users; + LPCSTR str; + LPSTR ptr; + LONG res; + UINT r; + + version = TRUE; + if (!create_file_with_version("test.dll", MAKELONG(2, 1), MAKELONG(4, 3))) + version = FALSE; + + DeleteFileA("test.dll"); + + res = RegCreateKeyA(HKEY_CLASSES_ROOT, "Software\\Wine", &classes); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(classes, "Value1", 0, REG_SZ, + (const BYTE *)"regszdata", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegCreateKeyA(HKEY_CURRENT_USER, "Software\\Wine", &hkcu); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(hkcu, "Value1", 0, REG_SZ, + (const BYTE *)"regszdata", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + users = 0; + res = RegCreateKeyA(HKEY_USERS, "S-1-5-18\\Software\\Wine", &users); + ok(res == ERROR_SUCCESS || + broken(res == ERROR_INVALID_PARAMETER), + "Expected ERROR_SUCCESS, got %d\n", res); + + if (res == ERROR_SUCCESS) + { + res = RegSetValueExA(users, "Value1", 0, REG_SZ, + (const BYTE *)"regszdata", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + } + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine", &hklm); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueA(hklm, NULL, REG_SZ, "defvalue", 8); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(hklm, "Value1", 0, REG_SZ, + (const BYTE *)"regszdata", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + val = 42; + res = RegSetValueExA(hklm, "Value2", 0, REG_DWORD, + (const BYTE *)&val, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + val = -42; + res = RegSetValueExA(hklm, "Value3", 0, REG_DWORD, + (const BYTE *)&val, sizeof(DWORD)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(hklm, "Value4", 0, REG_EXPAND_SZ, + (const BYTE *)"%PATH%", 7); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(hklm, "Value5", 0, REG_EXPAND_SZ, + (const BYTE *)"my%NOVAR%", 10); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(hklm, "Value6", 0, REG_MULTI_SZ, + (const BYTE *)"one\0two\0", 9); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + binary[0] = 0x1234abcd; + binary[1] = 0x567890ef; + res = RegSetValueExA(hklm, "Value7", 0, REG_BINARY, + (const BYTE *)binary, sizeof(binary)); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(hklm, "Value8", 0, REG_SZ, + (const BYTE *)"#regszdata", 11); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + create_test_file("FileName1"); + sprintf(path, "%s\\FileName1", CURR_DIR); + res = RegSetValueExA(hklm, "Value9", 0, REG_SZ, + (const BYTE *)path, lstrlenA(path) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + sprintf(path, "%s\\FileName2", CURR_DIR); + res = RegSetValueExA(hklm, "Value10", 0, REG_SZ, + (const BYTE *)path, lstrlenA(path) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + lstrcpyA(path, CURR_DIR); + res = RegSetValueExA(hklm, "Value11", 0, REG_SZ, + (const BYTE *)path, lstrlenA(path) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegSetValueExA(hklm, "Value12", 0, REG_SZ, + (const BYTE *)"", 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + create_file_with_version("FileName3.dll", MAKELONG(2, 1), MAKELONG(4, 3)); + sprintf(path, "%s\\FileName3.dll", CURR_DIR); + res = RegSetValueExA(hklm, "Value13", 0, REG_SZ, + (const BYTE *)path, lstrlenA(path) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + create_file_with_version("FileName4.dll", MAKELONG(1, 2), MAKELONG(3, 4)); + sprintf(path, "%s\\FileName4.dll", CURR_DIR); + res = RegSetValueExA(hklm, "Value14", 0, REG_SZ, + (const BYTE *)path, lstrlenA(path) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + create_file_with_version("FileName5.dll", MAKELONG(2, 1), MAKELONG(4, 3)); + sprintf(path, "%s\\FileName5.dll", CURR_DIR); + res = RegSetValueExA(hklm, "Value15", 0, REG_SZ, + (const BYTE *)path, lstrlenA(path) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + sprintf(path, "\"%s\\FileName1\" -option", CURR_DIR); + res = RegSetValueExA(hklm, "value16", 0, REG_SZ, + (const BYTE *)path, lstrlenA(path) + 1); + + space = (strchr(CURR_DIR, ' ')) ? TRUE : FALSE; + sprintf(path, "%s\\FileName1 -option", CURR_DIR); + res = RegSetValueExA(hklm, "value17", 0, REG_SZ, + (const BYTE *)path, lstrlenA(path) + 1); + + hdb = create_package_db(); + ok(hdb, "Expected a valid database handle\n"); + + r = create_appsearch_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP1', 'NewSignature1'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP2', 'NewSignature2'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP3', 'NewSignature3'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP4', 'NewSignature4'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP5', 'NewSignature5'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP6', 'NewSignature6'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP7', 'NewSignature7'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP8', 'NewSignature8'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP9', 'NewSignature9'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP10', 'NewSignature10'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP11', 'NewSignature11'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP12', 'NewSignature12'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP13', 'NewSignature13'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP14', 'NewSignature14'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP15', 'NewSignature15'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP16', 'NewSignature16'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP17', 'NewSignature17'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP18', 'NewSignature18'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP19', 'NewSignature19'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP20', 'NewSignature20'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP21', 'NewSignature21'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP22', 'NewSignature22'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP23', 'NewSignature23'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP24', 'NewSignature24'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP25', 'NewSignature25'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP26', 'NewSignature26'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP27', 'NewSignature27'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP28', 'NewSignature28'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP29', 'NewSignature29'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP30', 'NewSignature30'"); + 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); + + /* HKLM, msidbLocatorTypeRawValue, REG_SZ */ + str = "'NewSignature1', 2, 'Software\\Wine', 'Value1', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, positive DWORD */ + str = "'NewSignature2', 2, 'Software\\Wine', 'Value2', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, negative DWORD */ + str = "'NewSignature3', 2, 'Software\\Wine', 'Value3', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, REG_EXPAND_SZ */ + str = "'NewSignature4', 2, 'Software\\Wine', 'Value4', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, REG_EXPAND_SZ */ + str = "'NewSignature5', 2, 'Software\\Wine', 'Value5', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, REG_MULTI_SZ */ + str = "'NewSignature6', 2, 'Software\\Wine', 'Value6', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, REG_BINARY */ + str = "'NewSignature7', 2, 'Software\\Wine', 'Value7', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, REG_SZ first char is # */ + str = "'NewSignature8', 2, 'Software\\Wine', 'Value8', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFileName, signature, file exists */ + str = "'NewSignature9', 2, 'Software\\Wine', 'Value9', 1"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFileName, signature, file does not exist */ + str = "'NewSignature10', 2, 'Software\\Wine', 'Value10', 1"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFileName, no signature */ + str = "'NewSignature11', 2, 'Software\\Wine', 'Value9', 1"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeDirectory, no signature, file exists */ + str = "'NewSignature12', 2, 'Software\\Wine', 'Value9', 0"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeDirectory, no signature, directory exists */ + str = "'NewSignature13', 2, 'Software\\Wine', 'Value11', 0"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeDirectory, signature, file exists */ + str = "'NewSignature14', 2, 'Software\\Wine', 'Value9', 0"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKCR, msidbLocatorTypeRawValue, REG_SZ */ + str = "'NewSignature15', 0, 'Software\\Wine', 'Value1', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKCU, msidbLocatorTypeRawValue, REG_SZ */ + str = "'NewSignature16', 1, 'Software\\Wine', 'Value1', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKU, msidbLocatorTypeRawValue, REG_SZ */ + str = "'NewSignature17', 3, 'S-1-5-18\\Software\\Wine', 'Value1', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, REG_SZ, NULL Name */ + str = "'NewSignature18', 2, 'Software\\Wine', '', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, REG_SZ, key does not exist */ + str = "'NewSignature19', 2, 'Software\\IDontExist', '', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeRawValue, REG_SZ, value is empty */ + str = "'NewSignature20', 2, 'Software\\Wine', 'Value12', 2"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFileName, signature, file exists w/ version */ + str = "'NewSignature21', 2, 'Software\\Wine', 'Value13', 1"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFileName, file exists w/ version, version > max */ + str = "'NewSignature22', 2, 'Software\\Wine', 'Value14', 1"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFileName, file exists w/ version, sig->name ignored */ + str = "'NewSignature23', 2, 'Software\\Wine', 'Value15', 1"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFileName, no signature, directory exists */ + str = "'NewSignature24', 2, 'Software\\Wine', 'Value11', 1"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFileName, no signature, file does not exist */ + str = "'NewSignature25', 2, 'Software\\Wine', 'Value10', 1"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeDirectory, signature, directory exists */ + str = "'NewSignature26', 2, 'Software\\Wine', 'Value11', 0"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeDirectory, signature, file does not exist */ + str = "'NewSignature27', 2, 'Software\\Wine', 'Value10', 0"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeDirectory, no signature, file does not exist */ + str = "'NewSignature28', 2, 'Software\\Wine', 'Value10', 0"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFile, file exists, in quotes */ + str = "'NewSignature29', 2, 'Software\\Wine', 'Value16', 1"; + r = add_reglocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* HKLM, msidbLocatorTypeFile, file exists, no quotes */ + str = "'NewSignature30', 2, 'Software\\Wine', 'Value17', 1"; + r = add_reglocator_entry(hdb, str); + 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); + + str = "'NewSignature9', 'FileName1', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature10', 'FileName2', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature14', 'FileName1', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature21', 'FileName3.dll', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature22', 'FileName4.dll', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature23', 'ignored', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + ptr = strrchr(CURR_DIR, '\\') + 1; + sprintf(path, "'NewSignature26', '%s', '', '', '', '', '', '', ''", ptr); + r = add_signature_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature27', 'FileName2', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature29', 'FileName1', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature30', 'FileName1', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + hpkg = package_from_db(hdb); + ok(hpkg, "Expected a valid package handle\n"); + + r = MsiDoAction(hpkg, "AppSearch"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP1", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "regszdata"), + "Expected \"regszdata\", got \"%s\"\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP2", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "#42"), "Expected \"#42\", got \"%s\"\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP3", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "#-42"), "Expected \"#-42\", got \"%s\"\n", prop); + + ExpandEnvironmentStringsA("%PATH%", path, MAX_PATH); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP4", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP5", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, + "my%NOVAR%"), "Expected \"my%%NOVAR%%\", got \"%s\"\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP6", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!memcmp(prop, "\0one\0two\0\0", 10), + "Expected \"\\0one\\0two\\0\\0\"\n"); + } + + size = MAX_PATH; + lstrcpyA(path, "#xCDAB3412EF907856"); + r = MsiGetPropertyA(hpkg, "SIGPROP7", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP8", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "##regszdata"), + "Expected \"##regszdata\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName1", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP9", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP10", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP11", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP12", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP13", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP14", 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, "SIGPROP15", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "regszdata"), + "Expected \"regszdata\", got \"%s\"\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP16", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "regszdata"), + "Expected \"regszdata\", got \"%s\"\n", prop); + + if (users) + { + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP17", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "regszdata"), + "Expected \"regszdata\", got \"%s\"\n", prop); + } + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP18", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "defvalue"), + "Expected \"defvalue\", got \"%s\"\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP19", 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, "SIGPROP20", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + if (version) + { + size = MAX_PATH; + sprintf(path, "%s\\FileName3.dll", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP21", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP22", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName5.dll", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP23", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + } + + size = MAX_PATH; + lstrcpyA(path, CURR_DIR); + ptr = strrchr(path, '\\') + 1; + *ptr = '\0'; + r = MsiGetPropertyA(hpkg, "SIGPROP24", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP25", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP26", 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, "SIGPROP27", 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, "SIGPROP28", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName1", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP29", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName1", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP30", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + if (space) + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + else + todo_wine ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + RegSetValueA(hklm, NULL, REG_SZ, "", 0); + RegDeleteValueA(hklm, "Value1"); + RegDeleteValueA(hklm, "Value2"); + RegDeleteValueA(hklm, "Value3"); + RegDeleteValueA(hklm, "Value4"); + RegDeleteValueA(hklm, "Value5"); + RegDeleteValueA(hklm, "Value6"); + RegDeleteValueA(hklm, "Value7"); + RegDeleteValueA(hklm, "Value8"); + RegDeleteValueA(hklm, "Value9"); + RegDeleteValueA(hklm, "Value10"); + RegDeleteValueA(hklm, "Value11"); + RegDeleteValueA(hklm, "Value12"); + RegDeleteValueA(hklm, "Value13"); + RegDeleteValueA(hklm, "Value14"); + RegDeleteValueA(hklm, "Value15"); + RegDeleteValueA(hklm, "Value16"); + RegDeleteValueA(hklm, "Value17"); + RegDeleteKeyA(hklm, ""); + RegCloseKey(hklm); + + RegDeleteValueA(classes, "Value1"); + RegDeleteKeyA(classes, ""); + RegCloseKey(classes); + + RegDeleteValueA(hkcu, "Value1"); + RegDeleteKeyA(hkcu, ""); + RegCloseKey(hkcu); + + RegDeleteValueA(users, "Value1"); + RegDeleteKeyA(users, ""); + RegCloseKey(users); + + DeleteFileA("FileName1"); + DeleteFileA("FileName3.dll"); + DeleteFileA("FileName4.dll"); + DeleteFileA("FileName5.dll"); + MsiCloseHandle(hpkg); + DeleteFileA(msifile); +} + +static void delete_win_ini(LPCSTR file) +{ + CHAR path[MAX_PATH]; + + GetWindowsDirectoryA(path, MAX_PATH); + lstrcatA(path, "\\"); + lstrcatA(path, file); + + DeleteFileA(path); +} + +static void test_appsearch_inilocator(void) +{ + MSIHANDLE hpkg, hdb; + CHAR path[MAX_PATH]; + CHAR prop[MAX_PATH]; + BOOL version; + LPCSTR str; + LPSTR ptr; + DWORD size; + UINT r; + + version = TRUE; + if (!create_file_with_version("test.dll", MAKELONG(2, 1), MAKELONG(4, 3))) + version = FALSE; + + DeleteFileA("test.dll"); + + WritePrivateProfileStringA("Section", "Key", "keydata,field2", "IniFile.ini"); + + create_test_file("FileName1"); + sprintf(path, "%s\\FileName1", CURR_DIR); + WritePrivateProfileStringA("Section", "Key2", path, "IniFile.ini"); + + WritePrivateProfileStringA("Section", "Key3", CURR_DIR, "IniFile.ini"); + + sprintf(path, "%s\\IDontExist", CURR_DIR); + WritePrivateProfileStringA("Section", "Key4", path, "IniFile.ini"); + + create_file_with_version("FileName2.dll", MAKELONG(2, 1), MAKELONG(4, 3)); + sprintf(path, "%s\\FileName2.dll", CURR_DIR); + WritePrivateProfileStringA("Section", "Key5", path, "IniFile.ini"); + + create_file_with_version("FileName3.dll", MAKELONG(1, 2), MAKELONG(3, 4)); + sprintf(path, "%s\\FileName3.dll", CURR_DIR); + WritePrivateProfileStringA("Section", "Key6", path, "IniFile.ini"); + + create_file_with_version("FileName4.dll", MAKELONG(2, 1), MAKELONG(4, 3)); + sprintf(path, "%s\\FileName4.dll", CURR_DIR); + WritePrivateProfileStringA("Section", "Key7", path, "IniFile.ini"); + + hdb = create_package_db(); + ok(hdb, "Expected a valid database handle\n"); + + r = create_appsearch_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP1', 'NewSignature1'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP2', 'NewSignature2'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP3', 'NewSignature3'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP4', 'NewSignature4'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP5', 'NewSignature5'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP6', 'NewSignature6'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP7', 'NewSignature7'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP8', 'NewSignature8'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP9', 'NewSignature9'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP10', 'NewSignature10'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP11', 'NewSignature11'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP12', 'NewSignature12'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = create_inilocator_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeRawValue, field 1 */ + str = "'NewSignature1', 'IniFile.ini', 'Section', 'Key', 1, 2"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeRawValue, field 2 */ + str = "'NewSignature2', 'IniFile.ini', 'Section', 'Key', 2, 2"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeRawValue, entire field */ + str = "'NewSignature3', 'IniFile.ini', 'Section', 'Key', 0, 2"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeFile */ + str = "'NewSignature4', 'IniFile.ini', 'Section', 'Key2', 1, 1"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeDirectory, file */ + str = "'NewSignature5', 'IniFile.ini', 'Section', 'Key2', 1, 0"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeDirectory, directory */ + str = "'NewSignature6', 'IniFile.ini', 'Section', 'Key3', 1, 0"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeFile, file, no signature */ + str = "'NewSignature7', 'IniFile.ini', 'Section', 'Key2', 1, 1"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeFile, dir, no signature */ + str = "'NewSignature8', 'IniFile.ini', 'Section', 'Key3', 1, 1"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeFile, file does not exist */ + str = "'NewSignature9', 'IniFile.ini', 'Section', 'Key4', 1, 1"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeFile, signature with version */ + str = "'NewSignature10', 'IniFile.ini', 'Section', 'Key5', 1, 1"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeFile, signature with version, ver > max */ + str = "'NewSignature11', 'IniFile.ini', 'Section', 'Key6', 1, 1"; + r = add_inilocator_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* msidbLocatorTypeFile, signature with version, sig->name ignored */ + str = "'NewSignature12', 'IniFile.ini', 'Section', 'Key7', 1, 1"; + r = add_inilocator_entry(hdb, str); + 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, "'NewSignature4', 'FileName1', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature9', 'IDontExist', '', '', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature10', 'FileName2.dll', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature11', 'FileName3.dll', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_signature_entry(hdb, "'NewSignature12', 'ignored', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + hpkg = package_from_db(hdb); + ok(hpkg, "Expected a valid package handle\n"); + + r = MsiDoAction(hpkg, "AppSearch"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP1", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "keydata"), "Expected \"keydata\", got \"%s\"\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP2", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "field2"), "Expected \"field2\", got \"%s\"\n", prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP3", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, "keydata,field2"), + "Expected \"keydata,field2\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName1", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP4", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP5", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP6", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP7", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + lstrcpyA(path, CURR_DIR); + ptr = strrchr(path, '\\'); + *(ptr + 1) = '\0'; + r = MsiGetPropertyA(hpkg, "SIGPROP8", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP9", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + if (version) + { + size = MAX_PATH; + sprintf(path, "%s\\FileName2.dll", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP10", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP11", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\FileName4.dll", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP12", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + } + + delete_win_ini("IniFile.ini"); + DeleteFileA("FileName1"); + DeleteFileA("FileName2.dll"); + DeleteFileA("FileName3.dll"); + DeleteFileA("FileName4.dll"); + MsiCloseHandle(hpkg); + DeleteFileA(msifile); +} + +static void test_appsearch_drlocator(void) +{ + MSIHANDLE hpkg, hdb; + CHAR path[MAX_PATH]; + CHAR prop[MAX_PATH]; + BOOL version; + LPCSTR str; + DWORD size; + UINT r; + + version = TRUE; + if (!create_file_with_version("test.dll", MAKELONG(2, 1), MAKELONG(4, 3))) + version = FALSE; + + DeleteFileA("test.dll"); + + create_test_file("FileName1"); + CreateDirectoryA("one", NULL); + CreateDirectoryA("one\\two", NULL); + CreateDirectoryA("one\\two\\three", NULL); + create_test_file("one\\two\\three\\FileName2"); + CreateDirectoryA("another", NULL); + create_file_with_version("FileName3.dll", MAKELONG(2, 1), MAKELONG(4, 3)); + create_file_with_version("FileName4.dll", MAKELONG(1, 2), MAKELONG(3, 4)); + create_file_with_version("FileName5.dll", MAKELONG(2, 1), MAKELONG(4, 3)); + + hdb = create_package_db(); + ok(hdb, "Expected a valid database handle\n"); + + r = create_appsearch_table(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP1', 'NewSignature1'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP2', 'NewSignature2'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP3', 'NewSignature3'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP4', 'NewSignature4'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP5', 'NewSignature5'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP6', 'NewSignature6'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP7', 'NewSignature7'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP8', 'NewSignature8'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP9', 'NewSignature9'"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_appsearch_entry(hdb, "'SIGPROP10', 'NewSignature10'"); + 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); + + /* no parent, full path, depth 0, signature */ + sprintf(path, "'NewSignature1', '', '%s', 0", CURR_DIR); + r = add_drlocator_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* no parent, full path, depth 0, no signature */ + sprintf(path, "'NewSignature2', '', '%s', 0", CURR_DIR); + r = add_drlocator_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* no parent, relative path, depth 0, no signature */ + sprintf(path, "'NewSignature3', '', '%s', 0", CURR_DIR + 3); + r = add_drlocator_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* no parent, full path, depth 2, signature */ + sprintf(path, "'NewSignature4', '', '%s', 2", CURR_DIR); + r = add_drlocator_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* no parent, full path, depth 3, signature */ + sprintf(path, "'NewSignature5', '', '%s', 3", CURR_DIR); + r = add_drlocator_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* no parent, full path, depth 1, signature is dir */ + sprintf(path, "'NewSignature6', '', '%s', 1", CURR_DIR); + r = add_drlocator_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* parent is in DrLocator, relative path, depth 0, signature */ + sprintf(path, "'NewSignature7', 'NewSignature1', 'one\\two\\three', 1"); + r = add_drlocator_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* no parent, full path, depth 0, signature w/ version */ + sprintf(path, "'NewSignature8', '', '%s', 0", CURR_DIR); + r = add_drlocator_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* no parent, full path, depth 0, signature w/ version, ver > max */ + sprintf(path, "'NewSignature9', '', '%s', 0", CURR_DIR); + r = add_drlocator_entry(hdb, path); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* no parent, full path, depth 0, signature w/ version, sig->name not ignored */ + sprintf(path, "'NewSignature10', '', '%s', 0", CURR_DIR); + r = add_drlocator_entry(hdb, path); + 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); + + str = "'NewSignature1', 'FileName1', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature4', 'FileName2', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature5', 'FileName2', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature6', 'another', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature7', 'FileName2', '', '', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature8', 'FileName3.dll', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature9', 'FileName4.dll', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + str = "'NewSignature10', 'necessary', '1.1.1.1', '2.1.1.1', '', '', '', '', ''"; + r = add_signature_entry(hdb, str); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + hpkg = package_from_db(hdb); + ok(hpkg, "Expected a valid package handle\n"); + + r = MsiDoAction(hpkg, "AppSearch"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + sprintf(path, "%s\\FileName1", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP1", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP2", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + sprintf(path, "%s\\", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP3", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP4", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\one\\two\\three\\FileName2", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP5", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP6", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + + size = MAX_PATH; + sprintf(path, "%s\\one\\two\\three\\FileName2", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP7", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + if (version) + { + size = MAX_PATH; + sprintf(path, "%s\\FileName3.dll", CURR_DIR); + r = MsiGetPropertyA(hpkg, "SIGPROP8", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, path), "Expected \"%s\", got \"%s\"\n", path, prop); + + size = MAX_PATH; + r = MsiGetPropertyA(hpkg, "SIGPROP9", 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, "SIGPROP10", prop, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(prop, ""), "Expected \"\", got \"%s\"\n", prop); + } + + DeleteFileA("FileName1"); + DeleteFileA("FileName3.dll"); + DeleteFileA("FileName4.dll"); + DeleteFileA("FileName5.dll"); + DeleteFileA("one\\two\\three\\FileName2"); + RemoveDirectoryA("one\\two\\three"); + RemoveDirectoryA("one\\two"); + RemoveDirectoryA("one"); + RemoveDirectoryA("another"); + MsiCloseHandle(hpkg); + DeleteFileA(msifile); +} + static void test_featureparents(void) { MSIHANDLE hpkg; @@ -4858,168 +7667,6 @@ static void test_installprops(void) 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; @@ -5116,95 +7763,6 @@ static void test_ccpsearch(void) 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; @@ -5367,14 +7925,22 @@ static void test_complocator(void) 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}"); + set_component_path("abelisaurus", MSIINSTALLCONTEXT_MACHINE, + "{E3619EED-305A-418C-B9C7-F7D7377F0934}", NULL, FALSE); + set_component_path("bactrosaurus", MSIINSTALLCONTEXT_MACHINE, + "{D56B688D-542F-42Ef-90FD-B6DA76EE8119}", NULL, FALSE); + set_component_path("echinodon", MSIINSTALLCONTEXT_MACHINE, + "{A19E16C5-C75D-4699-8111-C4338C40C3CB}", NULL, FALSE); + set_component_path("falcarius", MSIINSTALLCONTEXT_MACHINE, + "{17762FA1-A7AE-4CC6-8827-62873C35361D}", NULL, FALSE); + set_component_path("iguanodon", MSIINSTALLCONTEXT_MACHINE, + "{8E0DA02E-F6A7-4A8F-B25D-6F564C492308}", NULL, FALSE); + set_component_path("jobaria", MSIINSTALLCONTEXT_MACHINE, + "{243C22B1-8C51-4151-B9D1-1AE5265E079E}", NULL, FALSE); + set_component_path("megaraptor", MSIINSTALLCONTEXT_MACHINE, + "{8B1034B7-BD5E-41ac-B52C-0105D3DFD74D}", NULL, FALSE); + set_component_path("neosodon", MSIINSTALLCONTEXT_MACHINE, + "{0B499649-197A-48EF-93D2-AF1C17ED6E90}", NULL, FALSE); r = MsiDoAction(hpkg, "AppSearch"); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); @@ -5492,14 +8058,1675 @@ static void test_complocator(void) 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}"); + delete_component_path("{E3619EED-305A-418C-B9C7-F7D7377F0934}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{D56B688D-542F-42Ef-90FD-B6DA76EE8119}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{A19E16C5-C75D-4699-8111-C4338C40C3CB}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{17762FA1-A7AE-4CC6-8827-62873C35361D}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{8E0DA02E-F6A7-4A8F-B25D-6F564C492308}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{243C22B1-8C51-4151-B9D1-1AE5265E079E}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{8B1034B7-BD5E-41ac-B52C-0105D3DFD74D}", + MSIINSTALLCONTEXT_MACHINE, NULL); + delete_component_path("{0B499649-197A-48EF-93D2-AF1C17ED6E90}", + MSIINSTALLCONTEXT_MACHINE, NULL); + DeleteFileA(msifile); +} + +static void set_suminfo_prop(MSIHANDLE db, DWORD prop, DWORD val) +{ + MSIHANDLE summary; + UINT r; + + r = MsiGetSummaryInformationA(db, NULL, 1, &summary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiSummaryInfoSetPropertyA(summary, prop, VT_I4, val, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiSummaryInfoPersist(summary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + MsiCloseHandle(summary); +} + +static void test_MsiGetSourcePath(void) +{ + MSIHANDLE hdb, hpkg; + CHAR path[MAX_PATH]; + CHAR cwd[MAX_PATH]; + CHAR subsrc[MAX_PATH]; + CHAR sub2[MAX_PATH]; + DWORD size; + UINT r; + + lstrcpyA(cwd, CURR_DIR); + lstrcatA(cwd, "\\"); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "subsource"); + lstrcatA(subsrc, "\\"); + + lstrcpyA(sub2, subsrc); + lstrcatA(sub2, "sub2"); + lstrcatA(sub2, "\\"); + + /* uncompressed source */ + + hdb = create_package_db(); + ok( hdb, "failed to create database\n"); + + set_suminfo_prop(hdb, PID_WORDCOUNT, 0); + + r = add_directory_entry(hdb, "'TARGETDIR', '', 'SourceDir'"); + ok(r == S_OK, "failed\n"); + + r = add_directory_entry(hdb, "'SubDir', 'TARGETDIR', 'subtarget:subsource'"); + ok(r == S_OK, "failed\n"); + + r = add_directory_entry(hdb, "'SubDir2', 'SubDir', 'sub2'"); + ok(r == S_OK, "failed\n"); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS , "Failed to commit database\n"); + + hpkg = package_from_db(hdb); + ok(hpkg, "failed to create package\n"); + + MsiCloseHandle(hdb); + + /* invalid database handle */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(-1, "TARGETDIR", path, &size); + ok(r == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* NULL szFolder */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, NULL, path, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* empty szFolder */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* try TARGETDIR */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "TARGETDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* try SourceDir */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* try SOURCEDIR */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* source path does not exist, but the property exists */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + /* try SubDir */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* try SubDir2 */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir2", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + r = MsiDoAction(hpkg, "CostInitialize"); + ok(r == ERROR_SUCCESS, "cost init failed\n"); + + /* try TARGETDIR after CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "TARGETDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SourceDir after CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SOURCEDIR after CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* source path does not exist, but the property exists */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + } + + /* try SubDir after CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + /* try SubDir2 after CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir2", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, sub2), "Expected \"%s\", got \"%s\"\n", sub2, path); + ok(size == lstrlenA(sub2), "Expected %d, got %d\n", lstrlenA(sub2), size); + + r = MsiDoAction(hpkg, "ResolveSource"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* try TARGETDIR after ResolveSource */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "TARGETDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SourceDir after ResolveSource */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SOURCEDIR after ResolveSource */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* source path does not exist, but the property exists */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SubDir after ResolveSource */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + /* try SubDir2 after ResolveSource */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir2", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, sub2), "Expected \"%s\", got \"%s\"\n", sub2, path); + ok(size == lstrlenA(sub2), "Expected %d, got %d\n", lstrlenA(sub2), size); + + r = MsiDoAction(hpkg, "FileCost"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* try TARGETDIR after FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "TARGETDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SourceDir after FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SOURCEDIR after FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* try SubDir after FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + /* try SubDir2 after FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir2", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, sub2), "Expected \"%s\", got \"%s\"\n", sub2, path); + ok(size == lstrlenA(sub2), "Expected %d, got %d\n", lstrlenA(sub2), size); + + r = MsiDoAction(hpkg, "CostFinalize"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* try TARGETDIR after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "TARGETDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SourceDir after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SOURCEDIR after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* try SubDir after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + /* try SubDir2 after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir2", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, sub2), "Expected \"%s\", got \"%s\"\n", sub2, path); + ok(size == lstrlenA(sub2), "Expected %d, got %d\n", lstrlenA(sub2), size); + + /* nonexistent directory */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "IDontExist", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* NULL szPathBuf */ + size = MAX_PATH; + r = MsiGetSourcePath(hpkg, "SourceDir", NULL, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* NULL pcchPathBuf */ + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, NULL); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + + /* pcchPathBuf is 0 */ + size = 0; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* pcchPathBuf does not have room for NULL terminator */ + size = lstrlenA(cwd); + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!strncmp(path, cwd, lstrlenA(cwd) - 1), + "Expected path with no backslash, got \"%s\"\n", path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* pcchPathBuf has room for NULL terminator */ + size = lstrlenA(cwd) + 1; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + MsiCloseHandle(hpkg); + + /* compressed source */ + + r = MsiOpenDatabase(msifile, MSIDBOPEN_DIRECT, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + set_suminfo_prop(hdb, PID_WORDCOUNT, msidbSumInfoSourceTypeCompressed); + + hpkg = package_from_db(hdb); + ok(hpkg, "failed to create package\n"); + + r = MsiDoAction(hpkg, "CostInitialize"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiDoAction(hpkg, "FileCost"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiDoAction(hpkg, "CostFinalize"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* try TARGETDIR after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "TARGETDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SourceDir after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SOURCEDIR after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + } + + /* try SubDir after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* try SubDir2 after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir2", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + MsiCloseHandle(hpkg); + DeleteFile(msifile); +} + +static void test_shortlongsource(void) +{ + MSIHANDLE hdb, hpkg; + CHAR path[MAX_PATH]; + CHAR cwd[MAX_PATH]; + CHAR subsrc[MAX_PATH]; + DWORD size; + UINT r; + + lstrcpyA(cwd, CURR_DIR); + lstrcatA(cwd, "\\"); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "long"); + lstrcatA(subsrc, "\\"); + + /* long file names */ + + hdb = create_package_db(); + ok( hdb, "failed to create database\n"); + + set_suminfo_prop(hdb, PID_WORDCOUNT, 0); + + r = add_directory_entry(hdb, "'TARGETDIR', '', 'SourceDir'"); + ok(r == S_OK, "failed\n"); + + r = add_directory_entry(hdb, "'SubDir', 'TARGETDIR', 'short|long'"); + ok(r == S_OK, "failed\n"); + + /* CostInitialize:short */ + r = add_directory_entry(hdb, "'SubDir2', 'TARGETDIR', 'one|two'"); + ok(r == S_OK, "failed\n"); + + /* CostInitialize:long */ + r = add_directory_entry(hdb, "'SubDir3', 'TARGETDIR', 'three|four'"); + ok(r == S_OK, "failed\n"); + + /* FileCost:short */ + r = add_directory_entry(hdb, "'SubDir4', 'TARGETDIR', 'five|six'"); + ok(r == S_OK, "failed\n"); + + /* FileCost:long */ + r = add_directory_entry(hdb, "'SubDir5', 'TARGETDIR', 'seven|eight'"); + ok(r == S_OK, "failed\n"); + + /* CostFinalize:short */ + r = add_directory_entry(hdb, "'SubDir6', 'TARGETDIR', 'nine|ten'"); + ok(r == S_OK, "failed\n"); + + /* CostFinalize:long */ + r = add_directory_entry(hdb, "'SubDir7', 'TARGETDIR', 'eleven|twelve'"); + ok(r == S_OK, "failed\n"); + + MsiDatabaseCommit(hdb); + + hpkg = package_from_db(hdb); + ok(hpkg, "failed to create package\n"); + + MsiCloseHandle(hdb); + + CreateDirectoryA("one", NULL); + CreateDirectoryA("four", NULL); + + r = MsiDoAction(hpkg, "CostInitialize"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + CreateDirectory("five", NULL); + CreateDirectory("eight", NULL); + + r = MsiDoAction(hpkg, "FileCost"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + CreateDirectory("nine", NULL); + CreateDirectory("twelve", NULL); + + r = MsiDoAction(hpkg, "CostFinalize"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* neither short nor long source directories exist */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + CreateDirectoryA("short", NULL); + + /* short source directory exists */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + CreateDirectoryA("long", NULL); + + /* both short and long source directories exist */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "two"); + lstrcatA(subsrc, "\\"); + + /* short dir exists before CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir2", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "four"); + lstrcatA(subsrc, "\\"); + + /* long dir exists before CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir3", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "six"); + lstrcatA(subsrc, "\\"); + + /* short dir exists before FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir4", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "eight"); + lstrcatA(subsrc, "\\"); + + /* long dir exists before FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir5", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "ten"); + lstrcatA(subsrc, "\\"); + + /* short dir exists before CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir6", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "twelve"); + lstrcatA(subsrc, "\\"); + + /* long dir exists before CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir7", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + MsiCloseHandle(hpkg); + RemoveDirectoryA("short"); + RemoveDirectoryA("long"); + RemoveDirectoryA("one"); + RemoveDirectoryA("four"); + RemoveDirectoryA("five"); + RemoveDirectoryA("eight"); + RemoveDirectoryA("nine"); + RemoveDirectoryA("twelve"); + + /* short file names */ + + r = MsiOpenDatabase(msifile, MSIDBOPEN_DIRECT, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + set_suminfo_prop(hdb, PID_WORDCOUNT, msidbSumInfoSourceTypeSFN); + + hpkg = package_from_db(hdb); + ok(hpkg, "failed to create package\n"); + + MsiCloseHandle(hdb); + + CreateDirectoryA("one", NULL); + CreateDirectoryA("four", NULL); + + r = MsiDoAction(hpkg, "CostInitialize"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + CreateDirectory("five", NULL); + CreateDirectory("eight", NULL); + + r = MsiDoAction(hpkg, "FileCost"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + CreateDirectory("nine", NULL); + CreateDirectory("twelve", NULL); + + r = MsiDoAction(hpkg, "CostFinalize"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "short"); + lstrcatA(subsrc, "\\"); + + /* neither short nor long source directories exist */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + CreateDirectoryA("short", NULL); + + /* short source directory exists */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + CreateDirectoryA("long", NULL); + + /* both short and long source directories exist */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "one"); + lstrcatA(subsrc, "\\"); + + /* short dir exists before CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir2", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "three"); + lstrcatA(subsrc, "\\"); + + /* long dir exists before CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir3", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "five"); + lstrcatA(subsrc, "\\"); + + /* short dir exists before FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir4", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "seven"); + lstrcatA(subsrc, "\\"); + + /* long dir exists before FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir5", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "nine"); + lstrcatA(subsrc, "\\"); + + /* short dir exists before CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir6", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "eleven"); + lstrcatA(subsrc, "\\"); + + /* long dir exists before CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SubDir7", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, subsrc), "Expected \"%s\", got \"%s\"\n", subsrc, path); + ok(size == lstrlenA(subsrc), "Expected %d, got %d\n", lstrlenA(subsrc), size); + + MsiCloseHandle(hpkg); + RemoveDirectoryA("short"); + RemoveDirectoryA("long"); + RemoveDirectoryA("one"); + RemoveDirectoryA("four"); + RemoveDirectoryA("five"); + RemoveDirectoryA("eight"); + RemoveDirectoryA("nine"); + RemoveDirectoryA("twelve"); + DeleteFileA(msifile); +} + +static void test_sourcedir(void) +{ + MSIHANDLE hdb, hpkg; + CHAR package[10]; + CHAR path[MAX_PATH]; + CHAR cwd[MAX_PATH]; + CHAR subsrc[MAX_PATH]; + DWORD size; + UINT r; + + lstrcpyA(cwd, CURR_DIR); + lstrcatA(cwd, "\\"); + + lstrcpyA(subsrc, cwd); + lstrcatA(subsrc, "long"); + lstrcatA(subsrc, "\\"); + + hdb = create_package_db(); + ok( hdb, "failed to create database\n"); + + r = add_directory_entry(hdb, "'TARGETDIR', '', 'SourceDir'"); + ok(r == S_OK, "failed\n"); + + sprintf(package, "#%li", hdb); + r = MsiOpenPackage(package, &hpkg); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* properties only */ + + /* SourceDir prop */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + /* SOURCEDIR prop */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + r = MsiDoAction(hpkg, "CostInitialize"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* SourceDir after CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + /* SOURCEDIR after CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + r = MsiDoAction(hpkg, "FileCost"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* SourceDir after FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + /* SOURCEDIR after FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + r = MsiDoAction(hpkg, "CostFinalize"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* SourceDir after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + /* SOURCEDIR after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + r = MsiDoAction(hpkg, "ResolveSource"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* SourceDir after ResolveSource */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SOURCEDIR after ResolveSource */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* random casing */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SoUrCeDiR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + + MsiCloseHandle(hpkg); + + /* reset the package state */ + sprintf(package, "#%li", hdb); + r = MsiOpenPackage(package, &hpkg); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* test how MsiGetSourcePath affects the properties */ + + /* SourceDir prop */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + } + + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* SourceDir after MsiGetSourcePath */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + } + + /* SOURCEDIR prop */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + } + + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* SOURCEDIR prop after MsiGetSourcePath */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + } + + r = MsiDoAction(hpkg, "CostInitialize"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* SourceDir after CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + todo_wine + { + ok(!lstrcmpA(path, ""), "Expected \"\", got \"%s\"\n", path); + ok(size == 0, "Expected 0, got %d\n", size); + } + + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SourceDir after MsiGetSourcePath */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SOURCEDIR after CostInitialize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SOURCEDIR source path still does not exist */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + r = MsiDoAction(hpkg, "FileCost"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* SourceDir after FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SOURCEDIR after FileCost */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SOURCEDIR source path still does not exist */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + r = MsiDoAction(hpkg, "CostFinalize"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* SourceDir after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SOURCEDIR after CostFinalize */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SOURCEDIR source path still does not exist */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + r = MsiDoAction(hpkg, "ResolveSource"); + ok(r == ERROR_SUCCESS, "file cost failed\n"); + + /* SourceDir after ResolveSource */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SourceDir", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SOURCEDIR after ResolveSource */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetProperty(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(path, cwd), "Expected \"%s\", got \"%s\"\n", cwd, path); + ok(size == lstrlenA(cwd), "Expected %d, got %d\n", lstrlenA(cwd), size); + + /* SOURCEDIR source path still does not exist */ + size = MAX_PATH; + lstrcpyA(path, "kiwi"); + r = MsiGetSourcePath(hpkg, "SOURCEDIR", path, &size); + ok(r == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", r); + ok(!lstrcmpA(path, "kiwi"), + "Expected path to be unchanged, got \"%s\"\n", path); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + MsiCloseHandle(hdb); + MsiCloseHandle(hpkg); + DeleteFileA(msifile); +} + +struct access_res +{ + BOOL gothandle; + DWORD lasterr; + BOOL ignore; +}; + +static const struct access_res create[16] = +{ + { TRUE, ERROR_SUCCESS, TRUE }, + { TRUE, ERROR_SUCCESS, TRUE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { TRUE, ERROR_SUCCESS, TRUE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { TRUE, ERROR_SUCCESS, TRUE } +}; + +static const struct access_res create_commit[16] = +{ + { TRUE, ERROR_SUCCESS, TRUE }, + { TRUE, ERROR_SUCCESS, TRUE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { TRUE, ERROR_SUCCESS, TRUE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { FALSE, ERROR_SHARING_VIOLATION, FALSE }, + { TRUE, ERROR_SUCCESS, TRUE } +}; + +static const struct access_res create_close[16] = +{ + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS, FALSE }, + { TRUE, ERROR_SUCCESS } +}; + +static void _test_file_access(LPCSTR file, const struct access_res *ares, DWORD line) +{ + DWORD access = 0, share = 0; + DWORD lasterr; + HANDLE hfile; + int i, j, idx = 0; + + for (i = 0; i < 4; i++) + { + if (i == 0) access = 0; + if (i == 1) access = GENERIC_READ; + if (i == 2) access = GENERIC_WRITE; + if (i == 3) access = GENERIC_READ | GENERIC_WRITE; + + for (j = 0; j < 4; j++) + { + if (ares[idx].ignore) + continue; + + if (j == 0) share = 0; + if (j == 1) share = FILE_SHARE_READ; + if (j == 2) share = FILE_SHARE_WRITE; + if (j == 3) share = FILE_SHARE_READ | FILE_SHARE_WRITE; + + SetLastError(0xdeadbeef); + hfile = CreateFileA(file, access, share, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0); + lasterr = GetLastError(); + + ok((hfile != INVALID_HANDLE_VALUE) == ares[idx].gothandle, + "(%d, handle, %d): Expected %d, got %d\n", + line, idx, ares[idx].gothandle, + (hfile != INVALID_HANDLE_VALUE)); + + ok(lasterr == ares[idx].lasterr || + lasterr == 0xdeadbeef, /* win9x */ + "(%d, lasterr, %d): Expected %d, got %d\n", + line, idx, ares[idx].lasterr, lasterr); + + CloseHandle(hfile); + idx++; + } + } +} + +#define test_file_access(file, ares) _test_file_access(file, ares, __LINE__) + +static void test_access(void) +{ + MSIHANDLE hdb; + UINT r; + + r = MsiOpenDatabaseA(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + test_file_access(msifile, create); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + test_file_access(msifile, create_commit); + + MsiCloseHandle(hdb); + + test_file_access(msifile, create_close); + + DeleteFileA(msifile); + + r = MsiOpenDatabaseA(msifile, MSIDBOPEN_CREATEDIRECT, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + test_file_access(msifile, create); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + test_file_access(msifile, create_commit); + + MsiCloseHandle(hdb); + + test_file_access(msifile, create_close); + + DeleteFileA(msifile); +} + +static void test_emptypackage(void) +{ + MSIHANDLE hpkg, hdb, hsuminfo; + MSIHANDLE hview, hrec; + MSICONDITION condition; + CHAR buffer[MAX_PATH]; + DWORD size; + UINT r; + + r = MsiOpenPackageA("", &hpkg); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + hdb = MsiGetActiveDatabase(hpkg); + todo_wine + { + ok(hdb != 0, "Expected a valid database handle\n"); + } + + r = MsiDatabaseOpenView(hdb, "SELECT * FROM `_Tables`", &hview); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + r = MsiViewExecute(hview, 0); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + r = MsiViewFetch(hview, &hrec); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 1, buffer, &size); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buffer, "_Property"), + "Expected \"_Property\", got \"%s\"\n", buffer); + } + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 1, buffer, &size); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buffer, "#_FolderCache"), + "Expected \"_Property\", got \"%s\"\n", buffer); + } + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + condition = MsiDatabaseIsTablePersistentA(hdb, "_Property"); + todo_wine + { + ok(condition == MSICONDITION_FALSE, + "Expected MSICONDITION_FALSE, got %d\n", condition); + } + + r = MsiDatabaseOpenView(hdb, "SELECT * FROM `_Property`", &hview); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + r = MsiViewExecute(hview, 0); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + /* _Property table is not empty */ + r = MsiViewFetch(hview, &hrec); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + condition = MsiDatabaseIsTablePersistentA(hdb, "#_FolderCache"); + todo_wine + { + ok(condition == MSICONDITION_FALSE, + "Expected MSICONDITION_FALSE, got %d\n", condition); + } + + r = MsiDatabaseOpenView(hdb, "SELECT * FROM `#_FolderCache`", &hview); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + r = MsiViewExecute(hview, 0); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + /* #_FolderCache is not empty */ + r = MsiViewFetch(hview, &hrec); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + r = MsiDatabaseOpenView(hdb, "SELECT * FROM `_Streams`", &hview); + todo_wine + { + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + } + + r = MsiDatabaseOpenView(hdb, "SELECT * FROM `_Storages`", &hview); + todo_wine + { + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + } + + r = MsiGetSummaryInformationA(hdb, NULL, 0, &hsuminfo); + todo_wine + { + ok(r == ERROR_INSTALL_PACKAGE_INVALID, + "Expected ERROR_INSTALL_PACKAGE_INVALID, got %d\n", r); + } + + MsiCloseHandle(hsuminfo); + + r = MsiDatabaseCommit(hdb); + todo_wine + { + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + } + + MsiCloseHandle(hdb); + MsiCloseHandle(hpkg); +} + +static void test_MsiGetProductProperty(void) +{ + MSIHANDLE hprod, hdb; + CHAR val[MAX_PATH]; + CHAR path[MAX_PATH]; + CHAR query[MAX_PATH]; + CHAR keypath[MAX_PATH*2]; + CHAR prodcode[MAX_PATH]; + CHAR prod_squashed[MAX_PATH]; + HKEY prodkey, userkey, props; + LPSTR usersid; + DWORD size; + LONG res; + UINT r; + SC_HANDLE scm; + + scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (!scm && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)) + { + win_skip("Different registry keys on Win9x and WinMe\n"); + return; + } + CloseServiceHandle(scm); + + GetCurrentDirectoryA(MAX_PATH, path); + lstrcatA(path, "\\"); + + create_test_guid(prodcode, prod_squashed); + get_user_sid(&usersid); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = set_summary_info(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = run_query(hdb, + "CREATE TABLE `Directory` ( " + "`Directory` CHAR(255) NOT NULL, " + "`Directory_Parent` CHAR(255), " + "`DefaultDir` CHAR(255) NOT NULL " + "PRIMARY KEY `Directory`)"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = run_query(hdb, + "CREATE TABLE `Property` ( " + "`Property` CHAR(72) NOT NULL, " + "`Value` CHAR(255) " + "PRIMARY KEY `Property`)"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sprintf(query, "INSERT INTO `Property` " + "(`Property`, `Value`) " + "VALUES( 'ProductCode', '%s' )", prodcode); + r = run_query(hdb, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hdb); + + 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); + + lstrcpyA(keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\"); + lstrcatA(keypath, "Installer\\UserData\\S-1-5-18\\Products\\"); + lstrcatA(keypath, prod_squashed); + + res = RegCreateKeyA(HKEY_LOCAL_MACHINE, keypath, &userkey); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = RegCreateKeyA(userkey, "InstallProperties", &props); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + lstrcpyA(val, path); + lstrcatA(val, "\\winetest.msi"); + res = RegSetValueExA(props, "LocalPackage", 0, REG_SZ, + (const BYTE *)val, lstrlenA(val) + 1); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + hprod = 0xdeadbeef; + r = MsiOpenProductA(prodcode, &hprod); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(hprod != 0 && hprod != 0xdeadbeef, "Expected a valid product handle\n"); + + /* hProduct is invalid */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(0xdeadbeef, "ProductCode", val, &size); + ok(r == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* szProperty is NULL */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, NULL, val, &size); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == MAX_PATH, "Expected size to be unchanged, got %d\n", size); + + /* szProperty is empty */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, "", val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, ""), "Expected \"\", got \"%s\"\n", val); + ok(size == 0, "Expected 0, got %d\n", size); + + /* get the property */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, "ProductCode", val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, val); + ok(size == lstrlenA(prodcode), + "Expected %d, got %d\n", lstrlenA(prodcode), size); + + /* lpValueBuf is NULL */ + size = MAX_PATH; + r = MsiGetProductPropertyA(hprod, "ProductCode", NULL, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(size == lstrlenA(prodcode), + "Expected %d, got %d\n", lstrlenA(prodcode), size); + + /* pcchValueBuf is NULL */ + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, "ProductCode", val, NULL); + ok(r == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", r); + ok(!lstrcmpA(val, "apple"), + "Expected val to be unchanged, got \"%s\"\n", val); + ok(size == lstrlenA(prodcode), + "Expected %d, got %d\n", lstrlenA(prodcode), size); + + /* pcchValueBuf is too small */ + size = 4; + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, "ProductCode", val, &size); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!strncmp(val, prodcode, 3), + "Expected first 3 chars of \"%s\", got \"%s\"\n", prodcode, val); + ok(size == lstrlenA(prodcode), + "Expected %d, got %d\n", lstrlenA(prodcode), size); + + /* pcchValueBuf does not leave room for NULL terminator */ + size = lstrlenA(prodcode); + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, "ProductCode", val, &size); + ok(r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r); + ok(!strncmp(val, prodcode, lstrlenA(prodcode) - 1), + "Expected first 37 chars of \"%s\", got \"%s\"\n", prodcode, val); + ok(size == lstrlenA(prodcode), + "Expected %d, got %d\n", lstrlenA(prodcode), size); + + /* pcchValueBuf has enough room for NULL terminator */ + size = lstrlenA(prodcode) + 1; + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, "ProductCode", val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, val); + ok(size == lstrlenA(prodcode), + "Expected %d, got %d\n", lstrlenA(prodcode), size); + + /* nonexistent property */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, "IDontExist", val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, ""), "Expected \"\", got \"%s\"\n", val); + ok(size == 0, "Expected 0, got %d\n", size); + + r = MsiSetPropertyA(hprod, "NewProperty", "value"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* non-product property set */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, "NewProperty", val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, ""), "Expected \"\", got \"%s\"\n", val); + ok(size == 0, "Expected 0, got %d\n", size); + + r = MsiSetPropertyA(hprod, "ProductCode", "value"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* non-product property that is also a product property set */ + size = MAX_PATH; + lstrcpyA(val, "apple"); + r = MsiGetProductPropertyA(hprod, "ProductCode", val, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(val, prodcode), + "Expected \"%s\", got \"%s\"\n", prodcode, val); + ok(size == lstrlenA(prodcode), + "Expected %d, got %d\n", lstrlenA(prodcode), size); + + MsiCloseHandle(hprod); + + RegDeleteValueA(props, "LocalPackage"); + RegDeleteKeyA(props, ""); + RegCloseKey(props); + RegDeleteKeyA(userkey, ""); + RegCloseKey(userkey); + RegDeleteKeyA(prodkey, ""); + RegCloseKey(prodkey); DeleteFileA(msifile); } @@ -5508,8 +9735,6 @@ START_TEST(package) GetCurrentDirectoryA(MAX_PATH, CURR_DIR); test_createpackage(); - test_getsourcepath_bad(); - test_getsourcepath(); test_doaction(); test_gettargetpath_bad(); test_settargetpath(); @@ -5522,11 +9747,19 @@ START_TEST(package) test_getproperty(); test_removefiles(); test_appsearch(); + test_appsearch_complocator(); + test_appsearch_reglocator(); + test_appsearch_inilocator(); + test_appsearch_drlocator(); test_featureparents(); test_installprops(); - test_sourcedirprop(); - test_prop_path(); test_launchconditions(); test_ccpsearch(); test_complocator(); + test_MsiGetSourcePath(); + test_shortlongsource(); + test_sourcedir(); + test_access(); + test_emptypackage(); + test_MsiGetProductProperty(); } diff --git a/rostests/winetests/msi/record.c b/rostests/winetests/msi/record.c index 471fc9ce372..29a57a04f30 100644 --- a/rostests/winetests/msi/record.c +++ b/rostests/winetests/msi/record.c @@ -361,6 +361,11 @@ static void test_MsiRecordGetString(void) rec = MsiCreateRecord(2); ok(rec != 0, "Expected a valid handle\n"); + sz = MAX_PATH; + r = MsiRecordGetString(rec, 1, NULL, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r); + ok(sz == 0, "Expected 0, got %d\n",sz); + sz = MAX_PATH; lstrcpyA(buf, "apple"); r = MsiRecordGetString(rec, 1, buf, &sz); @@ -376,10 +381,71 @@ static void test_MsiRecordGetString(void) ok(sz == 0, "Expected 0, got %d\n", sz); MsiCloseHandle(rec); + + rec = MsiCreateRecord(1); + ok(rec != 0, "Expected a valid handle\n"); + + r = MsiRecordSetInteger(rec, 1, 5); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + r = MsiRecordGetString(rec, 1, NULL, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r); + ok(sz == 1, "Expected 1, got %d\n",sz); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "5"), "Expected \"5\", got \"%s\"\n", buf); + ok(sz == 1, "Expectd 1, got %d\n", sz); + + r = MsiRecordSetInteger(rec, 1, -5); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "-5"), "Expected \"-5\", got \"%s\"\n", buf); + ok(sz == 2, "Expectd 2, got %d\n", sz); + + MsiCloseHandle(rec); +} + +static void test_MsiRecordGetInteger(void) +{ + MSIHANDLE rec; + INT val; + UINT r; + + rec = MsiCreateRecord(1); + ok(rec != 0, "Expected a valid handle\n"); + + r = MsiRecordSetString(rec, 1, "5"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(rec, 1); + ok(val == 5, "Expected 5, got %d\n", val); + + r = MsiRecordSetString(rec, 1, "-5"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(rec, 1); + ok(val == -5, "Expected -5, got %d\n", val); + + r = MsiRecordSetString(rec, 1, "5apple"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(rec, 1); + ok(val == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", val); + + MsiCloseHandle(rec); } START_TEST(record) { test_msirecord(); test_MsiRecordGetString(); + test_MsiRecordGetInteger(); } diff --git a/rostests/winetests/msi/source.c b/rostests/winetests/msi/source.c index 148ea40382c..9248b709c17 100644 --- a/rostests/winetests/msi/source.c +++ b/rostests/winetests/msi/source.c @@ -130,6 +130,8 @@ static int get_user_sid(LPSTR *usersid) PTOKEN_USER user; BOOL rc; + if (!pConvertSidToStringSidA) + return 0; rc=OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token); if (!rc && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) return 0; @@ -2864,13 +2866,10 @@ static void test_MsiSourceListEnumMediaDisks(void) prompt, &promptsz); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); ok(id == 1, "Expected 1, got %d\n", id); - todo_wine - { - ok(!lstrcmpA(label, "#42"), "Expected \"#42\", got \"%s\"\n", label); - ok(labelsz == 3, "Expected 3, got %d\n", labelsz); - ok(!lstrcmpA(prompt, "#42"), "Expected \"#42\", got \"%s\"\n", prompt); - ok(promptsz == 3, "Expected 3, got %d\n", promptsz); - } + ok(!lstrcmpA(label, "#42"), "Expected \"#42\", got \"%s\"\n", label); + ok(labelsz == 3, "Expected 3, got %d\n", labelsz); + ok(!lstrcmpA(prompt, "#42"), "Expected \"#42\", got \"%s\"\n", prompt); + ok(promptsz == 3, "Expected 3, got %d\n", promptsz); RegDeleteValueA(media, "1"); RegDeleteValueA(media, "2");