Imports SolidEdgeConstants, SolidEdgeFramework
Imports System.Object
Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices.ComTypes

Public Class MouseEvents
    Inherits System.Windows.Forms.Form

    Implements SolidEdgeFramework.ISECommandEvents
    Implements SolidEdgeFramework.ISEMouseEvents

    Private Command_CP As IConnectionPoint
    Private Command_CP_Cookie As Integer
    Private Mouse_CP As IConnectionPoint
    Private Mouse_CP_Cookie As Integer

    Dim objApp As SolidEdgeFramework.Application
    Dim objDoc As Object
    Dim objCmd As SolidEdgeFramework.Command
    Dim objMouse As SolidEdgeFramework.Mouse

    ' Define Action and other data needed to manipulate the controls on the form from the mouse COM event handlers.
    ' The COM events for the mouse come in on a worker thread. That makes it tougher to update the controls on the form.
    ' I can use a delegate but with .NET 3.5 (?) the "Action" was introduced making handling cross-thread calls a bit
    ' easier.
    ' An action that sets text on a text box.
    Dim mySetTextAction As Action(Of System.Windows.Forms.TextBox, String)
    ' And an action that sets the readout display data on the form.
    Dim mySetReadoutDisplay As Action(Of Integer, Integer, Double, Double, Double, Object, Long, Object)


    Private Sub MouseEvents_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        ' Failure to end command by setting "Done" can result in the command still running after the form closes!
        ' I call CleanUP with False so that the code disconnects from the command events but 
        CleanUP()

        If Not objCmd Is Nothing Then
            objCmd.Done = True
            ReleaseComObject(objCmd)
            'objCmd = Nothing
        End If
    End Sub

    Private Sub MouseEvents_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' Unlike VB 6, .NET does not provide an OLE message filter. Failure to register one can result in random runtime communication failures
        ' such as "call rejected by callee"
        MessageFilter.Register()

        'Actions can be created on the fly or not. I decided to set them once in code to avoid calling "new Action" all the time in the hope that
        'memory usage is minimized as I can receive a lot of mouse move messages if move is enabled by the user.
        mySetTextAction = AddressOf SetText
        mySetReadoutDisplay = AddressOf SetReadoutDisplay

        objApp = GetObject(, "SolidEdge.Application")
        objDoc = objApp.ActiveDocument
        objCmd = objApp.CreateCommand(seCmdFlag.seNoDeactivate)
        objMouse = objCmd.Mouse
        objCmd.Start()
        optDynOff.Checked = True
        optLocOff.Checked = True
        txtPause.Text = 0
        optWindowGraph.Checked = True
        txtCurMouseEvent.Text = "None"
        optModelCoord.Checked = True
        optModelUnits.Checked = True

        ' Add all of the geometry types to locate filter list.
        lstLocate.Items.Add("seGeometry2d")
        lstLocate.Items.Add("seArc2d")
        lstLocate.Items.Add("seBspCurve2d")
        lstLocate.Items.Add("seCircle2d")
        lstLocate.Items.Add("seComplexString2d")
        lstLocate.Items.Add("seCurve2d")
        lstLocate.Items.Add("seEllipse2d")
        lstLocate.Items.Add("seLine2d")
        lstLocate.Items.Add("seLineString2d")
        lstLocate.Items.Add("sePoint2d")
        lstLocate.Items.Add("seBoundary2d")
        lstLocate.Items.Add("seProfile")
        lstLocate.Items.Add("seHole2d")
        lstLocate.Items.Add("sePoint")
        lstLocate.Items.Add("seLine")
        lstLocate.Items.Add("seLineString")
        lstLocate.Items.Add("seEllipse")
        lstLocate.Items.Add("seArc")
        lstLocate.Items.Add("seBspCurve")
        lstLocate.Items.Add("seComplexString")
        lstLocate.Items.Add("sePlane")
        lstLocate.Items.Add("seCone")
        lstLocate.Items.Add("seSphere")
        lstLocate.Items.Add("seTorus")
        lstLocate.Items.Add("seProjectedFace")
        lstLocate.Items.Add("seRevolvedFace")
        lstLocate.Items.Add("seBspSurfaceFace")
        lstLocate.Items.Add("seBoundary")
        lstLocate.Items.Add("seRuledFace")
        lstLocate.Items.Add("sePolyMesh")
        lstLocate.Items.Add("seRefPlane")
        lstLocate.Items.Add("seEdge")
        lstLocate.Items.Add("seFace")
        lstLocate.Items.Add("seVertex")
        lstLocate.Items.Add("seDrivingDimension")
        lstLocate.Items.Add("seFeature")
        lstLocate.Items.Add("seRefAxis")
        lstLocate.Items.Add("sePart")
        lstLocate.Items.Add("seCenterMark")
        lstLocate.Items.Add("seCenterLine")
        lstLocate.Items.Add("seCuttingPlane")
        lstLocate.Items.Add("seDrawingView")
        lstLocate.Items.Add("seDetailEnvelope")
        lstLocate.Items.Add("seViewPlane")
        lstLocate.Items.Add("seCrossSection")
        lstLocate.Items.Add("seLeader")
        lstLocate.Items.Add("seDatumFrame")
        lstLocate.Items.Add("seDatumTarget")
        lstLocate.Items.Add("seFeatureControlFrame")
        lstLocate.Items.Add("seSurfaceFinishSymbols")
        lstLocate.Items.Add("seWeldSymbols")
        lstLocate.Items.Add("seBalloon")
        lstLocate.Items.Add("seSmartFrame2d")
        lstLocate.Items.Add("seTextBox")
        lstLocate.Items.Add("seCallout")
        lstLocate.Items.Add("seLocateRelation2d")
        lstLocate.Items.Add("seDVLine2d")
        lstLocate.Items.Add("seDVArc2d")
        lstLocate.Items.Add("seDVBSplineCurve2d")
        lstLocate.Items.Add("seDVCircle2d")
        lstLocate.Items.Add("seDVEllipse2d")
        lstLocate.Items.Add("seDVLineString2d")
        lstLocate.Items.Add("seDVEllipticalArc2d")
        lstLocate.Items.Add("seEllipticalArc2d")
        lstLocate.Items.Add("seBoltHoleCircle")
        lstLocate.Items.Add("seKeyPoint")
        lstLocate.Items.Add("seGroup")
        lstLocate.Items.Add("seBody")
        lstLocate.Items.Add("seGroup2d")
        lstLocate.Items.Add("seTextProfile")
        lstLocate.Items.Add("sePartAndTopology") ' This is no good by itself. Need sePart AND seFace or seEdge or seVertex ... then add this so assembly locates topology or part
        lstLocate.Items.Add("seAssemblyLayout")
        lstLocate.Items.Add("seLocateCoordinateSystem")

        AddOrRemoveEventHandlers(True, True, True)
    End Sub

    Private Sub chkEnableDrag_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkEnableDrag.CheckedChanged
        If chkEnableDrag.Checked Then
            objMouse.EnabledDrag = True
            lblState.Enabled = True
            txtState.Enabled = True
        Else
            objMouse.EnabledDrag = False
            lblState.Enabled = False
            txtState.Enabled = False
        End If
    End Sub

    Private Sub chkEnableMove_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkEnableMove.CheckedChanged
        If chkEnableMove.Checked Then
            objMouse.EnabledMove = True
        Else
            objMouse.EnabledMove = False
        End If
    End Sub

    Private Sub optDynOff_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optDynOff.CheckedChanged
        objMouse.DynamicsMode = seDynamicsModes.seDynamicsOff
    End Sub

    Private Sub optDynLine_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optDynLine.CheckedChanged
        objMouse.DynamicsMode = seDynamicsModes.seDynamicsLine
    End Sub

    Private Sub optDynCircle_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optDynCircle.CheckedChanged
        objMouse.DynamicsMode = seDynamicsModes.seDynamicsCircleByCenter
    End Sub

    Private Sub optDynRect_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optDynRect.CheckedChanged
        objMouse.DynamicsMode = seDynamicsModes.seDynamicsRectangle
    End Sub

    Private Sub optLocOff_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optLocOff.CheckedChanged
        objMouse.LocateMode = seLocateModes.seLocateOff
    End Sub

    Private Sub optLocSimple_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optLocSimple.CheckedChanged
        objMouse.LocateMode = seLocateModes.seLocateSimple
    End Sub

    Private Sub optLocQPick_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optLocQPick.CheckedChanged
        objMouse.LocateMode = seLocateModes.seLocateQuickPick
    End Sub

    Private Sub txtPause_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtPause.TextChanged
        objMouse.PauseLocate = txtPause.Text
    End Sub

    Private Sub chkInterDoc_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkInterDoc.CheckedChanged
        If chkInterDoc.Checked Then
            objMouse.InterDocumentLocate = True
        Else
            objMouse.InterDocumentLocate = False
        End If
    End Sub

    Private Sub optWindowGraph_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optWindowGraph.CheckedChanged
        objMouse.WindowTypes = 1
    End Sub

    Private Sub optWindowAll_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optWindowAll.CheckedChanged
        objMouse.WindowTypes = 0
    End Sub

    Private Sub optModelCoord_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optModelCoord.CheckedChanged
        'expects an integer but can find no definition of what it should be.
        objMouse.ScaleMode = 1
        'set up so units will be displayed in model coordinates.
        'need to figure out what this is
        'frmUnits.Enabled=True
    End Sub

    Private Sub optWindowCoord_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles optWindowCoord.CheckedChanged
        'expects an integer but can find no definition of what it should be.
        objMouse.ScaleMode = 0
        'set up so units will be displayed in model coordinates.
        'need to figure out what this is
        'frmUnits.Enabled=False
    End Sub

    Private Sub btnClearAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearAll.Click
        ' Unselect all items in the locate filter list box.
        Dim i As Integer
        For i = 0 To lstLocate.Items.Count - 1
            lstLocate.SetSelected(i, False)
        Next
    End Sub

    Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
        Close()
    End Sub

    Public Sub SetText(TextBox As System.Windows.Forms.TextBox, Text As String)
        TextBox.Text = Text
    End Sub

    Private Sub lstLocate_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstLocate.SelectedIndexChanged
        Dim i As Integer
        objMouse.ClearLocateFilter()
        For i = 0 To lstLocate.Items.Count - 1
            If lstLocate.GetSelected(i) = True Then
                ' Crapola! Someone skipped numbers in the constants enum and this code has always relied on the values incrementing by one.
                If i < 66 Then
                    objMouse.AddToLocateFilter(i)
                ElseIf i = 66 Or i = 67 Then
                    objMouse.AddToLocateFilter(i + 1)
                ElseIf i = 70 Or i = 71 Then
                    objMouse.AddToLocateFilter(i + 2)
                ElseIf i >= 72 Then
                    objMouse.AddToLocateFilter(i + 4)
                End If
            End If
        Next
    End Sub

    Public Sub SetReadoutDisplay(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Double, ByVal Y As Double, ByVal z As Double, ByVal Window As Object, ByVal KeyPointType As Long, ByVal Graphic As Object)
        ReadoutDisplay(Button, Shift, X, Y, z, Window, KeyPointType, Graphic)
    End Sub


    Private Sub ReadoutDisplay(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Double, ByVal Y As Double, ByVal z As Double, ByVal Window As Object, ByVal KeyPointType As Long, ByVal Graphic As Object)
        Dim UOM As Object

        ' Set the display for which button was clicked.
        txtButton.Text = Button

        ' Set the display for which keyboard button was down when the mouse was clicked.
        txtShift.Text = Shift

        ' Display the name of the window that the mouse was clicked in, but only if it isn't null because pathfinder locate
        ' may be on and in that case, no window is sent.
        If Not Window Is Nothing Then
            txtWindow.Text = Window.Caption
        End If
 
        ' Display the keypoint type when SmartSketch is being used.
        txtKeypType.Text = KeyPointType

        ' Display the position of the mouse.
        If optModelUnits.Checked = True Then
            ' Use the default database units, (meters).
            txtXCoord.Text = Format(X, "0.0000000000")
            txtYCoord.Text = Format(Y, "0.0000000000")
            txtZCoord.Text = Format(z, "0.0000000000")
        Else
            ' Convert the database units to the current display units defined in the
            ' properties dialog using a method on the UnitsOfMeasure object.
            UOM = objDoc.UnitsOfMeasure
            txtXCoord.Text = UOM.FormatUnit(Index:=SolidEdgeConstants.UnitTypeConstants.igUnitDistance, Dbus:=X)
            txtYCoord.Text = UOM.FormatUnit(Index:=SolidEdgeConstants.UnitTypeConstants.igUnitDistance, Dbus:=Y)
            txtZCoord.Text = UOM.FormatUnit(Index:=SolidEdgeConstants.UnitTypeConstants.igUnitDistance, Dbus:=z)
        End If

        ' Determine if a valid object was selected.
        If Not Graphic Is Nothing Then
            txtGraphicObj.Text = "True"

            On Error Resume Next
            txtGraphicName.Text = Graphic.Name
            On Error GoTo 0

            'LastObject = Graphic
        Else
            txtGraphicObj.Text = "False"
            'LastObject = Nothing
        End If

        ' check for a 3d keypoint
        Dim lFlag As Long
        Dim dX As Double
        Dim dY As Double
        Dim dZ As Double
        Call objMouse.PointOnGraphic(lFlag, dX, dY, dZ)
        txtPointOnGraphicFlag.Text = lFlag
        txtPointOnGraphicX.Text = ""
        txtPointOnGraphicY.Text = ""
        txtPointOnGraphicZ.Text = ""
        txtGraphicType.Text = ""
        If lFlag > 0 Then

            ' display the keypoint
            If optModelUnits.Checked = True Then
                ' Use the default database units, (meters).
                txtPointOnGraphicX.Text = Format(dX, "0.0000000000")
                txtPointOnGraphicY.Text = Format(dY, "0.0000000000")
                txtPointOnGraphicZ.Text = Format(dZ, "0.0000000000")
            Else
                ' Convert the database units to the current display units defined in the
                ' properties dialog using a method on the UnitsOfMeasure object.
                UOM = objDoc.UnitsOfMeasure
                txtPointOnGraphicX.Text = UOM.FormatUnit(Index:=SolidEdgeConstants.UnitTypeConstants.igUnitDistance, Dbus:=X)
                txtPointOnGraphicY.Text = UOM.FormatUnit(Index:=SolidEdgeConstants.UnitTypeConstants.igUnitDistance, Dbus:=Y)
                txtPointOnGraphicZ.Text = UOM.FormatUnit(Index:=SolidEdgeConstants.UnitTypeConstants.igUnitDistance, Dbus:=z)
            End If

            ' check out the graphic object
            If Not Graphic Is Nothing Then
                Select Case Graphic.Type
                    Case SolidEdgeConstants.ObjectType.igPart
                        txtGraphicType.Text = "sePart"
                    Case SolidEdgeConstants.ObjectType.igReference
                        Select Case Graphic.object.Type
                            Case SolidEdgeConstants.ObjectType.igPart
                                txtGraphicType.Text = "seReference::igPart"
                                'Case SolidEdgeConstants.ObjectType.igVertex
                                'txtGraphicType.Text = "seReference::igVertex"
                                'Case SolidEdgeFramework.ObjectType.igEdge
                                'txtGraphicType.Text = "seReference::igEdge"
                            Case SolidEdgeConstants.ObjectType.igReference
                                txtGraphicType.Text = "seReference::igReference"
                            Case Else
                                txtGraphicType.Text = "seReference::something else"
                        End Select
                    Case SolidEdgeConstants.ObjectType.igLine2d
                        txtGraphicType.Text = "seLine2d"
                    Case SolidEdgeConstants.ObjectType.igArc2d
                        txtGraphicType.Text = "seArc2d"
                    Case SolidEdgeConstants.ObjectType.igCircle2d
                        txtGraphicType.Text = "seCircle2d"
                    Case SolidEdgeConstants.ObjectType.igDimension
                        txtGraphicType.Text = "seDimension"
                    Case Else
                        txtGraphicType.Text = Graphic.Type
                End Select
            End If

        End If

    End Sub

    Private Delegate Sub CloseFormCallback()
    Public Sub CloseForm()
        ' I posted on the MSDN forums asking why Close() did not actually close the form. It turns out that calling Close when
        ' reacting to a COM event fails because the COM event handler is called from a different thread than the thread that created
        ' the form. A benevolent programmer posted back this code. 

        ' InvokeRequired required compares the thread ID of the calling thread to the thread ID of the creating thread.
        ' If these threads are different, it returns true. Actually since I am currently only calling this sub from an event
        ' handler, I know InvokeRequired will be true.

        ' See the comments in the Terminate event handler for why CloseForm is not called from there. Basically to cover all
        ' possible ways needed to actually end this dialog cleanly, the form would have to call CloseFrom from Terminate and from 
        ' KeyDown1 when the escape key is passed in (keycode is 0x1b = 27 decimal). Even though this form does not (at this time)
        ' call CloseForm, I left the code here in case it needs to be called for some other reason or workflow that comes along.

        If Me.IsHandleCreated AndAlso Me.InvokeRequired Then
            Dim d As New CloseFormCallback(AddressOf CloseForm)
            Me.Invoke(d)
        Else
            Me.Close()
        End If
    End Sub

    ' Developer's note: In .NET a COM event is passed to the client by the .NET framework on a worker thread. One cannot access the controls reliably on a form since the
    ' controls were created on the main thread (along with the form). To verify this check Me.InvokeRequired and see that when you are in a COM event handler that it is
    ' set to True. One can also go to the Visual Studio Debug menu and run the "Exceptions" command and in that dialog check "Common language runtime exceptions". Then
    ' do something in an event handler such as setting the text on a TextBox on the Form. The debugger will kindly show you that an exception has occurred. When this
    ' sample was first converted to .NET, we did not notice this exception becase we did not run with that exception box checked. And when running outside of the 
    ' debugger the form updates as expected. But that appears to just be sheer luck as there is a race condition that is still in play and failures at runtime can
    ' occur. Or so I was told when I posted an issue on the MSDN forums asking why the form fields did not update when running debug but seem to work fine when not
    ' running via the debugger. I did verify using a message box while I worked this issue out that even running the release target outside the debugger that the
    ' InvokeRequired member was indeed True. In any case, the code belows uses delegates as outlined in ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/fxref_system.windows.forms/html/71280002-4fdd-b084-d223-9167cfc0031a.htm

    Public Sub Activate1() Implements SolidEdgeFramework.ISECommandEvents.Activate

    End Sub

    Public Sub Deactivate1() Implements SolidEdgeFramework.ISECommandEvents.Deactivate

    End Sub

    Public Sub Idle(lCount As Integer, ByRef pbMore As Boolean) Implements SolidEdgeFramework.ISECommandEvents.Idle

    End Sub

    Public Sub KeyDown1(ByRef KeyCode As Short, Shift As Short) Implements SolidEdgeFramework.ISECommandEvents.KeyDown

    End Sub

    Public Sub KeyPress1(ByRef KeyAscii As Short) Implements SolidEdgeFramework.ISECommandEvents.KeyPress

    End Sub

    Public Sub KeyUp1(ByRef KeyCode As Short, Shift As Short) Implements SolidEdgeFramework.ISECommandEvents.KeyUp

    End Sub

    Public Sub Terminate() Implements SolidEdgeFramework.ISECommandEvents.Terminate
        ' I want to close the form if the user terminates the command in Solid Edge (e.g, by starting another command or hitting escape key)
        ' But of course I cannot just call Close() since the COM event comes in on a worker thread. So I have to delegate that call.

        ' Dev note: Usually Solid Edge will terminate the command when the user starts another command or hits the
        ' escape key. However when the user hits the escape key and Solid Edge calls the Terminate event, it turns
        ' out Solid Edge is in the busy state and the form cannot disconnect from event sets. We get a deadlock
        ' type of situation as we wait for the ole message filter to retry and retry the call to edge to no avail.
        ' One way to avoid this issue is to call System.Windows.Forms.Application.Exit() in the Terminate method.
        ' The form will still get the form_closed event and the event has returned control to edge by that time and
        ' edge will not be in a busy state. But I can also just rely on edge sending the VK_ESCAPE (0x1b = 27) key
        ' to the key press event and call CloseForm from there. At that time, I can go ahead and close the form instead of 
        ' waiting for the Terminate event to be fired. Solid Edge will fix this issue by firing Terminate when the escape key is 
        ' pressed but that probably won't happen until ST6. Either way, this call to Exit() seems to work nicely. Definitely it
        ' is better than the "End" statement since using that causes an abrubt shutdown (form_closed not called).

        System.Windows.Forms.Application.Exit()

    End Sub

    Public Sub MouseClick1(sButton As Short, sShift As Short, dX As Double, dY As Double, dZ As Double, pWindowDispatch As Object, lKeyPointType As Integer, pGraphicDispatch As Object) Implements SolidEdgeFramework.ISEMouseEvents.MouseClick
        Invoke(mySetTextAction, txtCurMouseEvent, "Mouse Click")
        Invoke(mySetReadoutDisplay, sButton, sShift, dX, dY, dZ, pWindowDispatch, lKeyPointType, pGraphicDispatch)
    End Sub

    Public Sub MouseDblClick(sButton As Short, sShift As Short, dX As Double, dY As Double, dZ As Double, pWindowDispatch As Object, lKeyPointType As Integer, pGraphicDispatch As Object) Implements SolidEdgeFramework.ISEMouseEvents.MouseDblClick
        Invoke(mySetTextAction, txtCurMouseEvent, "Mouse Dbl Click")
        Invoke(mySetReadoutDisplay, sButton, sShift, dX, dY, dZ, pWindowDispatch, lKeyPointType, pGraphicDispatch)
    End Sub

    Public Sub MouseDown1(sButton As Short, sShift As Short, dX As Double, dY As Double, dZ As Double, pWindowDispatch As Object, lKeyPointType As Integer, pGraphicDispatch As Object) Implements SolidEdgeFramework.ISEMouseEvents.MouseDown
        Invoke(mySetTextAction, txtCurMouseEvent, "Mouse Down")
        Invoke(mySetReadoutDisplay, sButton, sShift, dX, dY, dZ, pWindowDispatch, lKeyPointType, pGraphicDispatch)
    End Sub

    Public Sub MouseDrag(sButton As Short, sShift As Short, dX As Double, dY As Double, dZ As Double, pWindowDispatch As Object, DragState As Short, lKeyPointType As Integer, pGraphicDispatch As Object) Implements SolidEdgeFramework.ISEMouseEvents.MouseDrag
        Invoke(mySetTextAction, txtCurMouseEvent, "Mouse Drag")
        Invoke(mySetReadoutDisplay, sButton, sShift, dX, dY, dZ, pWindowDispatch, lKeyPointType, pGraphicDispatch)
    End Sub

    Public Sub MouseMove1(sButton As Short, sShift As Short, dX As Double, dY As Double, dZ As Double, pWindowDispatch As Object, lKeyPointType As Integer, pGraphicDispatch As Object) Implements SolidEdgeFramework.ISEMouseEvents.MouseMove
        Invoke(mySetTextAction, txtCurMouseEvent, "Mouse Move")
        Invoke(mySetReadoutDisplay, sButton, sShift, dX, dY, dZ, pWindowDispatch, lKeyPointType, pGraphicDispatch)
    End Sub

    Public Sub MouseUp1(sButton As Short, sShift As Short, dX As Double, dY As Double, dZ As Double, pWindowDispatch As Object, lKeyPointType As Integer, pGraphicDispatch As Object) Implements SolidEdgeFramework.ISEMouseEvents.MouseUp
        Invoke(mySetTextAction, txtCurMouseEvent, "Mouse Up")
        Invoke(mySetReadoutDisplay, sButton, sShift, dX, dY, dZ, pWindowDispatch, lKeyPointType, pGraphicDispatch)
    End Sub

