|
-
Apr 21st, 2022, 10:47 PM
#1
Thread Starter
Fanatic Member
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.
-
Apr 22nd, 2022, 12:17 AM
#2
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.
-
Apr 22nd, 2022, 12:33 AM
#3
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.
-
Apr 22nd, 2022, 09:00 AM
#4
Thread Starter
Fanatic Member
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.
-
Apr 22nd, 2022, 09:07 AM
#5
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.
-
Apr 22nd, 2022, 09:13 AM
#6
Fanatic Member
Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent
 Originally Posted by tmighty2
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.
-
Apr 22nd, 2022, 09:42 AM
#7
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.
-
Apr 24th, 2022, 06:48 PM
#8
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.
-
Apr 25th, 2022, 04:00 PM
#9
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.
-
Apr 25th, 2022, 05:35 PM
#10
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.
-
Apr 25th, 2022, 05:48 PM
#11
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.
-
Apr 25th, 2022, 05:52 PM
#12
Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent
 Originally Posted by Eduardo-
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.
-
Apr 25th, 2022, 05:53 PM
#13
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).
-
Apr 25th, 2022, 05:59 PM
#14
Re: How do I allow subclassing messages to occur during Do Loop without using DoEvent
 Originally Posted by Elroy
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?
-
Apr 25th, 2022, 06:26 PM
#15
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.
-
Apr 26th, 2022, 12:21 AM
#16
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|