[PATCH 3/5] msi: Implement UI messages for dialogs.
Zebediah Figura
z.figura12 at gmail.com
Wed Jul 5 23:31:53 CDT 2017
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
dlls/msi/action.c | 74 +++++++++++++--------------
dlls/msi/custom.c | 2 +-
dlls/msi/dialog.c | 55 +++++++++++++++++++-
dlls/msi/msi.rc | 3 +-
dlls/msi/msipriv.h | 1 +
dlls/msi/package.c | 7 +++
dlls/msi/resource.h | 1 +
dlls/msi/tests/package.c | 130 +++++++++++++++++++++++++++++++++++++++++++++--
8 files changed, 225 insertions(+), 48 deletions(-)
diff --git a/dlls/msi/action.c b/dlls/msi/action.c
index e016b43..fef076f 100644
--- a/dlls/msi/action.c
+++ b/dlls/msi/action.c
@@ -158,15 +158,17 @@ static const WCHAR szValidateProductID[] =
static const WCHAR szWriteEnvironmentStrings[] =
{'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t','S','t','r','i','n','g','s',0};
-static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template)
+static INT ui_actionstart(MSIPACKAGE *package, LPCWSTR action, LPCWSTR description, LPCWSTR template)
{
MSIRECORD *row = MSI_CreateRecord(3);
- if (!row) return;
+ INT rc;
+ if (!row) return -1;
MSI_RecordSetStringW(row, 1, action);
MSI_RecordSetStringW(row, 2, description);
MSI_RecordSetStringW(row, 3, template);
- MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
+ rc = MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
msiobj_release(&row->hdr);
+ return rc;
}
static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
@@ -608,22 +610,26 @@ static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
/********************************************************
* ACTION helper functions and functions that perform the actions
*******************************************************/
-static BOOL ACTION_HandleCustomAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc, UINT script )
+static UINT ACTION_HandleCustomAction(MSIPACKAGE *package, LPCWSTR action, UINT script)
{
- BOOL ret=FALSE;
UINT arc;
+ INT uirc;
- ui_actionstart(package, action, NULL, NULL);
+ uirc = ui_actionstart(package, action, NULL, NULL);
+ if (uirc == IDCANCEL)
+ return ERROR_INSTALL_USEREXIT;
ui_actioninfo(package, action, TRUE, 0);
arc = ACTION_CustomAction( package, action, script );
+
+ if (arc == ERROR_FUNCTION_NOT_CALLED && needs_ui_sequence(package))
+ arc = ACTION_ShowDialog(package, action);
+
+ if (arc == ERROR_INSTALL_USEREXIT) /* dialog UI returned -1 */
+ return ERROR_SUCCESS;
+
ui_actioninfo(package, action, FALSE, arc);
- if (arc != ERROR_CALL_NOT_IMPLEMENTED)
- {
- *rc = arc;
- ret = TRUE;
- }
- return ret;
+ return arc;
}
MSICOMPONENT *msi_get_loaded_component( MSIPACKAGE *package, const WCHAR *Component )
@@ -7771,9 +7777,9 @@ StandardActions[] =
{ 0 }
};
-static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UINT *rc )
+static UINT ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action)
{
- BOOL ret = FALSE;
+ UINT rc = ERROR_FUNCTION_NOT_CALLED;
UINT i;
i = 0;
@@ -7792,8 +7798,8 @@ static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UI
if (StandardActions[i].handler)
{
ui_actioninfo( package, action, TRUE, 0 );
- *rc = StandardActions[i].handler( package );
- ui_actioninfo( package, action, FALSE, *rc );
+ rc = StandardActions[i].handler( package );
+ ui_actioninfo( package, action, FALSE, rc );
if (StandardActions[i].action_rollback && !package->need_rollback)
{
@@ -7804,58 +7810,46 @@ static BOOL ACTION_HandleStandardAction( MSIPACKAGE *package, LPCWSTR action, UI
else
{
FIXME("unhandled standard action %s\n", debugstr_w(action));
- *rc = ERROR_SUCCESS;
+ rc = ERROR_SUCCESS;
}
- ret = TRUE;
break;
}
i++;
}
- return ret;
+ return rc;
}
UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
{
- UINT rc = ERROR_SUCCESS;
- BOOL handled;
+ UINT rc;
TRACE("Performing action (%s)\n", debugstr_w(action));
- handled = ACTION_HandleStandardAction(package, action, &rc);
+ rc = ACTION_HandleStandardAction(package, action);
- if (!handled)
- handled = ACTION_HandleCustomAction(package, action, &rc, script);
+ if (rc == ERROR_FUNCTION_NOT_CALLED)
+ rc = ACTION_HandleCustomAction(package, action, script);
- if (!handled)
- {
+ if (rc == ERROR_FUNCTION_NOT_CALLED)
WARN("unhandled msi action %s\n", debugstr_w(action));
- rc = ERROR_FUNCTION_NOT_CALLED;
- }
return rc;
}
UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
{
- UINT rc = ERROR_SUCCESS;
- BOOL handled = FALSE;
+ UINT rc;
TRACE("Performing action (%s)\n", debugstr_w(action));
package->action_progress_increment = 0;
- handled = ACTION_HandleStandardAction(package, action, &rc);
-
- if (!handled)
- handled = ACTION_HandleCustomAction(package, action, &rc, script);
+ rc = ACTION_HandleStandardAction(package, action);
- if( !handled && ACTION_DialogBox(package, action) == ERROR_SUCCESS )
- handled = TRUE;
+ if (rc == ERROR_FUNCTION_NOT_CALLED)
+ rc = ACTION_HandleCustomAction(package, action, script);
- if (!handled)
- {
+ if (rc == ERROR_FUNCTION_NOT_CALLED)
WARN("unhandled msi action %s\n", debugstr_w(action));
- rc = ERROR_FUNCTION_NOT_CALLED;
- }
return rc;
}
diff --git a/dlls/msi/custom.c b/dlls/msi/custom.c
index 35a1b5c..922927e 100644
--- a/dlls/msi/custom.c
+++ b/dlls/msi/custom.c
@@ -1215,7 +1215,7 @@ UINT ACTION_CustomAction( MSIPACKAGE *package, LPCWSTR action, UINT script )
row = MSI_QueryGetRecord( package->db, query, action );
if (!row)
- return ERROR_CALL_NOT_IMPLEMENTED;
+ return ERROR_FUNCTION_NOT_CALLED;
type = MSI_RecordGetInteger(row,2);
source = MSI_RecordGetString(row,3);
diff --git a/dlls/msi/dialog.c b/dlls/msi/dialog.c
index fc9c24c..93a3bad 100644
--- a/dlls/msi/dialog.c
+++ b/dlls/msi/dialog.c
@@ -30,7 +30,6 @@
#include "winuser.h"
#include "winnls.h"
#include "msi.h"
-#include "msipriv.h"
#include "msidefs.h"
#include "ocidl.h"
#include "olectl.h"
@@ -38,12 +37,15 @@
#include "commctrl.h"
#include "winreg.h"
#include "shlwapi.h"
-#include "msiserver.h"
#include "shellapi.h"
#include "wine/debug.h"
#include "wine/unicode.h"
+#include "msipriv.h"
+#include "msiserver.h"
+#include "resource.h"
+
WINE_DEFAULT_DEBUG_CHANNEL(msi);
extern HINSTANCE msi_hInstance;
@@ -4508,6 +4510,44 @@ static UINT event_reset( msi_dialog *dialog, const WCHAR *argument )
return ERROR_SUCCESS;
}
+UINT ACTION_ShowDialog( MSIPACKAGE *package, const WCHAR *dialog )
+{
+ static const WCHAR szDialog[] = {'D','i','a','l','o','g',0};
+ MSIRECORD *row;
+ INT rc;
+
+ if (!TABLE_Exists(package->db, szDialog)) return ERROR_FUNCTION_NOT_CALLED;
+
+ row = MSI_CreateRecord(0);
+ if (!row) return ERROR_OUTOFMEMORY;
+ MSI_RecordSetStringW(row, 0, dialog);
+ rc = MSI_ProcessMessage(package, INSTALLMESSAGE_SHOWDIALOG, row);
+ msiobj_release(&row->hdr);
+ if (rc == -1) return ERROR_INSTALL_USEREXIT;
+
+ if (!rc)
+ {
+ static const WCHAR szActionNotFound[] =
+ {'D','E','B','U','G',':',' ','E','r','r','o','r',' ','[','1',']',':',' ',' ',
+ 'A','c','t','i','o','n',' ','n','o','t',' ','f','o','u','n','d',':',' ','[','2',']',0};
+ WCHAR template[1024];
+ MSIRECORD *row = MSI_CreateRecord(2);
+ if (!row) return -1;
+ MSI_RecordSetStringW(row, 0, szActionNotFound); /* FIXME: this shouldn't attach "Info [1]." */
+ MSI_RecordSetInteger(row, 1, 2726);
+ MSI_RecordSetStringW(row, 2, dialog);
+ MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
+
+ LoadStringW(msi_hInstance, IDS_INSTALLERROR, template, 1024);
+ MSI_RecordSetStringW(row, 0, template);
+ MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
+
+ msiobj_release(&row->hdr);
+ return ERROR_FUNCTION_NOT_CALLED;
+ }
+ return ERROR_SUCCESS;
+}
+
/* Return ERROR_SUCCESS if dialog is process and ERROR_FUNCTION_FAILED
* if the given parameter is not a dialog box
*/
@@ -4536,6 +4576,17 @@ UINT ACTION_DialogBox( MSIPACKAGE *package, const WCHAR *dialog )
msi_free( name );
}
if (r == ERROR_IO_PENDING) r = ERROR_SUCCESS;
+ if (r == ERROR_SUCCESS)
+ {
+ static const WCHAR szDialogCreated[] =
+ {'D','i','a','l','o','g',' ','c','r','e','a','t','e','d',0};
+ MSIRECORD *row = MSI_CreateRecord(2);
+ if (!row) return ERROR_OUTOFMEMORY;
+ MSI_RecordSetStringW(row, 1, dialog);
+ MSI_RecordSetStringW(row, 2, szDialogCreated);
+ MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
+ msiobj_release(&row->hdr);
+ }
return r;
}
diff --git a/dlls/msi/msi.rc b/dlls/msi/msi.rc
index 5bd9956..2c86fae 100644
--- a/dlls/msi/msi.rc
+++ b/dlls/msi/msi.rc
@@ -72,12 +72,13 @@ STRINGTABLE
IDS_COMMONDATA "Message type: [1], Argument: [2]{, [3]}"
}
-/* INSTALLMESSAGE_INFO strings */
+/* Install message template strings */
STRINGTABLE
{
IDS_INFO_ACTIONSTART "Action start %s: [1]."
IDS_INFO_ACTIONENDED "Action ended %s: [1]. Return value [2]."
IDS_INFO_LOGGINGSTART "=== Logging started: %s %s ==="
+ IDS_INSTALLERROR "The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is [1]. {{The arguments are: [2], [3], [4]}}"
}
/* Standard action description strings */
diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h
index cc42316..9271065 100644
--- a/dlls/msi/msipriv.h
+++ b/dlls/msi/msipriv.h
@@ -800,6 +800,7 @@ extern void msi_free_patchinfo( MSIPATCHINFO *patch ) DECLSPEC_HIDDEN;
/* action internals */
extern UINT MSI_InstallPackage( MSIPACKAGE *, LPCWSTR, LPCWSTR ) DECLSPEC_HIDDEN;
+extern UINT ACTION_ShowDialog( MSIPACKAGE*, LPCWSTR) DECLSPEC_HIDDEN;
extern UINT ACTION_DialogBox( MSIPACKAGE*, LPCWSTR) DECLSPEC_HIDDEN;
extern UINT ACTION_ForceReboot(MSIPACKAGE *package) DECLSPEC_HIDDEN;
extern UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable ) DECLSPEC_HIDDEN;
diff --git a/dlls/msi/package.c b/dlls/msi/package.c
index e762f62..bd11656 100644
--- a/dlls/msi/package.c
+++ b/dlls/msi/package.c
@@ -1788,6 +1788,13 @@ static INT internal_ui_handler(MSIPACKAGE *package, INSTALLMESSAGE eMessageType,
case INSTALLMESSAGE_INITIALIZE:
case INSTALLMESSAGE_TERMINATE:
return 0;
+ case INSTALLMESSAGE_SHOWDIALOG:
+ {
+ LPWSTR dialog = msi_dup_record_field(record, 0);
+ UINT rc = ACTION_DialogBox(package, dialog);
+ msi_free(dialog);
+ return (rc == ERROR_SUCCESS);
+ }
case INSTALLMESSAGE_ACTIONSTART:
{
LPWSTR deformatted;
diff --git a/dlls/msi/resource.h b/dlls/msi/resource.h
index 92fed33..6392c91 100644
--- a/dlls/msi/resource.h
+++ b/dlls/msi/resource.h
@@ -23,6 +23,7 @@
#define IDS_INFO_ACTIONSTART 1050
#define IDS_INFO_ACTIONENDED 1051
#define IDS_INFO_LOGGINGSTART 1052
+#define IDS_INSTALLERROR 1053
#define IDS_DESC_ALLOCATEREGISTRYSPACE 1100
#define IDS_DESC_APPSEARCH 1101
diff --git a/dlls/msi/tests/package.c b/dlls/msi/tests/package.c
index 44d5513..f1e85c6 100644
--- a/dlls/msi/tests/package.c
+++ b/dlls/msi/tests/package.c
@@ -642,6 +642,42 @@ static UINT create_custom_action_table( MSIHANDLE hdb )
"PRIMARY KEY `Action`)" );
}
+static UINT create_dialog_table( MSIHANDLE hdb )
+{
+ return run_query(hdb,
+ "CREATE TABLE `Dialog` ("
+ "`Dialog` CHAR(72) NOT NULL, "
+ "`HCentering` SHORT NOT NULL, "
+ "`VCentering` SHORT NOT NULL, "
+ "`Width` SHORT NOT NULL, "
+ "`Height` SHORT NOT NULL, "
+ "`Attributes` LONG, "
+ "`Title` CHAR(128) LOCALIZABLE, "
+ "`Control_First` CHAR(50) NOT NULL, "
+ "`Control_Default` CHAR(50), "
+ "`Control_Cancel` CHAR(50) "
+ "PRIMARY KEY `Dialog`)");
+}
+
+static UINT create_control_table( MSIHANDLE hdb )
+{
+ return run_query(hdb,
+ "CREATE TABLE `Control` ("
+ "`Dialog_` CHAR(72) NOT NULL, "
+ "`Control` CHAR(50) NOT NULL, "
+ "`Type` CHAR(20) NOT NULL, "
+ "`X` SHORT NOT NULL, "
+ "`Y` SHORT NOT NULL, "
+ "`Width` SHORT NOT NULL, "
+ "`Height` SHORT NOT NULL, "
+ "`Attributes` LONG, "
+ "`Property` CHAR(50), "
+ "`Text` CHAR(0) LOCALIZABLE, "
+ "`Control_Next` CHAR(50), "
+ "`Help` CHAR(255) LOCALIZABLE "
+ "PRIMARY KEY `Dialog_`, `Control`)");
+}
+
#define make_add_entry(type, qtext) \
static UINT add##_##type##_##entry( MSIHANDLE hdb, const char *values ) \
{ \
@@ -9330,6 +9366,40 @@ static const struct externalui_message doaction_custom_sequence[] = {
{0}
};
+static const struct externalui_message doaction_custom_fullui_sequence[] = {
+ {INSTALLMESSAGE_ACTIONSTART, 3, {"", "custom", "", ""}, {0, 1, 1, 1}},
+ {INSTALLMESSAGE_INFO, 2, {"", "custom", ""}, {0, 1, 1}},
+ {INSTALLMESSAGE_SHOWDIALOG, 0, {"custom"}, {1}},
+ {INSTALLMESSAGE_INFO, 2, {"", "custom", "1"}, {0, 1, 1}},
+ {0}
+};
+
+static const struct externalui_message doaction_dialog_nonexistent_sequence[] = {
+ {INSTALLMESSAGE_ACTIONSTART, 3, {"", "custom", "", ""}, {0, 1, 1, 1}},
+ {INSTALLMESSAGE_INFO, 2, {"", "custom", "1"}, {0, 1, 1}},
+ {INSTALLMESSAGE_SHOWDIALOG, 0, {"custom"}, {1}},
+ {INSTALLMESSAGE_INFO, 2, {"DEBUG: Error [1]: Action not found: [2]", "2726", "custom"}, {1, 1, 1}},
+ {INSTALLMESSAGE_INFO, 2, {"", "2726", "custom"}, {0, 1, 1}},
+ {INSTALLMESSAGE_INFO, 2, {"", "custom", "0"}, {0, 1, 1}},
+ {0}
+};
+
+static const struct externalui_message doaction_dialog_sequence[] = {
+ {INSTALLMESSAGE_ACTIONSTART, 3, {"", "dialog", "", ""}, {0, 1, 1, 1}},
+ {INSTALLMESSAGE_INFO, 2, {"", "dialog", "0"}, {0, 1, 1}},
+ {INSTALLMESSAGE_SHOWDIALOG, 0, {"dialog"}, {1}},
+ {INSTALLMESSAGE_ACTIONSTART, 2, {"", "dialog", "Dialog created"}, {0, 1, 1}},
+ {INSTALLMESSAGE_INFO, 2, {"", "dialog", "1"}, {0, 1, 1}},
+ {0}
+};
+
+static const struct externalui_message doaction_dialog_error_sequence[] = {
+ {INSTALLMESSAGE_ACTIONSTART, 3, {"", "error", "", ""}, {0, 1, 1, 1}},
+ {INSTALLMESSAGE_INFO, 2, {"", "error", "1"}, {0, 1, 1}},
+ {INSTALLMESSAGE_SHOWDIALOG, 0, {"error"}, {1}},
+ {0}
+};
+
static const struct externalui_message closehandle_sequence[] = {
{INSTALLMESSAGE_TERMINATE, -1},
{0}
@@ -9337,6 +9407,7 @@ static const struct externalui_message closehandle_sequence[] = {
static INT CALLBACK externalui_message_string_callback(void *context, UINT message, LPCSTR string)
{
+ INT retval = context ? *((INT *)context) : 0;
struct externalui_message msg;
msg.message = message;
@@ -9344,11 +9415,12 @@ static INT CALLBACK externalui_message_string_callback(void *context, UINT messa
strcpy(msg.field[0], string);
add_message(&msg);
- return 1;
+ return retval;
}
static INT CALLBACK externalui_message_callback(void *context, UINT message, MSIHANDLE hrecord)
{
+ INT retval = context ? *((INT *)context) : 0;
struct externalui_message msg;
char buffer[100];
DWORD length = 100;
@@ -9372,7 +9444,7 @@ static INT CALLBACK externalui_message_callback(void *context, UINT message, MSI
add_message(&msg);
- return 1;
+ return retval;
}
static void test_externalui_message(void)
@@ -9381,11 +9453,14 @@ static void test_externalui_message(void)
INSTALLUI_HANDLER_RECORD prev;
MSIHANDLE hdb, hpkg;
+ INT retval = 1;
UINT r;
+ MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
+
/* processing SHOWDIALOG with a record handler causes a crash on XP */
- MsiSetExternalUIA(externalui_message_string_callback, INSTALLLOGMODE_SHOWDIALOG, NULL);
- r = pMsiSetExternalUIRecord(externalui_message_callback, 0xffffffff ^ INSTALLLOGMODE_PROGRESS ^ INSTALLLOGMODE_SHOWDIALOG, NULL, &prev);
+ MsiSetExternalUIA(externalui_message_string_callback, INSTALLLOGMODE_SHOWDIALOG, &retval);
+ r = pMsiSetExternalUIRecord(externalui_message_callback, 0xffffffff ^ INSTALLLOGMODE_PROGRESS ^ INSTALLLOGMODE_SHOWDIALOG, &retval, &prev);
flush_sequence();
@@ -9430,6 +9505,53 @@ static void test_externalui_message(void)
MsiCloseHandle(hpkg);
ok_sequence(closehandle_sequence, "MsiCloseHandle()", FALSE);
+ /* Test dialogs */
+ hdb = create_package_db();
+ ok(hdb, "failed to create database\n");
+
+ r = MsiDatabaseImportA(hdb, CURR_DIR, "forcecodepage.idt");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d", r);
+
+ 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')");
+ 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)");
+ ok(r == ERROR_SUCCESS, "failed to insert into control table %u", r);
+
+ r = package_from_db(hdb, &hpkg);
+ ok(r == ERROR_SUCCESS, "failed to create package %u\n", r);
+ ok_sequence(openpackage_sequence, "MsiOpenPackage with valid db", TRUE);
+
+ /* Test a custom action */
+ r = MsiDoActionA(hpkg, "custom");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ ok_sequence(doaction_custom_fullui_sequence, "MsiDoAction(\"custom\")", TRUE);
+
+ retval = 0;
+ r = MsiDoActionA(hpkg, "custom");
+ ok(r == ERROR_FUNCTION_NOT_CALLED, "Expected ERROR_FUNCTION_NOT_CALLED, got %d\n", r);
+ ok_sequence(doaction_dialog_nonexistent_sequence, "MsiDoAction(\"custom\")", TRUE);
+
+ r = MsiDoActionA(hpkg, "dialog");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ ok_sequence(doaction_dialog_sequence, "MsiDoAction(\"dialog\")", TRUE);
+
+ retval = -1;
+ r = MsiDoActionA(hpkg, "error");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ ok_sequence(doaction_dialog_error_sequence, "MsiDoAction(\"error\")", FALSE);
+
+ MsiCloseHandle(hpkg);
+ ok_sequence(closehandle_sequence, "MsiCloseHandle()", TRUE);
+
CoUninitialize();
DeleteFileA(msifile);
DeleteFileA("forcecodepage.idt");
--
2.7.4
More information about the wine-patches
mailing list