Results 1 to 16 of 16

Thread: How do I allow subclassing messages to occur during Do Loop without using DoEvents?

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2017
    Posts
    761

    How do I allow subclassing messages to occur during Do Loop without using DoEvents?

    Hello!

    I am using Ctrl + C to copy the currently selected text from an external application.

    I am using subclassing to be notified when the clipboard contents has changed and the text is present in the clipboard.

    Once WM_CHANGECBCHAIN is received, I check the clipboard's content.

    My current approach looks like this:

    Code:
    Call SendCtrlC()
    
    Dim lStart&
    lStart = GetTickCount()
    
    Do
        Dim lTaken&
        lTaken = GetTickCount() - lStart
    
        If lTaken > 3000 Then '3 seconds have passed. something must have gone wrong. Stop waiting for the clipboard change event
            Exit Do
        End If
    
        If (m_bCBChangeOccured) Then 'Does not work!
           'The problem is that m_bCBChangeOccured never becomes True in this loop! Because during the loop, we are not notified of the clipboard changes. Only if I use DoEvents, but I may not use DoEvents
           s = Clipboard.GetText()
           Exit Do
        End If
     
        'I can NOT use DoEvents() due to restrictions in coding standards which I have to follow
        'However, if I don't use DoEvents, I don't get the WM_CHANGECBCHAIN message
        'What could I do to let WM messages to occur within this loop?
    
    Loop
    And here in my form I have:

    Code:
    Private Sub iSubclass_WndProc(ByVal bBefore As Boolean, bHandled As Boolean, lReturn As Long, ByVal lng_hWnd As Long, ByVal uMsg As eMsg, ByVal wParam As Long, ByVal lParam As Long, lParamUser As Long)
       Select Case uMsg
       Case WM_CHANGECBCHAIN
          ' If the next viewer window is closing, repair the chain:
          m_hWndNextViewer = lParam
          If (m_hWndNextViewer <> 0) Then
             ' Otherwise if there is a next window, pass the message on:
             SendMessageByLong m_hWndNextViewer, uMsg, wParam, lParam
          End If
          lReturn = 0 'ISubclass_WindowProc = 0
            
       Case WM_DRAWCLIPBOARD
          ' the content of the clipboard has changed.
          ' We raise a ClipboardChanged message and pass the message on:
          m_bCBChangeOccured = True
          RaiseEvent ClipboardChanged
    
    (...)
    End Sub
    Last edited by tmighty2; Apr 21st, 2022 at 10:51 PM.

  2. #2
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,294

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    Are you after the text in a specific window ? Or trying to monitor the clipboard for changes made by any program?

    Sending ctrl c I assume you already have the target windows hwnd

    You can use windows messages to get the text or selection of a traditional edit controls text, list boxes, treeview, etc. limitation won’t work with some fancy stuff like electron apps, wpf etc
    Last edited by dz32; Apr 22nd, 2022 at 07:15 AM.

  3. #3
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    ...did you register as a clipboard viewer? You only get that message if you register for it.

    But if all you want to do is monitor text you should just register as a format listener instead of a viewer.

    Code:
    Private Declare Function AddClipboardFormatListener Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function RemoveClipboardFormatListener Lib "user32" (ByVal hwnd As Long) As Long
    Then all you have to do is listen for WM_CLIPBOARDUPDATE.

  4. #4

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2017
    Posts
    761

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    Yes, I am using a clipboard viewer.

    The question however is how I can be notified of the WM_CLIPBOARDUPDATE message when I am in the loop.
    As I said, I can't use DoEvents.

  5. #5
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    You could call the API calls that DoEvents uses. And technically, that wouldn't be using DoEvents, but it's sort of the same thing.

    Here's some code that might get you pointed in the right direction:

    Code:
    Public Sub FastDoEvents()
        Dim uMsg As MsgType
        '
        Do While PeekMessage(uMsg, 0&, 0&, 0&, PM_REMOVE)   ' Reads and deletes message from queue.
            TranslateMessage uMsg                           ' Translates virtual-key messages into character messages.
            DispatchMessage uMsg                            ' Dispatches a message to a window procedure.
        Loop
    End Sub
    Now, I'd have to read the MSDN for those APIs, but you can call them, filtering/monitoring for only specific Windows messages, leaving all else alone that's in the queue. That's probably what you want to do.
    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. To all, peace and happiness.

  6. #6
    Fanatic Member
    Join Date
    Apr 2021
    Posts
    616

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    Quote Originally Posted by tmighty2 View Post
    As I said, I can't use DoEvents.
    Can't or won't? Repeatedly using DoEvents in a loop is counter-productive, I agree, as it seriously affects the speed of the loop...but a carefully crafted system that sporadically uses it is going to have less of an effect. I generally set a variable to store the current time, then compare the current time to that variable and if they change I update the variable to the new time and call DoEvents (this generally causes DoEvents to be called ~1 time per second if the loop is running quickly enough). It isn't perfect, but it does force the loop to return control to the other elements of the app for a split second and let things catch up (like any updates).

    Code:
    If Time <> ti Then DoEvents: ti = Time
    That code above is literally it...it stores the current time in "ti" and checks to see if time is different...if a second has elapsed, it calls DoEvents and updates "ti" to the new time...it's made many of my unresponsive loops very responsive while still keeping them relatively fast. Even if, as you say above, you literally CAN'T use DoEvents, this is handy to have in mind for the future when you have a similar situation but can use it...and I would hope that the WAITMESSAGE suggestion is an option for you that works :-)

    Another option is to use a timer to call DoEvents at regular intervals, but if your loop is taking 100% of the processor you'll find that the timer is unlikely to be triggered.

    I'm not 100% sure, but check this link from here to see if WAITMESSAGE is a useful alternative to DoEvents or Sleep (sleep would essentially pause the entire app, not letting messages get through, but this will pause for X milliseconds and let other events occur...it might be what you're after)

    EITHER way, you *need* your loop to pause its processor intensive code if you want other events to fire...if DoEvents is overkilll, maybe the link is preferable...or maybe others have better suggestions for ways to achieve this pause?
    Last edited by SmUX2k; Apr 22nd, 2022 at 09:20 AM.

  7. #7
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    If you're sure you're receiving the message I don't know why you need a pause/loop/DoEvents/equivalent at all. Just set a flag that the next change notification is yours. Because there's no chance for any other copy operation to occur. SendCtrlC->bCtrlCSent = True->If bCtrlCSent Then It's my message. Where the flag is scoped for both the subclass proc and wherever the copy command is sent from, and the check is done in the subclass proc. You can add a timing sanity check if neccessary, if it came in more than a few dozen ms after you sent Ctrl+C, your Ctrl+C probably failed.
    Last edited by fafalone; Apr 22nd, 2022 at 09:45 AM.

  8. #8
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    Heck, go read Trick's thread on multi-threading, start a new thread that'll interrupt your loop, share a bit of memory between the two threads (enough for a flag), and then have your primary/loop thread monitor that shared memory flag until it changes.

    That's a rough way to go, but it'll get it done ... and no DoEvents (nor subclass message monitoring, nor calls to PeekMessage).
    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. To all, peace and happiness.

  9. #9
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    Elroy is correct. Multi-threading is definitely an option here. I tested the concept in VB.Net and it works:-
    Code:
    Public Class Form1
    
        Private Const WM_NCHITTEST As Integer = &H84
    
        Private _newMsgRec As Boolean
    
        Private _msg As Message
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
            'Find a way to do this in VB6
            Threading.ThreadPool.QueueUserWorkItem(Sub()
                                                       DoLoop()
                                                   End Sub)
    
        End Sub
    
        Private Sub DoLoop()
            Do
                'Proves we are able to receive Window messages
                'in a tightloop as along as it executes on another thread
                If _newMsgRec Then
                    'The WndProc filters out only
                    'WM_NCHITTEST messages for us and we check them
                    'inside this loop
                    Debug.WriteLine(_msg.ToString())
    
                    'So we can check for a new message
                    'after this one
                    _newMsgRec = False
                End If
    
    
            Loop
        End Sub
    
        'Equiavalent of subclassing WndProc like you would in VB6
        'using the Win32 API
        Protected Overrides Sub WndProc(ByRef m As Message)
            'Debug.WriteLine(m.ToString)
    
            'We pay attention to hit test
            'messages
            If m.Msg = WM_NCHITTEST Then
                _msg = m
                _newMsgRec = True
            End If
    
            MyBase.WndProc(m)
        End Sub
    
    End Class
    Highlighted in red is what you need to figure out how to reproduce in VB6. The idea is to have the loop run on a separate thread so that the application's message loop remains free to process window messages.
    Last edited by Niya; Apr 25th, 2022 at 04:04 PM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  10. #10
    PowerPoster
    Join Date
    Feb 2017
    Posts
    5,666

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    There is an interesting subject here: how to do a "DoEvents" that processes just certain specific messages (and not all).
    But I guess I should open a new thread for that. Anyway I don't need that now, but at some point sometimes it is necessary and the alternative is always to put a "full" DoEvents.

  11. #11
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,909

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    To summarize, it's got to be a separate thread (or a separate process) that breaks the loop. There's no other way to interrupt a loop (other than kill the process). So, either PeekMessage or an examination of shared memory must take place within the loop. PeekMessage can have filters that allow for examining for a specific message (leaving all others alone). So that's certainly a way to do it. Also, using some kind of shared memory between the two threads (or processes) would do it.

    I see no other way.
    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. To all, peace and happiness.

  12. #12
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    Quote Originally Posted by Eduardo- View Post
    There is an interesting subject here: how to do a "DoEvents" that processes just certain specific messages (and not all).
    But I guess I should open a new thread for that. Anyway I don't need that now, but at some point sometimes it is necessary and the alternative is always to put a "full" DoEvents.
    .Net applications can do it using message filters. I'm confident that this method can be implemented in VB6.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  13. #13
    PowerPoster
    Join Date
    Feb 2017
    Posts
    5,666

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    Just to be clear about my message (#10): I was talking about processing messages in the current thread (but I suppose that is not what is being discussed here).

  14. #14
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    Quote Originally Posted by Elroy View Post
    To summarize, it's got to be a separate thread (or a separate process) that breaks the loop. There's no other way to interrupt a loop (other than kill the process). So, either PeekMessage or an examination of shared memory must take place within the loop. PeekMessage can have filters that allow for examining for a specific message (leaving all others alone). So that's certainly a way to do it. Also, using some kind of shared memory between the two threads (or processes) would do it.

    I see no other way.
    I'm fairly certain that the .Net example I posted could be implemented in VB6. However, right now I'm just not up to the task of decoding the arcane wizardry behind writing multi-threaded code in VB6. Olaf and Trick know how to do it though.

    Also, I feel as if we don't have enough information on what OP is really doing. I'm not entirely convinced that threading is even necessary for whatever he is trying to solve. I mean is there a reason he simply cannot process the messages as they arrive inside the sub-classed window procedure? Is it really necessary to have a separate loop processing these messages?
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  15. #15
    PowerPoster
    Join Date
    Feb 2017
    Posts
    5,666

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    I have no idea what the OP is (or was) after, but DoEvents is always for the current process, not for other processes.

    If you read the title "How do I allow subclassing messages to occur during Do Loop without using DoEvents?" taking it literally, it means something like the following:

    Code:
        Dim c As Long
        
        Do
            c = c + 1
        Loop Until c > 100000000
    And you want the program to process some events, then you would need to add:

    Code:
        Dim c As Long
        
        Do
            c = c + 1
            DoEvents
        Loop Until c > 100000000
    But the problem is that that code will process any event, including for example closing the form or whatever. And you want to allow the processing of just some events, for example a timer, a notification or a DDE event,, or maybe receiving a response from a web page.
    But not all and every event like DoEvent does, because you don't want to risk re-entry, or to allow to close the program.

    So you want something like this:

    Code:
        Dim c As Long
        
        Do
            c = c + 1
            DoSomeEvents EventID1 Or EventID2 Or EventID3
        Loop Until c > 100000000
    BTW: now reading again what the OP asked: just don't use a loop and won't need DoEvents, use a timer to check when the clipboard content changes instead.
    And he says "he can't use DoEvents" but he does not explain why.
    But it seems he solved it already, because he didn't return.

  16. #16
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    6,167

    Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent

    Should be possible with something similar to

    Code:
        Dim c As Long
        
        Do
            c = c + 1
            If PeekMessage(uMsg, 0, WM_DRAWCLIPBOARD, WM_DRAWCLIPBOARD, PM_NOREMOVE) <> 0 Then
                DoEvents
            End If
        Loop Until c > 100000000
    As far as I understand the use-case is when the app is already responding to a clipboard change with a deep loop and then suddenly another WM_DRAWCLIPBOARD is sent by chrome.

    The approach is brittle and completely flawed but this is how innovation (or meeting a dead end) happens.

    cheers,
    </wqw>

Tags for this Thread

Posting Permissions

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



Click Here to Expand Forum to Full Width