msi: hook up dialogs and control events (big)

Aric Stewart aric at codeweavers.com
Thu May 26 12:57:03 CDT 2005


Hook up the dialog code, get a basic UI and handle basic control events.
-------------- next part --------------
Index: dlls/msi/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/msi/Makefile.in,v
retrieving revision 1.26
diff -u -r1.26 Makefile.in
--- dlls/msi/Makefile.in	9 May 2005 14:42:33 -0000	1.26
+++ dlls/msi/Makefile.in	26 May 2005 16:16:03 -0000
@@ -16,6 +16,7 @@
 	delete.c \
 	dialog.c \
 	distinct.c \
+	events.c \
 	format.c \
 	handle.c \
 	insert.c \
Index: dlls/msi/action.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/action.c,v
retrieving revision 1.123
diff -u -r1.123 action.c
--- dlls/msi/action.c	26 May 2005 16:08:58 -0000	1.123
+++ dlls/msi/action.c	26 May 2005 16:16:06 -0000
@@ -685,6 +685,21 @@
     HeapFree(GetProcessHeap(),0,package->CommitAction);
 
     HeapFree(GetProcessHeap(),0,package->PackagePath);
+
+    /* cleanup control event subscriptions */
+    ControlEvent_CleanupSubscriptions(package);
+}
+
+static void ce_actiontext(MSIPACKAGE* package, LPCWSTR action)
+{
+    static const WCHAR szActionText[] = 
+        {'A','c','t','i','o','n','T','e','x','t',0};
+    MSIRECORD *row;
+
+    row = MSI_CreateRecord(1);
+    MSI_RecordSetStringW(row,1,action);
+    ControlEvent_FireSubscribedEvent(package,szActionText, row);
+    msiobj_release(&row->hdr);
 }
 
 static void ui_progress(MSIPACKAGE *package, int a, int b, int c, int d )
@@ -714,6 +729,8 @@
     MSIQUERY * view;
     MSIRECORD * row = 0;
     DWORD size;
+    static const WCHAR szActionData[] = 
+        {'A','c','t','i','o','n','D','a','t','a',0};
 
     if (!package->LastAction || strcmpW(package->LastAction,action))
     {
@@ -764,6 +781,9 @@
     MSI_RecordSetStringW(row,1,message);
  
     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
+
+    ControlEvent_FireSubscribedEvent(package,szActionData, row);
+
     msiobj_release(&row->hdr);
 }
 
@@ -1403,6 +1423,7 @@
     {
         if (strcmpW(StandardActions[i].action, action)==0)
         {
+            ce_actiontext(package, action);
             ui_actioninfo(package, action, TRUE, 0);
             ui_actionstart(package, action);
             if (StandardActions[i].handler)
@@ -1427,15 +1448,11 @@
 {
     BOOL ret = FALSE;
 
-    /*
-     * for the UI when we get that working
-     *
     if (ACTION_DialogBox(package,dialog) == ERROR_SUCCESS)
     {
         *rc = package->CurrentInstallState;
         ret = TRUE;
     }
-    */
     return ret;
 }
 
Index: dlls/msi/action.h
===================================================================
RCS file: /home/wine/wine/dlls/msi/action.h,v
retrieving revision 1.9
diff -u -r1.9 action.h
--- dlls/msi/action.h	26 May 2005 16:08:58 -0000	1.9
+++ dlls/msi/action.h	26 May 2005 16:16:06 -0000
@@ -196,3 +196,8 @@
 int get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature );
 int get_loaded_file(MSIPACKAGE* package, LPCWSTR file);
 int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path);