#Region "UTILITIES"

    Private Sub AddOrRemoveEventHandlers(ByVal Add As Boolean, ByVal CommandEvents As Boolean, ByVal MouseEvents As Boolean)

        Dim i As Type
        Dim EventGuid As Guid

        Try
            If CommandEvents Then
                i = GetType(SolidEdgeFramework.ISECommandEvents)
                EventGuid = i.GUID

                ConnectToEvents(Command_CP, Add, objCmd, Command_CP_Cookie, EventGuid)
            End If

            If MouseEvents Then
                i = GetType(SolidEdgeFramework.ISEMouseEvents)
                EventGuid = i.GUID

                ConnectToEvents(Mouse_CP, Add, objMouse, Mouse_CP_Cookie, EventGuid)
            End If

        Catch ex As Exception

        End Try
    End Sub

    Private Sub ConnectToEvents(ByRef CP As IConnectionPoint, ByVal Add As Boolean, ByVal obj As Object, ByRef Cookie As Integer, ByVal EventGuid As Guid)
        Dim CPC As IConnectionPointContainer

        CPC = obj

        If Not CPC Is Nothing Then

            If Add Then
                CPC.FindConnectionPoint(EventGuid, CP)
                If Not CP Is Nothing Then
                    CP.Advise(Me, Cookie)
                End If
            Else
                If Not CP Is Nothing Then
                    If Not Cookie = -1 Then
                        CP.Unadvise(Cookie)
                        Cookie = -1
                    End If
                End If

            End If
        End If
    End Sub

    ' Use this function to release any com objects. Since this tool is an executable, there is no chance that any other reference outside of
    ' this tool can exist. Hence this routine calls FinalReleaseComObject. That causes the .net runtime to actually call into Solid Edge
    ' to release the underlying (actual) COM object. That means there are no issues when (and if) GC actually runs as it will not attempt
    ' to call into Edge with an object that may no longer exist in Edge. Note once this routine is called, one cannot reference the object,
    ' even from another variable (say one stored in the form). So only call this routine when you are sure you are done with the object.
    ' Call System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) if you are releasing an object in a transient/temporary variable
    ' that may be stored elsewhere for later use.
    Public Sub ReleaseComObject(ByRef obj As Object)
        If Not obj Is Nothing Then
            ' Call FinalReleaseComObject. This call means that this tool MUST NOT try to reference the object again, even from another variable.
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(obj)
            obj = Nothing
        End If
    End Sub

    Private Sub CleanUP()
        ' If user closed the form set Done on the command object to terminate the command in Solid Edge. Also disconnect from the events. 
        ' If the user terminated the command via the Solid Edge UI, say by starting another command or hitting the escape key etc, I
        ' don't really need to set Done to True but I still want to disconnect from events. As long as I disconnect from the events
        ' before I set Done to true things work out. But if I don't, setting Done to true can cause Edge to fire an event (Terminate) to
        ' the command. Also the Idle event is called (a lot) and Solid Edge and this app can both lockup in a classic deadlock
        ' situation.
        AddOrRemoveEventHandlers(False, True, True)

        ReleaseComObject(objApp)
        ReleaseComObject(objMouse)

        objCmd.Done = True
        ReleaseComObject(objCmd)

        ReleaseComObject(objDoc)
    End Sub

#End Region

    Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles ckFrontToBack.CheckedChanged

        Try
            If ckFrontToBack.Checked Then
                objMouse.LocateFrontToBack = True
            Else
                objMouse.LocateFrontToBack = False
            End If
        Catch ex As Exception
            ' FrontToBack added recently to the mouse. Ignore exception as the property probably isn't available in the version of Edge running.
        End Try

    End Sub

    Private Sub ckPathfinder_CheckedChanged(sender As System.Object, e As System.EventArgs) Handles ckPathfinder.CheckedChanged
        Try
            If ckPathfinder.Checked Then
                objMouse.PathfinderLocate = True
            Else
                objMouse.PathfinderLocate = False
            End If
        Catch ex As Exception
            ' PathfinderLocate added recently to the mouse (ST9). Ignore exception as the property probably isn't available in the version of Edge running.
        End Try

    End Sub

    Private Sub gbxReadout_Enter(sender As Object, e As EventArgs) Handles gbxReadout.Enter

    End Sub
End Class
