Announcement

Collapse
No announcement yet.

Word Event Handler & Event Parameters

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Word Event Handler & Event Parameters

    I'm writing a PB9 app which uses MS Word. The PB COM Browser generated source code to define Word IDs, classes, etc. using MSWORD9.OLB. (I need to support Word 2000, XP, 2003 and 2007. MS recommends using the oldest version of Word which will be used.)

    My globals module defines:
    Code:
    GLOBAL goApp AS Int__Application
    
    GLOBAL goWordEvents AS ApplicationEvents2
    The function which initializes an instance of Word includes:
    Code:
        ' create a new instance of Word
    
        goApp = NEWCOM $PROGID_Word_Application
    
        ' if not a valid reference, exit & return failure code
    
        IF ISNOTHING(goApp) THEN
            FUNCTION = %False
            EXIT FUNCTION
        END IF
    
        ' create event handler
    
        goWordEvents = CLASS "Class_ApplicationEvents2"
    
        ' if not a valid reference,
        '    clear Word reference, exit & return failure code
    
        IF ISNOTHING(goWordEvents) THEN
            goApp.Quit
            goApp = NOTHING
            
            FUNCTION = %False
            EXIT FUNCTION
        END IF
    
        ' attach event handler to Word app
    
        EVENTS FROM goApp CALL goWordEvents
        
        ' return success
        
        FUNCTION = %True
    The app is able to connect with Word, then get/set properties and call methods with no problems. Results are identical on Word 2000, XP and 2007.

    But I wasn't receiving all events. This is how I rigged the event-handling class:
    Code:
    CLASS Class_ApplicationEvents2 $CLSID_Word_Event_ApplicationEvents2 AS EVENT
        INTERFACE ApplicationEvents2 $IID_Word_ApplicationEvents2
            INHERIT IDISPATCH
    
            METHOD Startup <1> ()
                LogIt("Startup: " & STR$(TIMER))
            END METHOD
    
            METHOD Quit <2> ()
                LogIt("Quit: " & STR$(TIMER))
            END METHOD
    
            METHOD DocumentChange <3> ()
                LogIt("DocumentChange: " & STR$(TIMER))
            END METHOD
    
            METHOD DocumentOpen <4> (BYVAL DOC AS IDISPATCH)
                LogIt("DocumentOpen: " & STR$(TIMER))
            END METHOD
    
            METHOD DocumentBeforeClose <6> (BYVAL DOC AS IDISPATCH, BYREF IN CANCEL AS INTEGER)
                LogIt("DocumentBeforeClose: " & STR$(TIMER))
            END METHOD
    
            METHOD DocumentBeforePrint <7> (BYVAL DOC AS IDISPATCH, BYREF IN CANCEL AS INTEGER)
                LogIt("DocumentBeforePrint: " & STR$(TIMER))
            END METHOD
    
            METHOD DocumentBeforeSave <8> (BYVAL DOC AS IDISPATCH, BYREF IN SaveAsUI AS INTEGER, BYREF IN CANCEL AS INTEGER)
                LogIt("DocumentBeforeSave: " & STR$(TIMER))
            END METHOD
    
            METHOD NewDocument <9> (BYVAL DOC AS IDISPATCH)
                LogIt("NewDocument: " & STR$(TIMER))
            END METHOD
    
            METHOD WindowActivate <10> (BYVAL DOC AS IDISPATCH, BYVAL Wn AS Int_Window)
                LogIt("WindowActivate: " & STR$(TIMER))
            END METHOD
    
            METHOD WindowDeactivate <11> (BYVAL DOC AS IDISPATCH, BYVAL Wn AS Int_Window)
                LogIt("WindowDeactivate: " & STR$(TIMER))
            END METHOD
    
            METHOD WindowSelectionChange <12> (BYVAL Sel AS Selection)
                LogIt("WindowSelectionChange: " & STR$(TIMER))
            END METHOD
    
            METHOD WindowBeforeRightClick <13> (BYVAL Sel AS Selection, BYREF IN CANCEL AS INTEGER)
                LogIt("WindowBeforeRightClick: " & STR$(TIMER))
            END METHOD
    
            METHOD WindowBeforeDoubleClick <14> (BYVAL Sel AS Selection, BYREF IN CANCEL AS INTEGER)
                LogIt("WindowBeforeDoubleClick: " & STR$(TIMER))
            END METHOD
    
        END INTERFACE
    END CLASS
    Startup, Quit and DocumentChange are always called when they should be. The other events are never called.

    Cleverly noting that none of the called events have parameters, while the uncalled events all have paraments, I have tried modifying parameter types. i.e. changing
    Code:
            METHOD DocumentOpen <4> (BYVAL DOC AS IDISPATCH)
    to
    Code:
            METHOD DocumentOpen <4> (BYVAL DOC AS Int__Document)
    and
    Code:
            METHOD DocumentOpen <4> (BYVAL DOC AS VARIANT)
    Neither attempt worked.

    Does anyone have any ideas on why the events with parameters are not called? Any suggestions would be appreciated.

    Ron

  • #2
    This is odd. I tried a low-level COM version and a PB9 COM version.
    All events in the low-level COM version fire. I am getting the same
    results you did with the PB9 COM version.
    Dominic Mitchell
    Phoenix Visual Designer
    http://www.phnxthunder.com

    Comment


    • #3
      Originally posted by Dominic Mitchell View Post
      This is odd. I tried a low-level COM version and a PB9 COM version.
      All events in the low-level COM version fire. I am getting the same
      results you did with the PB9 COM version.
      Dominic, could you clarify what is meant by "a low-level COM version" ?

      And thanks for looking at this issue. When your results replicate mine, at least I know I'm not going crazy. (Actually, the two aren't mutually exclusive...)

      Ron

      Comment


      • #4
        Dominic, could you clarify what is meant by "a low-level COM version" ?
        By low-level COM I mean using COM directly instead of relying on the COM capabilities of PowerBASIC.
        The Connection Point mechanism is one of the methods by which a server can communicate with a client.
        To facilitate this, the client sets up a sink that is based on the template published by the server.
        The client then passes a reference to the sink to the server via the IConnectionXXX interfaces implementd
        by the server.
        For example, let's assume that we created a sink for the ApplicationEvents2 connection point. We will name
        it oEvents for PB9 COM and pEvents for low-level COM. The following is the PB9 COM and low-level COM versions
        of the code needed to connect the sink to the connection point.
        PB9 COM
        Code:
        EVENTS FROM oWordApp CALL oEvents
        Low-level COM
        Code:
           
            rguid = $IID_APPLICATIONEVENTS2
            riid = $IID_ICONNECTIONPOINTCONTAINER
            hr = IUnknown_QueryInterface(pIUnknown, riid, pIConnectionPointContainer)
            IF hr = %S_OK THEN
              hr = IConnectionPointContainer_FindConnectionPoint(pIConnectionPointContainer, rguid, pIConnectionPoint)
              IF hr = %S_OK THEN
                  hr = IConnectionPoint_Advise(pIConnectionPoint, pEvents, dwCookie)
                END IF
              END IF
              IConnectionPointContainer_Release pIConnectionPointContainer
            END IF
          END IF
        This is only a small taste of what really goes on behind your PB code.

        Now to throw something out there that might spark some discussion. One of the drawbacks to the PB9 COM
        sinks, is the lack of a reference to the object that implemented the connection point.
        For example, you are processing an event from and ActiveX control or a DocObject and needs a reference to the object.
        How did you obtain that reference? Whatever method you used is a handicap. Think beyond the box of single-instance
        windows and one OCX per window to see why it is a handicap.
        Dominic Mitchell
        Phoenix Visual Designer
        http://www.phnxthunder.com

        Comment


        • #5
          One of the drawbacks to the PB9 COM sinks, is the lack of a reference to the object that implemented the connection point.
          This has an easy solution.

          1. Add an instance variable to the class

          Code:
          INSTANCE m_pObj AS MyInterface
          2. Add a method to set the value of m_pObj.

          Code:
          METHOD SetObjRef (BYVAL pObj AS MyInterface)
             m_pObj = pObj
          END METHOD
          3. After creating an instance of the class, set the value:

          Code:
          pEvtSink.SetObjRef pObj
          Forum: http://www.jose.it-berater.org/smfforum/index.php

          Comment


          • #6
            That is a very good solution.
            The one case where it is not going to work is where a single sink is associated
            with the same connection point on multiple instances of the same object. Not
            something anyone with a level head should be doing though.
            Dominic Mitchell
            Phoenix Visual Designer
            http://www.phnxthunder.com

            Comment


            • #7
              Hi Ronald--

              Even if you access WORD through direct, VTable COM, WORD raises events through the IDispatch interface. The IDispatch interface describes an optional feature known as named parameters, whereby some arguments are described by their name and may also be omitted. This is not a required implementation, and the COM specification suggests that the COM server (WORD) should be capable of handling both named parameters or positional parameters. Since the use of named parameters is extraordinarily slow, PowerBASIC does not support them. Rather, it uses traditional positional parameters for much better performance. For reasons known only to ms, it appears that WORD is incapable of calling event methods with standard, positional parameters. Since Dominic mentioned that he successfully executed this event code, I'm surprised he didn't notice.

              We believe this is a fairly isolated case. However, if this turns out to become more of an issue with other COM servers, we'll certainly add the option to PowerBASIC.

              Best regards,

              Bob Zale
              PowerBASIC Inc.

              Comment


              • #8
                I guess I never noticed it was using named parameters. Should have checked the Phoenix OLE container log.
                But because of the way the DispParams are cracked in Phoenix, the client will still receive the events.
                I suspect, however, that the values of the parameters are not correct.
                Oh well, an easy fix. Finally something exciting I can sink my teeth into.
                Dominic Mitchell
                Phoenix Visual Designer
                http://www.phnxthunder.com

                Comment


                • #9
                  Problem Handled, So To Speak

                  I want to thank everyone for their comments and suggestions. They in turn led to reviewing sample code here on the forum, far too much time staring at MSDN topics on COM interfaces, and substantial experimentation.

                  The result is a class-based event handler which reliably receives all events and passed parameters. It works on Word 2000, XP and 2007.

                  Since my requirements involve only a single instance of the Word application object, I don't have any trouble knowing which application instance received the event.

                  There can, however, be multiple open documents; some documents may be displayed in more than 1 window. But I riffed off the OBJPTR approach of storing pointers in dialog user areas, so the callback function can determine which instance of an object handles window events.

                  Experimentally, at least, I can create an instance of my own class for each open document and store references in the event handler object. Then a Word event which passes a document can find the proper instance of my document class object to process the event for that document.

                  I'm going to post the code in the source forum, as soon as I verify I haven't left in any confidential info from the main app.

                  But thanks again to all who pointed toward places I hadn't explored.

                  Ron

                  Comment


                  • #10
                    But because of the way the DispParams are cracked in Phoenix, the client will still receive the events.
                    I suspect, however, that the values of the parameters are not correct.
                    Quite right. Instead of using an index in reversed order to unpack the parameters, we need to retrieve it using the NamedDispId pointer.

                    To test it, I have written a low-level class for event sink and, in the Invoke method, I'm sending the events to a callback function (see code below). In the WindowBeforeDoubleClick event, we had to unpack Sel using 1 as the index and Cancel using 0 as the index, if they were positional parameters, but as they are named parameters we need to use 0 for Sel and 1 for Cancel.

                    Code:
                    FUNCTION ApplicationEvents2Callback ( _
                       BYVAL pthis AS DWORD _               ' // Pointer to the client's IDispatch
                     , BYVAL dispidMember AS LONG _         ' // Identifier of the event
                     , BYREF pdispparams AS DISPPARAMS _    ' // Structure containing an array of arguments
                     , BYREF pvarResult AS VARIANT _        ' // Pointer to the location where the result is to be stored, or NULL if the caller expects no result
                     , BYVAL pCustData AS DWORD _           ' // Pointer to user defined data
                     ) AS LONG                              ' // Return value
                    
                       LOCAL hr AS LONG                     ' // HRESULT   
                       LOCAL i AS LONG                      ' // Loop index
                       LOCAL pv AS VARIANT PTR              ' // Pointer to arguments array
                       LOCAL pvapi AS VARIANTAPI PTR        ' // Pointer to arguments array
                       LOCAL pDoc AS IDispatch              ' // Document coclass
                       LOCAL pSel AS Selection              ' // Selection dispatch interface
                       LOCAL pWindow AS Int_Window          ' // Window dispatch interface
                       LOCAL bCancel AS INTEGER             ' // boolean value
                       LOCAL bSaveAsUI AS INTEGER           ' // boolean value
                    
                       FUNCTION = %S_OK
                    
                       OutputDebugString STR$(dispidMember) & STR$(pdispparams.CountArgs) & STR$(pdispparams.CountNamed)
                    
                       pv = pDispParams.VariantArgs
                       pvapi = pDispParams.VariantArgs
                    
                       SELECT CASE AS LONG dispidMember
                          CASE 1   ' // Startup
                             OutputDebugString "Startup"
                          CASE 2   ' // Quit
                             OutputDebugString "Quit"
                          CASE 3   ' // DocumentChange
                             OutputDebugString "DocumentChange"
                          CASE 4   ' // DocumentOpen
                             OutputDebugString "DocumentOpen"
                             IF pdispparams.CountNamed THEN
                                ' pDoc = @pv[0]
                                ' ...
                                ' pDoc = NOTHING
                             END IF
                          CASE 6   ' // DocumentBeforeClose
                             OutputDebugString "DocumentBeforeClose"
                             IF pdispparams.CountNamed THEN
                                FOR i = 0 TO pdispparams.CountNamed - 1
                                   OutputDebugString "DocumentBeforeClose - prm " & STR$(i) & " id = " & STR$([email protected][i])
                                   SELECT CASE [email protected][i]
                                      CASE 0   ' // Doc - *Document coclass
                                         ' pDoc = @pv[0]
                                         ' ...
                                         ' pDoc = NOTHING
                                      CASE 1   ' // Cancel - VT_BOOL OR VT_BYREF
                                         ' bCancel = VARIANT#(@pv[1])
                                         ' To return a value, use:
                                         ' bCancel = <value> (0 for false, -1 for true)
                                         ' @pvapi[1][email protected] = bCancel
                                   END SELECT
                                NEXT
                             END IF
                          CASE 7   ' // DocumentBeforePrint
                             OutputDebugString "DocumentBeforePrint"
                             IF pdispparams.CountNamed THEN
                                FOR i = 0 TO pdispparams.CountNamed - 1
                                   OutputDebugString "DocumentBeforePrint - prm " & STR$(i) & " id = " & STR$([email protected][i])
                                   SELECT CASE [email protected][i]
                                      CASE 0   ' // Doc - *Document coclass
                                         ' pDoc = @pv[0]
                                         ' ...
                                         ' pDoc = NOTHING
                                      CASE 1   ' // Cancel - VT_BOOL OR VT_BYREF
                                         ' bCancel = VARIANT#(@pv[1])
                                         ' To return a value, use:
                                         ' bCancel = <value> (0 for false, -1 for true)
                                         ' @pvapi[1][email protected] = bCancel
                                   END SELECT
                                NEXT
                             END IF
                          CASE 8   ' // DocumentBeforeSave
                             OutputDebugString "DocumentBeforeSave"
                             IF pdispparams.CountNamed THEN
                                FOR i = 0 TO pdispparams.CountNamed - 1
                                   OutputDebugString "DocumentBeforeSave - prm " & STR$(i) & " id = " & STR$([email protected][i])
                                   SELECT CASE [email protected][i]
                                      CASE 0   ' // Doc - *Document coclass
                                         ' pSel = @pv[0]
                                         ' ...
                                         ' pSel = NOTHING
                                      CASE 1   ' // SaveAsUI - VT_BOOL OR VT_BYREF
                                         ' bSaveAsUI = VARIANT#(@pv[1])
                                         ' To return a value, use:
                                         ' bSaveAsUI = <value> (0 for false, -1 for true)
                                         ' @pvapi[1][email protected] = bSaveAsUI
                                      CASE 2   ' // Cancel - VT_BOOL OR VT_BYREF
                                         ' bCancel = VARIANT#(@pv[2])
                                         ' To return a value, use:
                                         ' bCancel = <value> (0 for false, -1 for true)
                                         ' @pvapi[2][email protected] = bCancel
                                   END SELECT
                                NEXT
                             END IF
                          CASE 9   ' // NewDocument
                             OutputDebugString "NewDocument"
                             IF pdispparams.CountNamed THEN
                                ' pDoc = @pv[0]
                                ' ...
                                ' pDoc = NOTHING
                             END IF
                          CASE 10  ' // WindowActivate
                             OutputDebugString "WindowActivate"
                             IF pdispparams.CountNamed THEN
                                FOR i = 0 TO pdispparams.CountNamed - 1
                                   OutputDebugString "WindowActivate - prm " & STR$(i) & " id = " & STR$([email protected][i])
                                   SELECT CASE [email protected][i]
                                      CASE 0   ' // Doc - *Document coclass
                                         ' pDoc = @pv[0]
                                         ' ...
                                         ' pDoc = NOTHING
                                      CASE 1   ' // *Wn - *Window dispatch interface
                                         ' pWindow = @pv[1]
                                         ' ...
                                         ' pWindow = NOTHING
                                   END SELECT
                                NEXT
                             END IF
                          CASE 11  ' // WindowDeactivate
                             OutputDebugString "WindowDeactivate"
                             IF pdispparams.CountNamed THEN
                                FOR i = 0 TO pdispparams.CountNamed - 1
                                   OutputDebugString "WindowDeactivate - prm " & STR$(i) & " id = " & STR$([email protected][i])
                                   SELECT CASE [email protected][i]
                                      CASE 0   ' // Doc - *Document coclass
                                         ' pDoc = @pv[0]
                                         ' ...
                                         ' pDoc = NOTHING
                                      CASE 1   ' // *Wn - *Window dispatch interface
                                         ' pWindow = @pv[1]
                                         ' ...
                                         ' pWindow = NOTHING
                                   END SELECT
                                NEXT
                             END IF
                          CASE 12  ' // WindowSelectionChange
                             OutputDebugString "WindowSelectionChange"
                             IF pdispparams.CountNamed THEN
                                ' pSel = @pv[0]
                                ' ...
                                ' pSel = NOTHING
                             END IF
                          CASE 13  ' // WindowBeforeRightClick
                             OutputDebugString "WindowBeforeRightClick"
                             IF pdispparams.CountNamed THEN
                                FOR i = 0 TO pdispparams.CountNamed - 1
                                   OutputDebugString "WindowBeforeRightClick - prm " & STR$(i) & " id = " & STR$([email protected][i])
                                   SELECT CASE [email protected][i]
                                      CASE 0   ' // Sel - *Selection dispatch interface
                                         ' pSel = @pv[0]
                                         ' ...
                                         ' pSel = NOTHING
                                      CASE 1   ' // Cancel - VT_BOOL OR VT_BYREF
                                         ' bCancel = VARIANT#(@pv[1])
                                         ' To return a value, use:
                                         ' bCancel = <value> (0 for false, -1 for true)
                                         ' @pvapi[1][email protected] = bCancel
                                   END SELECT
                                NEXT
                             END IF
                          CASE 14  ' // WindowBeforeDoubleClick
                             OutputDebugString "WindowBeforeDoubleClick"
                             IF pdispparams.CountNamed THEN
                                FOR i = 0 TO pdispparams.CountNamed - 1
                                   OutputDebugString "WindowBeforeDoubleClick - prm " & STR$(i) & " id = " & STR$([email protected][i])
                                   SELECT CASE [email protected][i]
                                      CASE 0   ' // Sel - *Selection dispatch interface
                                         ' pSel = @pv[0]
                                         ' ...
                                         ' pSel = NOTHING
                                      CASE 1   ' // Cancel - VT_BOOL OR VT_BYREF
                                         ' bCancel = VARIANT#(@pv[1])
                                         ' To return a value, use:
                                         ' bCancel = <value> (0 for false, -1 for true)
                                         ' @pvapi[1][email protected] = bCancel
                                   END SELECT
                                NEXT
                             END IF
                          CASE ELSE
                            FUNCTION = %DISP_E_MEMBERNOTFOUND
                       END SELECT
                    
                    END FUNCTION
                    Last edited by José Roca; 8 Jan 2009, 01:25 AM.
                    Forum: http://www.jose.it-berater.org/smfforum/index.php

                    Comment


                    • #11
                      Instead of a low-level class for event sink, we can also use a PB event sink class and EVENTS FROM/EVENTS END if we INHERIT from IUnknown, remove the DispIDs and add the four methods of the IDispatch interface.

                      Code:
                      ' ########################################################################################
                      ' Class CApplicationEvents2
                      ' Interface name = ApplicationEvents2
                      ' IID = {000209FE-0000-0000-C000-000000000046}
                      ' Attributes = 4112 [&H1010] [Hidden] [Dispatchable]
                      ' ########################################################################################
                      
                      $IID_ApplicationEvents2 = GUID$("{000209FE-0000-0000-C000-000000000046}")
                      
                      CLASS CApplicationEvents2 GUID$("{C3E2DC2E-A8A3-4FA4-9C8C-44ED21E665E2}") AS EVENT
                      
                      INTERFACE ApplicationEvents2Impl $IID_ApplicationEvents2 AS EVENT
                      
                        INHERIT IUnknown
                      
                         ' =====================================================================================
                         METHOD GetTypeInfoCount (BYREF pctInfo AS DWORD)
                            OutputDebugString FUNCNAME$
                            pctInfo = 0
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD GetTypeInfo (BYVAL itinfo AS DWORD, BYVAL lcid AS DWORD, BYREF pptinfo AS DWORD)
                            OutputDebugString FUNCNAME$
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD GetIDsOfNames (BYREF riid AS GUID, BYREF rgszNames AS STRING, BYVAL cNames AS DWORD, BYVAL lcid AS DWORD, BYREF rgdispid AS LONG)
                            OutputDebugString FUNCNAME$
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD Invoke (BYVAL dispidMember AS LONG, BYREF riid AS GUID, _
                                        BYVAL lcid AS DWORD, BYVAL wFlags AS WORD, BYREF pdispparams AS DISPPARAMS, BYREF pvarResult AS VARIANT, _
                                        BYVAL pexcepinfo AS DWORD, BYREF puArgErr AS DWORD)
                         
                            LOCAL i AS LONG
                            LOCAL pv AS VARIANT PTR
                            LOCAL pvapi AS VARIANTAPI PTR
                            pv = pDispParams.VariantArgs
                            pvapi = pDispParams.VariantArgs
                      
                            LOCAL pDoc AS IDispatch
                            LOCAL pWin AS IDispatch
                            LOCAL pSel AS IDispatch
                            LOCAL bCancel AS INTEGER
                            LOCAL bSaveAsUI AS INTEGER
                      
                            OutputDebugString FUNCNAME$ & STR$(dispidMember)
                            SELECT CASE AS LONG dispidMember
                               CASE 1   ' // Startup
                                  ME.Startup
                               CASE 2   ' // Quit
                                  ME.Quit
                               CASE 3   ' // DocumentChange
                                  ME.DocumentChange
                               CASE 4   ' // DocumentOpen
                                  pDoc = @pv[0]
                                  ME.DocumentOpen(pDoc)
                                  pDoc = NOTHING
                               CASE 6   ' // DocumentBeforeClose
                                  IF pdispparams.CountNamed THEN
                                     FOR i = 0 TO pdispparams.CountNamed - 1
                                        SELECT CASE [email protected][i]
                                           CASE 0   ' // Doc - *Document coclass
                                              pDoc = @pv[0]
                                           CASE 1   ' // Cancel - VT_BOOL OR VT_BYREF
                                              bCancel = VARIANT#(@pv[1])
                                        END SELECT
                                     NEXT
                                     ME.DocumentBeforeClose(pDoc, bCancel)
                                     @pvapi[1][email protected] = bCancel
                                     pDoc = NOTHING
                                  END IF
                               CASE 7   ' // DocumentBeforePrint
                                  IF pdispparams.CountNamed THEN
                                     FOR i = 0 TO pdispparams.CountNamed - 1
                                        SELECT CASE [email protected][i]
                                           CASE 0   ' // Doc - *Document coclass
                                              pDoc = @pv[0]
                                           CASE 1   ' // Cancel - VT_BOOL OR VT_BYREF
                                              bCancel = VARIANT#(@pv[1])
                                        END SELECT
                                     NEXT
                                     ME.DocumentBeforePrint(pDoc, bCancel)
                                     @pvapi[1][email protected] = bCancel
                                     pDoc = NOTHING
                                  END IF
                               CASE 8   ' // DocumentBeforeSave
                                  IF pdispparams.CountNamed THEN
                                     FOR i = 0 TO pdispparams.CountNamed - 1
                                        SELECT CASE [email protected][i]
                                           CASE 0   ' // Doc - *Document coclass
                                              pDoc = @pv[0]
                                           CASE 1   ' // SaveAsUI - VT_BOOL OR VT_BYREF
                                              bSaveAsUI = VARIANT#(@pv[1])
                                           CASE 2   ' // Cancel - VT_BOOL OR VT_BYREF
                                              bCancel = VARIANT#(@pv[2])
                                        END SELECT
                                     NEXT
                                     ME.DocumentBeforeSave(pDoc, bSaveAsUI, bCancel)
                                     @pvapi[1][email protected] = bSaveAsUI
                                     @pvapi[2][email protected] = bCancel
                                     pDoc = NOTHING
                                  END IF
                               CASE 9   ' // NewDocument
                                  pDoc = @pv[0]
                                  ME.DocumentOpen(pDoc)
                                  pDoc = NOTHING
                               CASE 10   ' // WindowActivate
                                  IF pdispparams.CountNamed THEN
                                     FOR i = 0 TO pdispparams.CountNamed - 1
                                        SELECT CASE [email protected][i]
                                           CASE 0   ' // Doc - *Document coclass
                                              pDoc = @pv[0]
                                           CASE 1   ' // *Wn - *Window dispatch interface
                                              pWin = @pv[1]
                                        END SELECT
                                     NEXT
                                     ME.WindowActivate(pDoc, pWin)
                                     pDoc = NOTHING
                                     pWin = NOTHING
                                  END IF
                               CASE 11   ' // WindowDeactivate
                                  IF pdispparams.CountNamed THEN
                                     FOR i = 0 TO pdispparams.CountNamed - 1
                                        SELECT CASE [email protected][i]
                                           CASE 0   ' // Doc - *Document coclass
                                              pDoc = @pv[0]
                                           CASE 1   ' // *Wn - *Window dispatch interface
                                              pWin = @pv[1]
                                        END SELECT
                                     NEXT
                                     ME.WindowDeactivate(pDoc, pWin)
                                     pDoc = NOTHING
                                     pWin = NOTHING
                                  END IF
                               CASE 12   ' // WindowSelectionChange
                                  pSel = @pv[0]
                                  ME.WindowSelectionChange(pSel)
                                  pSel = NOTHING
                               CASE 13   ' // WindowBeforeRightClick
                                  IF pdispparams.CountNamed THEN
                                     FOR i = 0 TO pdispparams.CountNamed - 1
                                        SELECT CASE [email protected][i]
                                           CASE 0   ' // Doc - *Document coclass
                                              pSel = @pv[0]
                                           CASE 1   ' // Cancel - VT_BOOL OR VT_BYREF
                                              bCancel = VARIANT#(@pv[1])
                                        END SELECT
                                     NEXT
                                     ME.WindowBeforeRightClick(pSel, bCancel)
                                     @pvapi[1][email protected] = bCancel
                                     pSel = NOTHING
                                  END IF
                               CASE 14   ' // WindowBeforeDoubleClick
                                  IF pdispparams.CountNamed THEN
                                     FOR i = 0 TO pdispparams.CountNamed - 1
                                        SELECT CASE [email protected][i]
                                           CASE 0   ' // Doc - *Document coclass
                                              pSel = @pv[0]
                                           CASE 1   ' // Cancel - VT_BOOL OR VT_BYREF
                                              bCancel = VARIANT#(@pv[1])
                                        END SELECT
                                     NEXT
                                     ME.WindowBeforeDoubleClick(pSel, bCancel)
                                     @pvapi[1][email protected] = bCancel
                                     pSel = NOTHING
                                  END IF
                           END SELECT
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD Startup
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD Quit
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD DocumentChange
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD DocumentOpen ( _
                           BYVAL pDoc AS IDispatch _                          ' [in] *Doc Document <coclass>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD DocumentBeforeClose ( _
                           BYVAL pDoc AS IDispatch _                          ' [in] *Doc Document <coclass>
                         , BYREF bCancel AS INTEGER _                         ' [in] *Cancel VT_BOOL <Integer>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD DocumentBeforePrint ( _
                           BYVAL pDoc AS IDispatch _                          ' [in] *Doc Document <coclass>
                         , BYREF bCancel AS INTEGER _                         ' [in] *Cancel VT_BOOL <Integer>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD DocumentBeforeSave ( _
                           BYVAL pDoc AS IDispatch _                          ' [in] *Doc Document <coclass>
                         , BYREF bSaveAsUI AS INTEGER _                       ' [in] *SaveAsUI VT_BOOL <Integer>
                         , BYREF bCancel AS INTEGER _                         ' [in] *Cancel VT_BOOL <Integer>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD NewDocument ( _
                           BYVAL pDoc AS IDispatch _                          ' [in] *Doc Document <coclass>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD WindowActivate ( _
                           BYVAL pDoc AS IDispatch _                          ' [in] *Doc Document <coclass>
                         , BYVAL pWin AS IDispatch _                          ' [in] *Wn Window <dispinterface>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD WindowDeactivate ( _
                           BYVAL pDoc AS IDispatch _                          ' [in] *Doc Document <coclass>
                         , BYVAL pWin AS IDispatch _                          ' [in] *Wn Window <dispinterface>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD WindowSelectionChange ( _
                           BYVAL pSel AS IDispatch _                          ' [in] *Sel Selection <dispinterface>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD WindowBeforeRightClick ( _
                           BYVAL pSel AS IDispatch _                          ' [in] *Sel Selection <dispinterface>
                         , BYREF bCancel AS INTEGER _                         ' [in] *Cancel VT_BOOL <Integer>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                           bCancel = -1
                      
                         END METHOD
                         ' =====================================================================================
                      
                         ' =====================================================================================
                         METHOD WindowBeforeDoubleClick ( _
                           BYVAL pSel AS IDispatch _                          ' [in] *Sel Selection <dispinterface>
                         , BYREF bCancel AS INTEGER _                         ' [in] *Cancel VT_BOOL <Integer>
                         )                                                    ' void
                      
                           OutputDebugString FUNCNAME$
                           OutputDebugString STR$(OBJPTR(pSel)) & STR$(bCancel)
                      
                         END METHOD
                         ' =====================================================================================
                      
                      END INTERFACE
                      
                      END CLASS
                      Forum: http://www.jose.it-berater.org/smfforum/index.php

                      Comment


                      • #12
                        Instead of using an index in reversed order to unpack the parameters, we need to retrieve it using the NamedDispId pointer.
                        True, that is why it was an easy fix. Also, in the general case I had to make
                        allowance for the possibility of a mixture of positional and named parameters.

                        There is also another problem that is common to all high level languages that have separate
                        functions for creating an object and connecting to events from that object.
                        For example, you will never receive the Startup event because by the time your code connects to
                        the object it is already too late.
                        I have solved this problem in Phoenix by having the create function set up the sink.
                        Dominic Mitchell
                        Phoenix Visual Designer
                        http://www.phnxthunder.com

                        Comment


                        • #13
                          Dominic:
                          For example, you will never receive the Startup event because by the time your code connects to
                          the object it is already too late.
                          This is true when you acquire a reference to the Word application object by using GETCOM, NEWCOM or ANYCOM.

                          But Word (and other Office apps) provide for COM add-ins, which can be flagged to load before the Startup event is raised. COM add-ins receive the OnConnection event, which passes a reference to the application object. If you connect your Word event handler during the COM add-in's OnConnection event, your Word event handler will receive the Startup event.

                          Also, if the Normal.dot template has an AutoExec macro, it is executed before the Startup event is raised. Like any other VBA macro, AutoExec can call your app and pass the Word application object. (Up until PB 9, this would have been through a standard DLL call to a function DECLAREd in VBA. Now that PB can create COM objects and register their GUIDs, your COM can be checked in the VBA References dialog. The macro can instantiate your PB COM object and call a method or property to pass Word's application object. If done in the AutoExec macro, and if your Word event handler was connected when the Word application object was passed, your Word event handler will receive the Startup event.

                          In fact, if the AutoExec macro opens a document before calling your PB app, your Word event handler will receive WindowActivate and DocumentOpen events before receiving the Startup event. (Actually, depending on what else the AutoExec macro does, you could receive WindowSelectionChange, DocumentChange and other events before the Startup event.)

                          Onc big advantage of acquiring the Word application object through this complexity is that automation performed in your code is treated as though it were performed in a macro. If you set the Word application's ScreenUpdating to %False, you can perform operations without updating the document. This often provides considerable speedup and is a lot easier on the eyes. (Remember, after your operations, set ScreenUpdating to %True and call the ScreenRefresh method.)

                          Another instance of MS applications bypassing the rules, but one which can be used to advantage.

                          Ron

                          Comment


                          • #14
                            José Sinks It

                            José:

                            Your demonstration of INHERITing from IUnknown and processing calls to the IDispatch interface is much more elegant than the workaround I fashioned. You avoid the problems (at least for a COM API novice like me) of getting the event sink, building the VTable, connecting & disconnecting.

                            BTW, it turns out that the document/window/selection pointers passed as parameters can be used as references to either an IDISPATCH object or the more flexible Int__Document/Int_Window/Selection interface objects.

                            I salute you. I'd much rather waste time cursing my client's idiosyncratic business practices than Microsoft's APIs. So I thank those like you whose expertise in such areas simplifies my work.

                            Ron

                            Comment

                            Working...
                            X