+
+/* control event stuff */
+VOID ControlEvent_FireSubscribedEvent(MSIPACKAGE *package, LPCWSTR event,
+                                      MSIRECORD *data);
+VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package);
Index: dlls/msi/msipriv.h
===================================================================
RCS file: /home/wine/wine/dlls/msi/msipriv.h,v
retrieving revision 1.58
diff -u -r1.58 msipriv.h
--- dlls/msi/msipriv.h	26 May 2005 12:24:28 -0000	1.58
+++ dlls/msi/msipriv.h	26 May 2005 16:16:06 -0000
@@ -226,6 +226,7 @@
     LPWSTR next_dialog;
 
     BOOL ExecuteSequenceRun;
+    struct _subscriptions *EventSubscriptions;
 } MSIPACKAGE;
 
 typedef struct tagMSIPREVIEW
--- /dev/null	2005-03-17 08:20:53.000000000 -0600
+++ dlls/msi/events.c	2005-05-26 10:31:14.000000000 -0500
@@ -0,0 +1,506 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+/*
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/controlevent_overview.asp
+*/
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "fdi.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msvcrt/fcntl.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "wine/unicode.h"
+#include "ver.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef void (*EVENTHANDLER)(MSIPACKAGE*,LPCWSTR,msi_dialog *);
+
+struct _events{
+    LPSTR event;
+    EVENTHANDLER handler;
+};
+
+struct _subscriber{
+    LPWSTR control;
+    LPWSTR attribute;
+    struct _subscriber *next;
+};
+
+struct _subscription_chain {
+    LPWSTR event;
+    struct _subscriber *chain;
+};
+
+struct _subscriptions {
+    DWORD chain_count; 
+    struct _subscription_chain* chain;
+};
+
+static VOID ControlEvent_EndDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog* dialog);
+static VOID ControlEvent_NewDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog* dialog);
+static VOID ControlEvent_SpawnDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                              msi_dialog* dialog);
+static VOID ControlEvent_SpawnWaitDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                                  msi_dialog* dialog);
+static VOID ControlEvent_DoAction(MSIPACKAGE* package, LPCWSTR argument, 
+                                  msi_dialog* dialog);
+static VOID ControlEvent_AddLocal(MSIPACKAGE* package, LPCWSTR argument, 
+                                  msi_dialog* dialog);
+static VOID ControlEvent_Remove(MSIPACKAGE* package, LPCWSTR argument, 
+                                msi_dialog* dialog);
+static VOID ControlEvent_AddSource(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog* dialog);
+
+struct _events Events[] = {
+    { "EndDialog",ControlEvent_EndDialog},
+    { "NewDialog",ControlEvent_NewDialog},
+    { "SpawnDialog",ControlEvent_SpawnDialog},
+    { "SpawnWaitDialog",ControlEvent_SpawnWaitDialog},
+    { "DoAction",ControlEvent_DoAction},
+    { "AddLocal",ControlEvent_AddLocal},
+    { "Remove",ControlEvent_Remove},
+    { "AddSource",ControlEvent_AddSource},
+    { NULL,NULL},
+    };
+
+VOID ControlEvent_HandleControlEvent(MSIPACKAGE *package, LPCWSTR event,
+                                     LPCWSTR argument, msi_dialog* dialog)
+{
+    int i = 0;
+
+    TRACE("Handling Control Event %s\n",debugstr_w(event));
+    if (!event)
+        return;
+
+    while( Events[i].event != NULL)
+    {
+        LPWSTR wevent = strdupAtoW(Events[i].event);
+        if (strcmpW(wevent,event)==0)
+        {
+            HeapFree(GetProcessHeap(),0,wevent);
+            Events[i].handler(package,argument,dialog);
+            return;
+        }
+        HeapFree(GetProcessHeap(),0,wevent);
+        i++;
+    }
+    FIXME("UNHANDLED control event %s arg(%s)\n",debugstr_w(event), 
+                                                 debugstr_w(argument));
+}
+
+
+/*
+ * Create a dialog box and run it if it's modal
+ */
+static UINT event_do_dialog( MSIPACKAGE *package, LPCWSTR name )
+{
+    msi_dialog *dialog;
+    UINT r;
+
+    /* kill the current modeless dialog */
+    if( package->dialog )
+        msi_dialog_destroy( package->dialog );
+    package->dialog = NULL;
+
+    /* create a new dialog */
+    dialog = msi_dialog_create( package, name,
+                                ControlEvent_HandleControlEvent );
+    if( dialog )
+    {
+        /* modeless dialogs return an error message */
+        r = msi_dialog_run_message_loop( dialog );
+        if( r == ERROR_SUCCESS )
+            msi_dialog_destroy( dialog );
+        else
+            package->dialog = dialog;
+    }
+    else
+        r = ERROR_FUNCTION_FAILED;
+
+    return r;
+}
+
+
+/*
+ * End a modal dialog box
+ */
+static VOID ControlEvent_EndDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog* dialog)
+{
+    static const WCHAR szExit[] = {
+    'E','x','i','t',0};
+    static const WCHAR szRetry[] = {
+    'R','e','t','r','y',0};
+    static const WCHAR szIgnore[] = {
+    'I','g','n','o','r','e',0};
+    static const WCHAR szReturn[] = {
+    'R','e','t','u','r','n',0};
+
+    if (strcmpW(argument,szExit)==0)
+        package->CurrentInstallState = ERROR_INSTALL_USEREXIT;
+    else if (strcmpW(argument, szRetry) == 0)
+        package->CurrentInstallState = ERROR_INSTALL_SUSPEND;
+    else if (strcmpW(argument, szIgnore) == 0)
+        package->CurrentInstallState = -1;
+    else if (strcmpW(argument, szReturn) == 0)
+        package->CurrentInstallState = ERROR_SUCCESS;
+    else
+    {
+        ERR("Unknown argument string %s\n",debugstr_w(argument));
+        package->CurrentInstallState = ERROR_FUNCTION_FAILED;
+    }
+
+    msi_dialog_end_dialog( dialog );
+}
+
+/*
+ * transition from one modal dialog to another modal dialog
+ */
+static VOID ControlEvent_NewDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog *dialog)
+{
+    /* store the name of the next dialog, and signal this one to end */
+    package->next_dialog = strdupW(argument);
+    msi_dialog_end_dialog( dialog );
+}
+
+/*
+ * Create a new child dialog of an existing modal dialog
+ */
+static VOID ControlEvent_SpawnDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                              msi_dialog *dialog)
+{
+    event_do_dialog( package, argument );
+    if( package->CurrentInstallState != ERROR_SUCCESS )
+        msi_dialog_end_dialog( dialog );
+}
+
+/*
+ * Creates a dialog that remains up for a period of time
+ * based on a condition
+ */
+static VOID ControlEvent_SpawnWaitDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                                  msi_dialog* dialog)
+{
+    FIXME("Doing Nothing\n");
+}
+
+static VOID ControlEvent_DoAction(MSIPACKAGE* package, LPCWSTR argument, 
+                                  msi_dialog* dialog)
+{
+    ACTION_PerformAction(package,argument);
+}
+
+static VOID ControlEvent_AddLocal(MSIPACKAGE* package, LPCWSTR argument, 
+                                  msi_dialog* dialog)
+{
+    static const WCHAR szAll[] = {'A','L','L',0};
+    int i;
+
+    if (strcmpW(szAll,argument))
+    {
+        MSI_SetFeatureStateW(package,argument,INSTALLSTATE_LOCAL);
+    }
+    else
+    {
+        for (i = 0; i < package->loaded_features; i++)
+        {
+            package->features[i].ActionRequest = INSTALLSTATE_LOCAL;
+            package->features[i].Action = INSTALLSTATE_LOCAL;
+        }
+        ACTION_UpdateComponentStates(package,argument);
+    }
+
+}
+
+static VOID ControlEvent_Remove(MSIPACKAGE* package, LPCWSTR argument, 
+                                msi_dialog* dialog)
+{
+    static const WCHAR szAll[] = {'A','L','L',0};
+    int i;
+
+    if (strcmpW(szAll,argument))
+    {
+        MSI_SetFeatureStateW(package,argument,INSTALLSTATE_ABSENT);
+    }
+    else
+    {
+        for (i = 0; i < package->loaded_features; i++)
+        {
+            package->features[i].ActionRequest = INSTALLSTATE_ABSENT;
+            package->features[i].Action= INSTALLSTATE_ABSENT;
+        }
+        ACTION_UpdateComponentStates(package,argument);
+    }
+}
+
+static VOID ControlEvent_AddSource(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog* dialog)
+{
+    static const WCHAR szAll[] = {'A','L','L',0};
+    int i;
+
+    if (strcmpW(szAll,argument))
+    {
+        MSI_SetFeatureStateW(package,argument,INSTALLSTATE_SOURCE);
+    }
+    else
+    {
+        for (i = 0; i < package->loaded_features; i++)
+        {
+            package->features[i].ActionRequest = INSTALLSTATE_SOURCE;
+            package->features[i].Action = INSTALLSTATE_SOURCE;
+        }
+        ACTION_UpdateComponentStates(package,argument);
+    }
+}
+
+
+/*
+ * Subscribed events
+ */
+static void free_subscriber(struct _subscriber *who)
+{
+    HeapFree(GetProcessHeap(),0,who->control);
+    HeapFree(GetProcessHeap(),0,who->attribute);
+    HeapFree(GetProcessHeap(),0,who);
+}
+
+VOID ControlEvent_SubscribeToEvent(MSIPACKAGE *package, LPCWSTR event,
+                                   LPCWSTR control, LPCWSTR attribute)
+{
+    int i;
+    struct _subscription_chain *chain;
+    struct _subscriber *subscriber, *ptr;
+
+    if (!package->EventSubscriptions)
+    {
+        package->EventSubscriptions = HeapAlloc(GetProcessHeap(), 0,
+                                       sizeof(struct _subscriptions));
+        package->EventSubscriptions->chain_count = 0;
+    }
+
+    chain = NULL;
+
+    for (i = 0; i < package->EventSubscriptions->chain_count; i++)
+    {
+        if (strcmpiW(package->EventSubscriptions->chain[i].event,event)==0)
+        {
+            chain = &package->EventSubscriptions->chain[i];
+            break;
+        }
+    }
+
+    if (chain == NULL)
+    {
+        if (package->EventSubscriptions->chain_count)
+            chain = HeapReAlloc(GetProcessHeap(), 0,
+                                package->EventSubscriptions->chain,
+                               (package->EventSubscriptions->chain_count + 1) *
+                                sizeof (struct _subscription_chain)); 
+        else
+            chain= HeapAlloc(GetProcessHeap(),0, sizeof (struct 
+                                                        _subscription_chain));
+
+        package->EventSubscriptions->chain = chain;
+        chain = &package->EventSubscriptions->chain[
+                                package->EventSubscriptions->chain_count];
+        package->EventSubscriptions->chain_count++;
+        memset(chain,0,sizeof(struct _subscription_chain));
+        chain->event = strdupW(event);
+    }
+
+    subscriber = ptr = chain->chain;
+    while (ptr)
+    {
+        subscriber = ptr;
+        ptr = ptr->next;
+    }
+
+    ptr = HeapAlloc(GetProcessHeap(),0,sizeof(struct _subscriber));
+    ptr->control = strdupW(control);
+    ptr->attribute = strdupW(attribute);
+    ptr->next = NULL;
+    
+    if (subscriber)
+        subscriber->next = ptr;
+    else
+        chain->chain = ptr;
+}
+
+VOID ControlEvent_UnSubscribeToEvent(MSIPACKAGE *package, LPCWSTR event,
+                                     LPCWSTR control, LPCWSTR attribute)
+{
+    int i;
+
+    if (!package->EventSubscriptions)
+        return;
+
+    for (i = 0; i < package->EventSubscriptions->chain_count; i++)
+    {
+        if (strcmpiW(package->EventSubscriptions->chain[i].event,event)==0)
+        {
+            struct _subscriber *who;
+            struct _subscriber *prev=NULL;
+            who = package->EventSubscriptions->chain[i].chain;
+            while (who)
+            {
+                if (strcmpiW(who->control,control)==0
+                   && strcmpiW(who->attribute,attribute)==0)
+                {
+                    if (prev)
+                        prev->next = who->next;
+                    else
+                        package->EventSubscriptions->chain[i].chain = who->next;
+        
+                    free_subscriber(who);
+                }
+                else
+                {
+                    prev = who;
+                    who = who->next;
+                }
+            }
+            break;
+        }
+    }
+}
+
+VOID ControlEvent_FireSubscribedEvent(MSIPACKAGE *package, LPCWSTR event, 
+                                      MSIRECORD *data)
+{
+    int i;
+
+    TRACE("Firing Event %s\n",debugstr_w(event));
+
+    if (!package->dialog)
+        return;
+
+    if (!package->EventSubscriptions)
+        return;
+
+    for (i = 0; i < package->EventSubscriptions->chain_count; i++)
+    {
+        if (strcmpiW(package->EventSubscriptions->chain[i].event,event)==0)
+        {
+            struct _subscriber *who;
+            who = package->EventSubscriptions->chain[i].chain;
+            while (who)
+            {
+                ERR("Should Fire event for %s %s\n",
+                    debugstr_w(who->control), debugstr_w(who->attribute));
+                /*
+                 msi_dialog_fire_subscribed_event(package->dialog, who->control,
+                                                  who->attribute, data);
+                */
+                who = who->next;
+            }
+            break;
+        }
+    }
+}
+
+VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package)
+{
+    int i;
+
+    if (!package->EventSubscriptions)
+        return;
+
+    for (i = 0; i < package->EventSubscriptions->chain_count; i++)
+    {
+        struct _subscriber *who;
+        struct _subscriber *ptr;
+        who = package->EventSubscriptions->chain[i].chain;
+        while (who)
+        {
+            ptr = who;
+            who = who->next;
+            free_subscriber(ptr);
+        }
+        HeapFree(GetProcessHeap(), 0,
+                    package->EventSubscriptions->chain[i].event);
+    }
+    HeapFree(GetProcessHeap(),0,package->EventSubscriptions->chain);
+    HeapFree(GetProcessHeap(),0,package->EventSubscriptions);
+    package->EventSubscriptions = NULL;
+}
+
+
+
+/*
+ * ACTION_DialogBox()
+ *
+ * Return ERROR_SUCCESS if dialog is process and ERROR_FUNCTION_FAILED
+ * if the given parameter is not a dialog box
+ */
+UINT ACTION_DialogBox( MSIPACKAGE* package, LPCWSTR szDialogName )
+{
+    UINT r = ERROR_SUCCESS;
+
+    if( package->next_dialog )
+        ERR("Already a next dialog... ignoring it\n");
+    package->next_dialog = NULL;
+
+    /*
+     * Dialogs are chained by filling in the next_dialog member
+     *  of the package structure, then terminating the current dialog.
+     *  The code below sees the next_dialog member set, and runs the
+     *  next dialog.
+     * We fall out of the loop below if we come across a modeless
+     *  dialog, as it returns ERROR_IO_PENDING when we try to run
+     *  its message loop.
+     */
+    r = event_do_dialog( package, szDialogName );
+    while( r == ERROR_SUCCESS && package->next_dialog )
+    {
+        LPWSTR name = package->next_dialog;
+
+        package->next_dialog = NULL;
+        r = event_do_dialog( package, name );
+        HeapFree( GetProcessHeap(), 0, name );
+    }
+
+    if( r == ERROR_IO_PENDING )
+        r = ERROR_SUCCESS;
+
+    return r;
+


More information about the wine-patches mailing list