msi: Process ShowDialog/EndDialog after all other control events.

Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2017-07-20 23:43:13 -05:00 committed by Alexandre Julliard
parent 44fb23dc32
commit 8826584b1d
2 changed files with 199 additions and 11 deletions

View File

@ -55,6 +55,7 @@ typedef struct msi_control_tag msi_control;
typedef UINT (*msi_handler)( msi_dialog *, msi_control *, WPARAM );
typedef void (*msi_update)( msi_dialog *, msi_control * );
typedef UINT (*control_event_handler)( msi_dialog *, const WCHAR *, const WCHAR * );
typedef UINT (*event_handler)( msi_dialog *, const WCHAR * );
struct msi_control_tag
{
@ -100,6 +101,8 @@ struct msi_dialog_tag
HWND hWndFocus;
LPWSTR control_default;
LPWSTR control_cancel;
event_handler pending_event;
LPWSTR pending_argument;
WCHAR name[1];
};
@ -993,6 +996,16 @@ static UINT msi_dialog_button_handler( msi_dialog *dialog, msi_control *control,
}
r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog );
msiobj_release( &view->hdr );
/* dialog control events must be processed last regardless of ordering */
if (dialog->pending_event)
{
r = dialog->pending_event( dialog, dialog->pending_argument );
msi_free( dialog->pending_argument );
dialog->pending_event = NULL;
dialog->pending_argument = NULL;
}
return r;
}
@ -4317,8 +4330,6 @@ UINT WINAPI MsiPreviewBillboardA( MSIHANDLE hPreview, LPCSTR szControlName, LPCS
return ERROR_CALL_NOT_IMPLEMENTED;
}
typedef UINT (*event_handler)( msi_dialog *, const WCHAR * );
struct control_event
{
const WCHAR *event;
@ -4386,6 +4397,14 @@ static UINT event_end_dialog( msi_dialog *dialog, const WCHAR *argument )
return ERROR_SUCCESS;
}
static UINT pending_event_end_dialog( msi_dialog *dialog, const WCHAR *argument )
{
dialog->pending_event = event_end_dialog;
if (dialog->pending_argument) msi_free( dialog->pending_argument );
dialog->pending_argument = strdupW( argument );
return ERROR_SUCCESS;
}
/* transition from one modal dialog to another modal dialog */
static UINT event_new_dialog( msi_dialog *dialog, const WCHAR *argument )
{
@ -4396,6 +4415,14 @@ static UINT event_new_dialog( msi_dialog *dialog, const WCHAR *argument )
return ERROR_SUCCESS;
}
static UINT pending_event_new_dialog( msi_dialog *dialog, const WCHAR *argument )
{
dialog->pending_event = event_new_dialog;
if (dialog->pending_argument) msi_free( dialog->pending_argument );
dialog->pending_argument = strdupW( argument );
return ERROR_SUCCESS;
}
/* create a new child dialog of an existing modal dialog */
static UINT event_spawn_dialog( msi_dialog *dialog, const WCHAR *argument )
{
@ -4409,6 +4436,14 @@ static UINT event_spawn_dialog( msi_dialog *dialog, const WCHAR *argument )
return ERROR_SUCCESS;
}
static UINT pending_event_spawn_dialog( msi_dialog *dialog, const WCHAR *argument )
{
dialog->pending_event = event_spawn_dialog;
if (dialog->pending_argument) msi_free( dialog->pending_argument );
dialog->pending_argument = strdupW( argument );
return ERROR_SUCCESS;
}
/* creates a dialog that remains up for a period of time based on a condition */
static UINT event_spawn_wait_dialog( msi_dialog *dialog, const WCHAR *argument )
{
@ -4634,9 +4669,9 @@ static const WCHAR validate_product_idW[] = {'V','a','l','i','d','a','t','e','P'
static const struct control_event control_events[] =
{
{ end_dialogW, event_end_dialog },
{ new_dialogW, event_new_dialog },
{ spawn_dialogW, event_spawn_dialog },
{ end_dialogW, pending_event_end_dialog },
{ new_dialogW, pending_event_new_dialog },
{ spawn_dialogW, pending_event_spawn_dialog },
{ spawn_wait_dialogW, event_spawn_wait_dialog },
{ do_actionW, event_do_action },
{ add_localW, event_add_local },

View File

@ -674,6 +674,19 @@ static UINT create_control_table( MSIHANDLE hdb )
"PRIMARY KEY `Dialog_`, `Control`)");
}
static UINT create_controlevent_table( MSIHANDLE hdb )
{
return run_query(hdb,
"CREATE TABLE `ControlEvent` ("
"`Dialog_` CHAR(72) NOT NULL, "
"`Control_` CHAR(50) NOT NULL, "
"`Event` CHAR(50) NOT NULL, "
"`Argument` CHAR(255) NOT NULL, "
"`Condition` CHAR(255), "
"`Ordering` SHORT "
"PRIMARY KEY `Dialog_`, `Control_`, `Event`, `Argument`, `Condition`)");
}
#define make_add_entry(type, qtext) \
static UINT add##_##type##_##entry( MSIHANDLE hdb, const char *values ) \
{ \
@ -757,6 +770,18 @@ make_add_entry(custom_action,
"INSERT INTO `CustomAction` "
"(`Action`, `Type`, `Source`, `Target`) VALUES( %s )")
make_add_entry(dialog,
"INSERT INTO `Dialog` "
"(`Dialog`, `HCentering`, `VCentering`, `Width`, `Height`, `Attributes`, `Control_First`) VALUES ( %s )")
make_add_entry(control,
"INSERT INTO `Control` "
"(`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Text`) VALUES( %s )");
make_add_entry(controlevent,
"INSERT INTO `ControlEvent` "
"(`Dialog_`, `Control_`, `Event`, `Argument`, `Condition`, `Ordering`) VALUES( %s )");
static UINT add_reglocator_entry( MSIHANDLE hdb, const char *sig, UINT root, const char *path,
const char *name, UINT type )
{
@ -9562,16 +9587,12 @@ static void test_externalui_message(void)
r = create_dialog_table(hdb);
ok(r == ERROR_SUCCESS, "failed to create dialog table %u\n", r);
r = run_query(hdb, "INSERT INTO `Dialog` (`Dialog`, `HCentering`, "
"`VCentering`, `Width`, `Height`, `Control_First`) "
"VALUES ('dialog', 5, 5, 100, 100, 'dummy')");
r = add_dialog_entry(hdb, "'dialog', 50, 50, 100, 100, 0, 'dummy'");
ok(r == ERROR_SUCCESS, "failed to insert into dialog table %u\n", r);
r = create_control_table(hdb);
ok(r == ERROR_SUCCESS, "failed to create control table %u\n", r);
r = run_query(hdb, "INSERT INTO `Control` (`Dialog_`, `Control`, "
"`Type`, `X`, `Y`, `Width`, `Height`) "
"VALUES('dialog', 'dummy', 'Text', 5, 5, 5, 5)");
r = add_control_entry(hdb, "'dialog', 'dummy', 'Text', 5, 5, 5, 5, 3, 'dummy'");
ok(r == ERROR_SUCCESS, "failed to insert into control table %u", r);
r = package_from_db(hdb, &hpkg);
@ -9630,6 +9651,137 @@ static void test_externalui_message(void)
DeleteFileA("forcecodepage.idt");
}
static const struct externalui_message controlevent_spawn_sequence[] = {
{INSTALLMESSAGE_ACTIONSTART, 3, {"", "spawn", "", ""}, {0, 1, 1, 1}},
{INSTALLMESSAGE_INFO, 2, {"", "spawn", ""}, {0, 1, 1}},
{INSTALLMESSAGE_SHOWDIALOG, 0, {"spawn"}, {1}},
{INSTALLMESSAGE_ACTIONSTART, 2, {"", "spawn", "Dialog created"}, {0, 1, 1}},
{INSTALLMESSAGE_ACTIONSTART, 3, {"", "custom", "", ""}, {0, 1, 1, 1}},
{INSTALLMESSAGE_INFO, 2, {"", "custom", ""}, {0, 1, 1}},
{INSTALLMESSAGE_INFO, 2, {"", "custom", "1"}, {0, 1, 1}},
{INSTALLMESSAGE_ACTIONSTART, 2, {"", "child1", "Dialog created"}, {0, 1, 1}},
{INSTALLMESSAGE_INFO, 2, {"", "spawn", "2"}, {0, 1, 1}},
{0}
};
static const struct externalui_message controlevent_spawn2_sequence[] = {
{INSTALLMESSAGE_ACTIONSTART, 3, {"", "spawn2", "", ""}, {0, 1, 1, 1}},
{INSTALLMESSAGE_INFO, 2, {"", "spawn2", "2"}, {0, 1, 1}},
{INSTALLMESSAGE_SHOWDIALOG, 0, {"spawn2"}, {1}},
{INSTALLMESSAGE_ACTIONSTART, 2, {"", "spawn2", "Dialog created"}, {0, 1, 1}},
{INSTALLMESSAGE_ACTIONSTART, 3, {"", "custom", "", ""}, {0, 1, 1, 1}},
{INSTALLMESSAGE_INFO, 2, {"", "custom", "2"}, {0, 1, 1}},
{INSTALLMESSAGE_INFO, 2, {"", "custom", "1"}, {0, 1, 1}},
{INSTALLMESSAGE_ACTIONSTART, 2, {"", "child2", "Dialog created"}, {0, 1, 1}},
{INSTALLMESSAGE_INFO, 2, {"", "spawn2", "2"}, {0, 1, 1}},
{0}
};
static void test_controlevent(void)
{
INSTALLUI_HANDLER_RECORD prev;
MSIHANDLE hdb, hpkg;
UINT r;
if (!winetest_interactive)
{
skip("interactive ControlEvent tests\n");
return;
}
MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
/* processing SHOWDIALOG with a record handler causes a crash on XP */
MsiSetExternalUIA(externalui_message_string_callback, INSTALLLOGMODE_SHOWDIALOG, NULL);
r = MsiSetExternalUIRecord(externalui_message_callback, 0xffffffff ^ INSTALLLOGMODE_PROGRESS ^ INSTALLLOGMODE_SHOWDIALOG, NULL, &prev);
flush_sequence();
CoInitialize(NULL);
hdb = create_package_db();
ok(hdb, "failed to create database\n");
create_file_data("forcecodepage.idt", "\r\n\r\n1252\t_ForceCodepage\r\n");
r = MsiDatabaseImportA(hdb, CURR_DIR, "forcecodepage.idt");
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
r = create_dialog_table(hdb);
ok(r == ERROR_SUCCESS, "failed to create Dialog table: %u\n", r);
r = add_dialog_entry(hdb, "'spawn', 50, 50, 100, 100, 3, 'button'");
ok(r == ERROR_SUCCESS, "failed to insert into Dialog table: %u\n", r);
r = add_dialog_entry(hdb, "'spawn2', 50, 50, 100, 100, 3, 'button'");
ok(r == ERROR_SUCCESS, "failed to insert into Dialog table: %u\n", r);
r = add_dialog_entry(hdb, "'child1', 50, 50, 80, 40, 3, 'exit'");
ok(r == ERROR_SUCCESS, "failed to insert into Dialog table: %u\n", r);
r = add_dialog_entry(hdb, "'child2', 50, 50, 80, 40, 3, 'exit'");
ok(r == ERROR_SUCCESS, "failed to insert into Dialog table: %u\n", r);
r = create_control_table(hdb);
ok(r == ERROR_SUCCESS, "failed to create Control table: %u\n", r);
r = add_control_entry(hdb, "'spawn', 'button', 'PushButton', 10, 10, 66, 17, 3, 'Click me'");
ok(r == ERROR_SUCCESS, "failed to insert into Control table: %u\n", r);
r = add_control_entry(hdb, "'spawn2', 'button', 'PushButton', 10, 10, 66, 17, 3, 'Click me'");
ok(r == ERROR_SUCCESS, "failed to insert into Control table: %u\n", r);
r = add_control_entry(hdb, "'child1', 'exit', 'PushButton', 10, 10, 66, 17, 3, 'Click me'");
ok(r == ERROR_SUCCESS, "failed to insert into Control table: %u\n", r);
r = add_control_entry(hdb, "'child2', 'exit', 'PushButton', 10, 10, 66, 17, 3, 'Click me'");
ok(r == ERROR_SUCCESS, "failed to insert into Control table: %u\n", r);
r = create_controlevent_table(hdb);
ok(r == ERROR_SUCCESS, "failed to create ControlEvent table: %u\n", r);
r = add_controlevent_entry(hdb, "'child1', 'exit', 'EndDialog', 'Exit', 1, 1");
ok(r == ERROR_SUCCESS, "failed to insert into ControlEvent table: %u\n", r);
r = add_controlevent_entry(hdb, "'child2', 'exit', 'EndDialog', 'Exit', 1, 1");
ok(r == ERROR_SUCCESS, "failed to insert into ControlEvent table: %u\n", r);
r = create_custom_action_table(hdb);
ok(r == ERROR_SUCCESS, "failed to create CustomAction table: %u\n", r);
r = add_custom_action_entry(hdb, "'custom', 51, 'dummy', 'dummy value'");
ok(r == ERROR_SUCCESS, "failed to insert into CustomAction table: %u\n", r);
/* SpawnDialog and EndDialog should trigger after all other events */
r = add_controlevent_entry(hdb, "'spawn', 'button', 'SpawnDialog', 'child1', 1, 1");
ok(r == ERROR_SUCCESS, "failed to insert into ControlEvent table: %u\n", r);
r = add_controlevent_entry(hdb, "'spawn', 'button', 'DoAction', 'custom', 1, 2");
ok(r == ERROR_SUCCESS, "failed to insert into ControlEvent table: %u\n", r);
/* Multiple dialog events cause only the last one to be triggered */
r = add_controlevent_entry(hdb, "'spawn2', 'button', 'SpawnDialog', 'child1', 1, 1");
ok(r == ERROR_SUCCESS, "failed to insert into ControlEvent table: %u\n", r);
r = add_controlevent_entry(hdb, "'spawn2', 'button', 'SpawnDialog', 'child2', 1, 2");
ok(r == ERROR_SUCCESS, "failed to insert into ControlEvent table: %u\n", r);
r = add_controlevent_entry(hdb, "'spawn2', 'button', 'DoAction', 'custom', 1, 3");
ok(r == ERROR_SUCCESS, "failed to insert into ControlEvent table: %u\n", r);
r = package_from_db(hdb, &hpkg);
ok(r == ERROR_SUCCESS, "failed to create package: %u\n", r);
ok_sequence(openpackage_sequence, "MsiOpenPackage()", FALSE);
r = MsiDoActionA(hpkg, "spawn");
todo_wine
ok(r == ERROR_INSTALL_USEREXIT, "expected ERROR_INSTALL_USEREXIT, got %u\n", r);
ok_sequence(controlevent_spawn_sequence, "control event: spawn", TRUE);
r = MsiDoActionA(hpkg, "spawn2");
todo_wine
ok(r == ERROR_INSTALL_USEREXIT, "expected ERROR_INSTALL_USEREXIT, got %u\n", r);
ok_sequence(controlevent_spawn2_sequence, "control event: spawn2", TRUE);
MsiCloseHandle(hpkg);
ok_sequence(closehandle_sequence, "MsiCloseHandle()", FALSE);
CoUninitialize();
DeleteFileA(msifile);
DeleteFileA("forcecodepage.idt");
}
START_TEST(package)
{
STATEMGRSTATUS status;
@ -9689,6 +9841,7 @@ START_TEST(package)
test_MsiDatabaseCommit();
test_externalui();
test_externalui_message();
test_controlevent();
if (pSRSetRestorePointA && !pMsiGetComponentPathExA && ret)
{