[RESOLVED] Detecting when IDE Stop button has been clicked-VBForums
Results 1 to 19 of 19

Thread: [RESOLVED] Detecting when IDE Stop button has been clicked

  1. #1

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,287

    Resolved [RESOLVED] Detecting when IDE Stop button has been clicked

    Ok, I've been adapting all my code to use the "new" (ok, not so new) comctl32.dll method of subclassing. If done with care, I've found that I can actually make my subclassing IDE Stop button safe. That was quite amazing to me, and I'd encourage you to read through my other Subclassing thread for more details on this.

    However, my question of the day has to do with detecting when the IDE Stop button has been presses, as opposed to a form just naturally closing. If we're in a compiled program, I won't really care. I'm specifically talking about running programs in the IDE.

    Here's a short (BAS) module to illustrate what I'd like to do:

    Code:
    
    Option Explicit
    '
    Private Const WM_DESTROY = &H2&
    '
    Private Declare Function SetWindowSubclass Lib "comctl32.dll" Alias "#410" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, Optional ByVal dwRefData As Long) As Long
    Private Declare Function GetWindowSubclass Lib "comctl32.dll" Alias "#411" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, pdwRefData As Long) As Long
    Private Declare Function RemoveWindowSubclass Lib "comctl32.dll" Alias "#412" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long) As Long
    Private Declare Function DefSubclassProc Lib "comctl32.dll" Alias "#413" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    '
    '
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As Long)
    Private Type POINTAPI
        x As Long
        y As Long
    End Type
    Private Type MINMAXINFO
        ptReserved As POINTAPI
        ptMaxSize As POINTAPI
        ptMaxPosition As POINTAPI
        ptMinTrackSize As POINTAPI
        ptMaxTrackSize As POINTAPI
    End Type
    '
    
    Public Function IdeStopButton() As Boolean
    
        ' What might I put here to figure out whether the IDE "Stop"
        ' button was pressed, as opposed to just the form naturally closing.
    
    End Function
    
    
    Public Sub SubclassFormFixedWidth(frm As VB.Form)
        Call SetWindowSubclass(frm.hWnd, AddressOf FixedWidth_Proc, frm.hWnd, CLng(frm.Width \ Screen.TwipsPerPixelX))
    End Sub
    
    Private Function FixedWidth_Proc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
        If uMsg = WM_DESTROY Then
            Call RemoveWindowSubclass(hWnd, AddressOf_FixedWidth_Proc, hWnd)
            FixedWidth_Proc = DefSubclassProc(hWnd, uMsg, wParam, lParam)
    
    
            Debug.Print IdeStopButton
    
    
            Exit Function
        End If
        '
        Dim PelWidth As Long
        Dim MMI As MINMAXINFO
        Const WM_GETMINMAXINFO As Long = &H24&
        '
        ' And now we force our width to not change.
        If uMsg = WM_GETMINMAXINFO Then
            ' Force the form to stay at initial size.
            PelWidth = dwRefData
            CopyMemory MMI, ByVal lParam, LenB(MMI)
            MMI.ptMinTrackSize.x = PelWidth
            MMI.ptMaxTrackSize.x = PelWidth
            CopyMemory ByVal lParam, MMI, LenB(MMI)
            Exit Function ' If we process the message, we must return 0 and not let more hook code execute.
        End If
        '
        ' Give control to other hooks, if they exist.
        FixedWidth_Proc = DefSubclassProc(hWnd, uMsg, wParam, lParam)
    End Function
    
    Private Function AddressOf_FixedWidth_Proc() As Long
        AddressOf_FixedWidth_Proc = ProcedureAddress(AddressOf FixedWidth_Proc)
    End Function
    
    Private Function ProcedureAddress(AddressOf_TheProc As Long)
        ' A private "helper" function for writing the AddressOf_... functions.
        ProcedureAddress = AddressOf_TheProc
    End Function
    
    To use it, just throw it into a BAS module and then throw the following into the default Form1's code:

    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
        SubclassFormFixedWidth Me
    End Sub
    
    
    It's that IdeStopButton function that I'd like to get working if anyone can figure it out. Even though, when the Stop button is pressed, all COM objects are un-instantiated and all dynamic arrays are erased, that still doesn't give me a way to do it.

    Here's one thing I tried, but it didn't work:

    Code:
    
    Public Function IdeStopButton() As Boolean
        On Error GoTo Stopping
        Dim bb() As Byte
        ReDim bb(0)
        Exit Function
    Stopping:
        IdeStopButton = True
    End Function
    
    I also tried this, but no cigar:

    Code:
    
    Public Function IdeStopButton() As Boolean
        On Error GoTo Stopping
        Dim coll As Collection
        Set coll = New Collection
        coll.Add "asdf", "asdf"
        Exit Function
    Stopping:
        IdeStopButton = True
    End Function
    
    
    Any idea are much appreciated.

    Also, I'd like to work this out without any machine-code insertion, but I suppose I'm willing to consider it if nothing else can be worked out.

    Elroy
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  2. #2
    Hyperactive Member
    Join Date
    May 2011
    Posts
    370

    Re: Detecting when IDE Stop button has been clicked

    Not sure but you can check the value from `EbMode` when on Stopping debug session outside tear-down code reaches your callback routine in the IDE.
    Code:
    Private Declare Function EbMode Lib "vba6" () As Long
    Probably also check if `vb6.dll` is loaded before calling it (not to fail when run compiled outside the IDE).

    Edit: Now I see this is what The trick proposed in the previous thread.

    cheers,
    </wqw>

  3. #3

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,287

    Re: Detecting when IDE Stop button has been clicked

    Hmmm, ok, I'm still stuck. Wqweto, good ideas, but no cigar.

    The EbMode returns 1 in both cases (whether we clicked "X" to close form, or whether we hit the IDE Stop button).

    I also threw in the following module, and the exact same set of dependencies is listed in both cases. I listed them inside of the IdeStopButton function that I'm trying to write.

    Code:
    
    Option Explicit
    '
    Private Const MAX_PATH = 260
    Private Type MODULEENTRY32
        dwSize As Long
        th32ModuleID As Long
        th32ProcessID As Long
        GlblcntUsage As Long
        ProccntUsage As Long
        modBaseAddr As Long
        modBaseSize As Long
        hModule As Long
        szModule As String * 256
        szExePath As String * MAX_PATH
    End Type
    '
    Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal lFlags As Long, ByVal lProcessID As Long) As Long
    Private Declare Function Module32First Lib "kernel32" (ByVal hSnapShot As Long, lpMe32 As MODULEENTRY32) As Long
    Private Declare Function Module32Next Lib "kernel32" (ByVal hSnapShot As Long, lpMe32 As MODULEENTRY32) As Long
    Private Declare Sub CloseHandle Lib "kernel32" (ByVal hPass As Long)
    Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
    '
    
    Private Sub Command1_Click()
        Dim Deps() As String
        Dim i As Long
        '
        Deps = GetMyProcessModules
        If IsDimmedStr(Deps) Then
            For i = LBound(Deps) To UBound(Deps)
                Debug.Print Deps(i)
            Next i
        End If
    End Sub
    
    Private Sub Command2_Click()
        MsgBox ImUsingThisModule("mscomctl.ocx")
    End Sub
    
    Public Function ImUsingThisModule(ByVal sModuleName As String) As Boolean
        ' Do NOT include a path.
        Dim Deps() As String
        Dim i As Long
        '
        ' Add a back-slash to keep from matching any partial names.
        sModuleName = "\" & UCase$(sModuleName)
        '
        Deps = GetMyProcessModules
        If IsDimmedStr(Deps) Then
            For i = LBound(Deps) To UBound(Deps)
                If UCase$(Right$(Deps(i), Len(sModuleName))) = sModuleName Then
                    ImUsingThisModule = True
                    Exit Function
                End If
            Next i
        End If
        ' If we fall out, it wasn't found.
    End Function
    
    Public Function GetMyProcessModules() As String()
        ' This function retrieves all dependencies of the program that calls it.
        ' It returns the complete path, so it can be used to determine which dependencies are
        ' side-by-side versus ones pulled from the registry.
        '
        Dim Me32 As MODULEENTRY32
        Dim lRet As Long
        Dim lhSnapShot As Long
        Dim pID As Long
        Dim iLen As Integer
        Dim sModule As String
        Dim DependencyList() As String
        Dim iCount As Long
        Const TH32CS_SNAPMODULE = &H8
        '
        pID = GetCurrentProcessId ' Get our process ID.
        '
        ' Snapshot tool will be used to get complete list of dependencies being used.
        lhSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pID)
        If lhSnapShot = 0 Then Exit Function
        '
        ' Get the dependencies.
        ReDim DependencyList(1 To 100)
        Me32.dwSize = LenB(Me32)                ' Set up the Module Entry structure.
        lRet = Module32First(lhSnapShot, Me32)  ' Call the "First" function to start the loop.
        Do While lRet
            If Me32.th32ProcessID = pID Then
                '
                ' If the dependency belongs to us, add it to the list.
                iCount = iCount + 1
                If iCount > UBound(DependencyList) Then ReDim Preserve DependencyList(1 To UBound(DependencyList) + 100)
                DependencyList(iCount) = RTrimNull(Me32.szExePath)
            End If
            ' Get next dependency and loop.
            lRet = Module32Next(lhSnapShot, Me32)
        Loop
        '
        ' All done, clean up and get out.
        If iCount Then
            ReDim Preserve DependencyList(1 To iCount)
        Else
            Erase DependencyList
        End If
        CloseHandle lhSnapShot
        GetMyProcessModules = DependencyList
    End Function
    
    Public Function RTrimNull(s As String) As String
        Dim i As Integer
        i = InStr(s, vbNullChar)
        If i Then
            RTrimNull = Left$(s, i - 1)
        Else
            RTrimNull = s
        End If
    End Function
    
    Public Function IsDimmedStr(TheArray() As String) As Boolean
        ' This won't fail on one of the (0 to -1) arrays.
        On Error Resume Next
            IsDimmedStr = (LBound(TheArray) = LBound(TheArray)) ' Will error (leaving IsDimmedStr = False) if not dimensioned.
        On Error GoTo 0
    End Function
    
    
    So ... it's still back to the drawing board. Any/all ideas are excitedly welcomed.

    Elroy

    EDIT1: Anyone can play around with the above module by just putting two buttons on a form, and then throwing that code into the form.

    EDIT2: A bit more info: The above list of dependencies is the same when unloading a window, regardless of whether it's the last window being unloaded in the program. In other words, if I hit the Stop button VERSUS unloading a form (with other forms still showing), the list of dependencies is still identical.
    Last edited by Elroy; Apr 21st, 2017 at 12:25 PM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  4. #4
    Fanatic Member
    Join Date
    Feb 2015
    Posts
    863

    Re: Detecting when IDE Stop button has been clicked

    Try that:
    Code:
    Option Explicit
    
    Private Declare Function FindWindow Lib "user32" _
                             Alias "FindWindowA" ( _
                             ByVal lpClassName As String, _
                             ByVal lpWindowName As String) As Long
    Private Declare Function SetWindowLong Lib "user32" _
                             Alias "SetWindowLongA" ( _
                             ByVal hWnd As Long, _
                             ByVal nIndex As Long, _
                             ByVal dwNewLong As Long) As Long
    Private Declare Function CallWindowProc Lib "user32" _
                             Alias "CallWindowProcA" ( _
                             ByVal lpPrevWndFunc As Long, _
                             ByVal hWnd As Long, _
                             ByVal Msg As Long, _
                             ByVal wParam As Long, _
                             ByVal lParam As Long) As Long
    
    Private Const GWL_WNDPROC   As Long = (-4)
    
    Private pfnPrevProc     As Long
    Private hWndIDE         As Long
    
    Public Function HijackStopButton() As Boolean
    
        hWndIDE = FindWindow("wndclass_desked_gsk", vbNullString)
        If hWndIDE = 0 Then Exit Function
        
        pfnPrevProc = SetWindowLong(hWndIDE, GWL_WNDPROC, AddressOf WndProc)
        
        HijackStopButton = pfnPrevProc
        
    End Function
    
    Public Sub RestoreStopButtonBehavior()
        
        If pfnPrevProc = 0 Or hWndIDE = 0 Then Exit Sub
        
        SetWindowLong hWndIDE, GWL_WNDPROC, pfnPrevProc
        
    End Sub
    
    Private Function WndProc( _
                     ByVal hWnd As Long, _
                     ByVal lMsg As Long, _
                     ByVal wParam As Long, _
                     ByVal lParam As Long) As Long
                         
        If lMsg = &H1044 And wParam = &H33 Then
        
            MsgBox "Stop button was pressed", vbInformation
            RestoreStopButtonBehavior
            
        Else
        
            WndProc = CallWindowProc(pfnPrevProc, hWnd, lMsg, wParam, lParam)
            
        End If
     
    End Function
    When you press "Stop" it'll show message and you can handle the needed things. The second pressing ends execution. I don't test it, i just see throught Spy+ windows messages and see that when you select commands on panel or menu it calls 0x1044 message with different second parameter. 0x33 - Stop button.

  5. #5
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,605

    Re: Detecting when IDE Stop button has been clicked

    couldn't you just use the IAT hook you posted from Krool's CCRP.

    @the Trick the method you posted works in VBA also. (different window class and message though)
    Last edited by DEXWERX; Apr 21st, 2017 at 12:48 PM.
    Imagine what it would be like to set breakpoints in, or step through subclassing code;
    and then being able to hit stop/end/debug or continue, without crashing the IDE.

    VB6.tlb | Bulletproof Subclassing in the IDE (no thunks/assembly/DEP issues)

  6. #6

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,287

    Re: Detecting when IDE Stop button has been clicked

    I actually got it. It was WAY easier than I thought it'd be. It turns out, when you hit Stop, all global/module variables are reset (in addition to all COMs being un-instantiated and all dynamic arrays erased). And that all happens BEFORE the static subclassing gets executed to allow you to un-subclass.

    Therefore, all I had to do was set a boolean when I subclassed, and check it whenever I want.

    Here's the code that shows it. It reports "False" when clicking the form's "X", and it reports "True" when the Stop button was clicked.

    Code:
    
    Option Explicit
    '
    Private Const WM_DESTROY = &H2&
    '
    Private Declare Function SetWindowSubclass Lib "comctl32.dll" Alias "#410" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, Optional ByVal dwRefData As Long) As Long
    Private Declare Function GetWindowSubclass Lib "comctl32.dll" Alias "#411" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, pdwRefData As Long) As Long
    Private Declare Function RemoveWindowSubclass Lib "comctl32.dll" Alias "#412" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long) As Long
    Private Declare Function DefSubclassProc Lib "comctl32.dll" Alias "#413" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    '
    '
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As Long)
    Private Type POINTAPI
        x As Long
        y As Long
    End Type
    Private Type MINMAXINFO
        ptReserved As POINTAPI
        ptMaxSize As POINTAPI
        ptMaxPosition As POINTAPI
        ptMinTrackSize As POINTAPI
        ptMaxTrackSize As POINTAPI
    End Type
    '
    Dim bSetWhenSubclassing_UsedByIdeStop As Boolean
    '
    
    Public Function IdeStopButton() As Boolean
        IdeStopButton = Not bSetWhenSubclassing_UsedByIdeStop
    End Function
    
    
    Public Sub SubclassFormFixedWidth(frm As VB.Form)
        Call SetWindowSubclass(frm.hWnd, AddressOf FixedWidth_Proc, frm.hWnd, CLng(frm.Width \ Screen.TwipsPerPixelX))
        ReDim bbStopCheck(0)
        bSetWhenSubclassing_UsedByIdeStop = True
    End Sub
    
    Private Function FixedWidth_Proc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
        If uMsg = WM_DESTROY Then
            Call RemoveWindowSubclass(hWnd, AddressOf_FixedWidth_Proc, hWnd)
            FixedWidth_Proc = DefSubclassProc(hWnd, uMsg, wParam, lParam)
    
    
    
            Debug.Print IdeStopButton
    
    
    
    
            Exit Function
        End If
        '
        Dim PelWidth As Long
        Dim MMI As MINMAXINFO
        Const WM_GETMINMAXINFO As Long = &H24&
        '
        ' And now we force our width to not change.
        If uMsg = WM_GETMINMAXINFO Then
            ' Force the form to stay at initial size.
            PelWidth = dwRefData
            CopyMemory MMI, ByVal lParam, LenB(MMI)
            MMI.ptMinTrackSize.x = PelWidth
            MMI.ptMaxTrackSize.x = PelWidth
            CopyMemory ByVal lParam, MMI, LenB(MMI)
            Exit Function ' If we process the message, we must return 0 and not let more hook code execute.
        End If
        '
        ' Give control to other hooks, if they exist.
        FixedWidth_Proc = DefSubclassProc(hWnd, uMsg, wParam, lParam)
    End Function
    
    Private Function AddressOf_FixedWidth_Proc() As Long
        AddressOf_FixedWidth_Proc = ProcedureAddress(AddressOf FixedWidth_Proc)
    End Function
    
    Private Function ProcedureAddress(AddressOf_TheProc As Long)
        ' A private "helper" function for writing the AddressOf_... functions.
        ProcedureAddress = AddressOf_TheProc
    End Function
    
    

    And again, to play, just throw the above in a BAS module, and throw the following into a Form1.

    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
        SubclassFormFixedWidth Me
    End Sub
    
    
    Say Trick, I'll be sure and look at your code, as I'm always fascinated with what you come up with.

    Thanks to ALL,
    Elroy

    EDIT1: With this last piece, I truly believe I'm going to be able to make my subclassing about as IDE safe as is possible.

    EDIT2: Wow, I feel like I'm living on the edge, but this has allowed me to get "On Error" totally out of all my subclassing procedures. And, truth be told, when I'm actually specifically debugging a subclassing procedure, I'd rather that it crash so that I can fix it, even if it does crash the IDE in the process. Hopefully, once I've debugged it, it'll all be IDE safe again. I doubt many say this about subclassing, but this stuff is COOL!
    Last edited by Elroy; Apr 21st, 2017 at 01:31 PM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  7. #7
    Fanatic Member
    Join Date
    Feb 2017
    Posts
    637

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    What about when getting an error and clicking terminate?
    What about when you change a declaration statement and VB prompts that the project need to be restarted?
    And Shift+F5...
    It's not just the stop button.

    Why don't you put the subclassing in a dll?

  8. #8

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,287

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Ok, just for posterity, here's how I used it all. I deleted long comments at the top to get it to fit in the post, but this is my model for writing totally IDE safe subclassing:

    Code:
    
    Option Explicit
    '
    Private Const WM_DESTROY = &H2&
    '
    Private Declare Function SetWindowSubclass Lib "comctl32.dll" Alias "#410" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, Optional ByVal dwRefData As Long) As Long
    Private Declare Function GetWindowSubclass Lib "comctl32.dll" Alias "#411" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long, pdwRefData As Long) As Long
    Private Declare Function RemoveWindowSubclass Lib "comctl32.dll" Alias "#412" (ByVal hWnd As Long, ByVal pfnSubclass As Long, ByVal uIdSubclass As Long) As Long
    Private Declare Function NextSubclassProcOnChain Lib "comctl32.dll" Alias "#413" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    'Private Declare Function DefSubclassProc Lib "comctl32.dll" Alias "#413" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    '
    Dim bSetWhenSubclassing_UsedByIdeStop As Boolean ' Never goes false once set by first subclassing, unless IDE Stop button is clicked.
    '
    '**************************************************************************************
    ' The following MODULE level stuff is specific to individual subclassing needs.
    '**************************************************************************************
    '
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As Long)
    '
    Private Type POINTAPI
        x As Long
        y As Long
    End Type
    Private Type MINMAXINFO
        ptReserved As POINTAPI
        ptMaxSize As POINTAPI
        ptMaxPosition As POINTAPI
        ptMinTrackSize As POINTAPI
        ptMaxTrackSize As POINTAPI
    End Type
    '
    
    '**************************************************************************************
    '**************************************************************************************
    '**************************************************************************************
    '
    ' Generic subclassing procedures (used in many of the specific subclassing).
    '
    '**************************************************************************************
    '**************************************************************************************
    '**************************************************************************************
    
    Private Sub SubclassSomeWindow(hWnd As Long, AddressOf_ProcToHook As Long, Optional dwRefData As Long)
        ' This just always uses hWnd for uIdSubclass, as we never have a need to subclass the same window to the same proc.
        ' The uniqueness is pfnSubclass and uIdSubclass (second and third argument below).
        '
        ' This can be called AFTER the initial subclassing to update dwRefData.
        '
        bSetWhenSubclassing_UsedByIdeStop = True
        Call SetWindowSubclass(hWnd, AddressOf_ProcToHook, hWnd, dwRefData)
    End Sub
    
    Private Function GetSubclassRefData(hWnd As Long, AddressOf_ProcToHook As Long) As Long
        ' This one is used only to fetch the optional dwRefData you may have specified when calling SubclassSomeWindow.
        ' Typically this would only be used by the hooked procedure, but it is available to anyone.
        Call GetWindowSubclass(hWnd, AddressOf_ProcToHook, hWnd, GetSubclassRefData)
    End Function
    
    Private Function IsSubclassed(hWnd As Long, AddressOf_ProcToHook As Long) As Boolean
        ' This just tells us we're already subclassed.
        Dim dwRefData As Long
        IsSubclassed = GetWindowSubclass(hWnd, AddressOf_ProcToHook, hWnd, dwRefData) = 1&
    End Function
    
    Private Sub UnSubclassSomeWindow(hWnd As Long, AddressOf_ProcToHook As Long)
        ' Only needed if we specifically want to un-subclass before we're closing the form (or control),
        ' otherwise, it's automatically taken care of when the window closes.
        '
        ' Be careful, some subclassing may require additional cleanup that's not done here.
        Call RemoveWindowSubclass(hWnd, AddressOf_ProcToHook, hWnd)
    End Sub
    
    Private Function ProcedureAddress(AddressOf_TheProc As Long)
        ' A private "helper" function for writing the AddressOf_... functions (see above notes).
        ProcedureAddress = AddressOf_TheProc
    End Function
    
    Private Function DummyProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
        ' This could be used to just use comctl32.dll to store data for us in the dwRefData.
        ' However, we'd do better to create specific "Dummy" procedure, and use the same hWnd.
        ' That way, there'd be no chance of a collision on hWnd.
        '
        ' Give control to other hooks, if they exist.
        DummyProc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
    End Function
    
    Private Function IdeStopButtonClicked() As Boolean
        IdeStopButtonClicked = Not bSetWhenSubclassing_UsedByIdeStop
    End Function
    
    '**************************************************************************************
    '**************************************************************************************
    '**************************************************************************************
    '
    ' The following are our functions to be hooked, along with their AddressOf_... function.
    ' All of the following should be Private to make sure we don't accidentally call it,
    ' except for the first procedure that's actually used to initiate the subclassing.
    '
    '**************************************************************************************
    '**************************************************************************************
    '**************************************************************************************
    
    Public Sub SubclassFormFixedWidth(frm As VB.Form)
        SubclassSomeWindow frm.hWnd, AddressOf FixedWidth_Proc, CLng(frm.Width \ Screen.TwipsPerPixelX)
    End Sub
    
    Private Function FixedWidth_Proc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal uIdSubclass As Long, ByVal dwRefData As Long) As Long
        If uMsg = WM_DESTROY Then
            UnSubclassSomeWindow hWnd, AddressOf_FixedWidth_Proc
            FixedWidth_Proc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
    
            If Not IdeStopButtonClicked Then
    
                ' You could do other clean-up here if you have any.
    
            End If
    
            Exit Function
        End If
        If IdeStopButtonClicked Then ' Protect the IDE.  Don't execute any specific stuff if we're stopping.  We may run into COM objects or other problems.
            FixedWidth_Proc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
            Exit Function
        End If
        '
        ' THE ABOVE TWO CHECKS SHOULD BE IN ANY NEW SUBCLASSING PROCEDURES TO PROTECT THE IDE.
        '
        Dim PelWidth As Long
        Dim MMI As MINMAXINFO
        Const WM_GETMINMAXINFO As Long = &H24&
        '
        ' And now we force our width to not change.
        If uMsg = WM_GETMINMAXINFO Then
            ' Force the form to stay at initial width.
            PelWidth = dwRefData
            '
            CopyMemory MMI, ByVal lParam, LenB(MMI)
            '
            MMI.ptMinTrackSize.x = PelWidth
            MMI.ptMaxTrackSize.x = PelWidth
            '
            CopyMemory ByVal lParam, MMI, LenB(MMI)
            Exit Function ' If we process the message, we must return 0 and not let more hook code execute.
        End If
        '
        ' Give control to other hooks, if they exist.
        FixedWidth_Proc = NextSubclassProcOnChain(hWnd, uMsg, wParam, lParam)
    End Function
    
    Private Function AddressOf_FixedWidth_Proc() As Long
        AddressOf_FixedWidth_Proc = ProcedureAddress(AddressOf FixedWidth_Proc)
    End Function
    
    
    And still ...

    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
        SubclassFormFixedWidth Me
    End Sub
    
    ... to see it in action.

    Also, as DexWerx pointed out, once you know your module name, you can do further cleanup with "AddressOf ModuleName.ThisProc".

    Enjoy,
    Elroy
    Last edited by Elroy; Apr 21st, 2017 at 01:53 PM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  9. #9
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    16,736

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Quote Originally Posted by Elroy View Post
    ... this is my model for writing totally IDE safe subclassing
    Not quite so fast sparky. Both scenarios I gave you in your other thread still crash on my system & that's using your latest tweaks above. System: Win10
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  10. #10

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,287

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Hahaha, I love being called "sparky". I've got a dog named Sparky, and he's something else.

    And, ok ok. I did see your post, but I didn't take a close look. I'll do that now.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  11. #11

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,287

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Drat, ok, I must agree that LaVolpe is correct. Let me outline the two scenarios here ...

    1) Use the example above (post #8). Put a command button on Form1, and then put this code in it ...

    Code:
    
    Private Sub Command1_Click()
        Error 1234
    End Sub
    
    Execute the project, click the command button (causing a runtime error), and then click "End" on the error reporting form that comes up. Bam ... IDE crash.


    2) Use the example above (post #8). Add a second form (Form2). On the first form (Form1), add a command button, and then put this code in it ...

    Code:
    
    Private Sub Command1_Click()
        Form2.Show vbModal
    End Sub
    
    Execute the project, click the command button (which will modally load Form2), and then click the IDE "Stop" button. Bam ... IDE crash.

    ---------------------------

    Interestingly, in the first scenario, even if I click "Debug", I'm losing my "Run To Cursor" and "Set Next Statement" options, which I frequently use. I'm not at all sure what that's about. I can still edit the code "on the fly" (pause mode), but that's not so good.

    Hmmm ... ok, it's not perfect. I still think I'll try it and see how things go. At this point, I'm too committed. Also, I like that ComCtl32.dll keeps track of what's subclassed rather than me having to do it, as evidenced by no hWndOrig() arrays (or collections) in my subclassing module.

    Sighs,
    Elroy

    EDIT1: Just a further FYI. I tested both of the above scenarios, and the subclassed procedure is NOT getting called with WM_DESTROY, which is what's causing the crash. It's like they're both more similar to executing an END statement than normal execution.
    Last edited by Elroy; Apr 21st, 2017 at 03:04 PM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  12. #12
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    16,736

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Interestingly, in the first scenario, even if I click "Debug", I'm losing my "Run To Cursor" and "Set Next Statement" options, which I frequently use. I'm not at all sure what that's about. I can still edit the code "on the fly", but that's not so good.
    If you see the yellow highlighting, you can click and drag (from the IDE window's left border) the yellow arrow and move it to the next line or wherever else in the routine you need to go.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  13. #13
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    16,736

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Elroy, don't spend too much time looking for the perfect fix if wanting to stay with the common controls subclassing APIs.

    If you are using subclassing, you'll know not to hit end. You'll know you can move from the offending line to get out of the routine and stave off a crash until you can shut down normally.

    If you are posting a project where subclassing is in use, simply warn any potential onlookers what End/Stop can do. You can optionally turn off subclassing while in IDE but demand it when compiled. A simple routine looks something like this. Debug statements are not compiled:

    Code:
    Private Function pvIsNotCompiled(Value As Long) As Boolean
        Value = 1 ' set to any non-zero value that means something to your calling routine
        pvIsNotCompiled = True
    End Function
    
    >> now in your routines where you may want to skip subclassing for IDE (temporarily or semi-permanently...
    
        Dim lValue As Long   ' within routine that initiates subclassing
        ....
        Debug.Assert pvIsNotCompiled(lValue)  ' rem out this line to allow subclassing in IDE
        If lValue Then Exit Sub/Function
        ...
    edited: didn't use the same function name, but here is a copy & paste from one of my projects informing users of the risk
    Code:
    ....
        Debug.Assert pvIsUncompiled(lValue) ' don't subclass in IDE; rem this line out if you wish, just don't hit STOP
        If lValue = 1& Then
            If MsgBox("Subclassing will begin to restrict form sizing." & vbCrLf & _
                "Click YES to allow subclassing and never press the END button." & vbCrLf & _
                "Click NO to prevent subclassing." & vbCrLf & vbCrLf & _
                "Note: This is not displayed when compiled." & vbCrLf & "You can stop displaying this by " & _
                "commenting out the Debug.Assert line in modWork.SetSizeRestrictions, allowing subclassing in IDE.", vbYesNo + vbInformation, "Confirmation") = vbNo Then Exit Function
        End If
    ...
    Last edited by LaVolpe; Apr 21st, 2017 at 03:33 PM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  14. #14

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,287

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Hi LaVolpe,

    Yeah, I was previously doing most of that before I changed over to comctl32.dll. And yeah, I virtually never hit "End" on the runtime-error window because I've probably got databases and all kinds of other things open that I'd like to shut down normally.

    Regarding doing an InIDE() check, I'm not so keen on that one. I'd rather run as "close as possible" to a compiled program while in the IDE. That way, I can catch any/all bugs that users may bump into.

    Also, I didn't say it earlier, but I very seldom use modal forms. For all my "involved" forms, they're virtually never modal. There is one spot that's an exception to that, and you've just explained why that spot crashed when I hit the "Stop" button. And, truth be told, I quite seldom hit the "Stop" button either. However, at times, it's nice to have that option. It's also nice to have a good understanding of this stuff.

    All The Best,
    Elroy
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  15. #15
    Fanatic Member
    Join Date
    Feb 2017
    Posts
    637

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    What about Shift+F9?

    One of the worse things that I have when the subclasser is in the same program is the inability to check the stack calls, to trace from where the program got there.
    If I hit Shift+F9(*) to see it, I cannot go back to the code any more, I need to close VB6 from the task manager.

    (*) Edit: I meant Ctrl+L, but also hitting Shift+F9 to check a variable value has the same effect.
    Last edited by Eduardo-; Apr 22nd, 2017 at 05:07 PM.

  16. #16
    Fanatic Member
    Join Date
    Feb 2015
    Posts
    863

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Why do you want to use no ASM thunk? That thunk is required only in IDE. I made my class for subclassing windows and it works quite stable as well. It uses SetWindowSubclass api as well. You can move IDE-check code to a standard module and get rid on class. That class uses two asm thunks: IDE-checking and stack builder. When you compile an end application IDE-checking is removed, i.e. you can use NO asm code in the end application.

  17. #17
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    16,736

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    And if you really want safe IDE subclassing, as mentioned earlier, Thunks work extremely well without relying on an activex dll or another 3rd party dll. I personally am quite comfortable with subclassing gotchas and don't feel the need for a class like "the trick" offers, but it would offer a nearly 100% IDE-safe environment for subclassing. If he's offering it, you don't need to learn ASM to roll your own. Its not gonna fix that CopyMemory call to an invalid pointer but that's a different topic.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  18. #18

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,287

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Wait ... CopyMemory to an invalid pointer?

    Code:
    
            CopyMemory MMI, ByVal lParam, LenB(MMI)
            '
            MMI.ptMinTrackSize.x = PelWidth
            MMI.ptMaxTrackSize.x = PelWidth
            '
            CopyMemory ByVal lParam, MMI, LenB(MMI)
    
    Somewhere in there? That's really old code (admittedly cut down somewhat for the example), but it seems to work.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  19. #19

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    3,287

    Re: [RESOLVED] Detecting when IDE Stop button has been clicked

    Also, if I'm going to insert some machine-code, I've got to keep track of what's been subclassed. I actually thought about this.

    Is there some way to get comctl32.dll to enumerate what's been subclassed? That would be fantastic.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width

Survey posted by VBForums.