Can I get rid of this short freeze when clicking the form?
Hi,
The problem can quickly be illustrated by creating a new project with
- A Label field
- A Timer control with interval =50 and Enabled = true
The only piece of code is this:
Private Sub Timer1_Timer()
Label1.Caption = Rnd()
End Sub
When you run the program the label field will update with random numbers in 20 Hz.
But when I left click the forms titlebar and keep the mouse button pressed as to move the form, the application pauses for 200-500 milliseconds.
Then (with the mouse button still pressed) it starts running at 20 Hz again. No further delays can be observed as I move the form around.
What is causing this short initial freeze and can it be avoided?
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by LaVolpe
Welcome to the forum
See if this also occurs when the sample project is compiled. It may. But timer controls behave slightly differently when app compiled and not
It does.
My actual program is a "realtime" Simulator where I see output delays whenever the user need to move the form around. This is causing all sorts of problems for the program I'm feeding data. Pretty annoying.
I've noticed one thing though:
If I immediately start dragging the form after clicking it, the delay looks to be shorter (or gone). It's only when I click and hold the button a short while before starting to move the form I have the problem.
Re: Can I get rid of this short freeze when clicking the form?
Hi leifmb,
Yes, welcome to VBForums.
Hmmm, we just recently had a discussion about this, but I can't seem to quickly find it. It was about moving a form around by dragging the title-bar, and how that pauses everything else.
Leifmb, what we must remember is that VB6 is always single-threaded in terms of execution (without jumping through some relatively high hoops). And this is inclusive of what windows wants to do in conjunction with our application (i.e., deal with Windows' non-client areas such as the title-bar and/or resizing bars).
I'm on Win10 1803, and I just tried your little example. And just to be clear, you said 20Hz, so I set the timer at an interval of 50 (1000/20). And I get exactly what you said (both IDE and compiled). When I left-click-hold, I get a short pause and then the timer resumes. When I right right-click-hold, the timer freezes until I release. This is a guess, but apparently, when I left-click-hold, Windows goes into some short loop (taking the thread) to verify a few things and waiting to see what the user is going to do. And apparently, on a right-click-hold, Windows just keeps the thread in some loop until the user makes a decision.
As I see it, there are only a couple of ways to solve this problem: 1) you could jump through the high-hoops to spawn another thread in your application that deals with your timer (or whatever) needs. This second thread wouldn't be affected by the first thread's activities (or, at least not significantly). In the other thread (that I can't find), we discussed a couple of different multi-threading approaches. The Trick (one of our members) has one approach. Krool (another of our members) has outlined another approach. There are others as well, but this is not something to be undertaken lightly.
2) You could also eliminate the "Windows" title-bar (and also possibly make the form non-sizable) from your form. And, if you need to drag it around, you could build your own title-bar and build your own dragging procedures. With your own dragging procedures, you could make sure that you don't trap the thread in a loop (the idea way of doing it), or you could place a DoEvents in the loop your build (a less desirable way to do it). You could also just design a way to drag the form around by any gray-space, rather than requiring a title-bar. I've posted one approach for doing this in the CodeBank.
Best Of Luck,
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. To all, peace and happiness.
Re: Can I get rid of this short freeze when clicking the form?
I have looked at a few Multi thread examples, but haven't found something that can easily be used as a template for my program.
I will need a data processing thread and one GUI thread. And I will need to be able to pass data to the GUI for display and receive commands/settings back.
The tip with hiding the Title bar can be a good workaround. I'll send some more time looking for the right "dual" thread example first, but if that comes up short, I'll try to hide the Title bar.
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by leifmb
I have looked at a few Multi thread examples, but haven't found something that can easily be used as a template for my program.
I will need a data processing thread and one GUI thread. And I will need to be able to pass data to the GUI for display and receive commands/settings back.
The tip with hiding the Title bar can be a good workaround. I'll send some more time looking for the right "dual" thread example first, but if that comes up short, I'll try to hide the Title bar.
BR
LeifMB
Try this multithreading out. http://www.vbforums.com/showthread.p...ultithreading)
The DLL can be used regfree by side by side manifest.
It's quite simple and easy to communicate between worker thread and GUI thread.
Re: Can I get rid of this short freeze when clicking the form?
Yes, this is a Windows thing. I first noticed it about nine years ago when I was working on my first VB.Net program, using VS2005. I had to regularly update some external hardware with a message at a minimum of 50hz. So, I knew the timer would run at 64hz with an interval set to 15 ms or less, so figured I was good to process and send the data from the timer at 64hz, give or take. But, definitely, if the user mousedowned on the titlebar and didn't move quickly enough (I suspected it was related to double-click wait period, to see if you double clicked and it should maximize or restore the Form's size). If you dragged quick enough, it knew you weren't going to double-click so the timer continued quickly. But if you didn't move the mouse, it paused everything for around 400ms was my calculation.
Fortunately, it was easy to create a background thread in .Net to handle the processing and sending of the message, so that took care of the issue.
Creating a secondary thread in VB6 should also fix the issue, I assume.
It won't prevent the GUI thread from hanging for that 400ms period, but at least your processing is being done.
If you try your test case, the label should still pause I think, since the GUI thread is hung, but then the label value should jump to the correct number as the counter should have continued incrementing in the second thread during that period.
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by Krool
Try this multithreading out. http://www.vbforums.com/showthread.p...ultithreading)
The DLL can be used regfree by side by side manifest.
It's quite simple and easy to communicate between worker thread and GUI thread.
Thanks. I have the demo project running and my first observation is that the progress bar stops updating if I left click (and hold) the title bar?
In this case I would assume the background worker kept running and as soon as the GUI was freed (by releasing mouse button) the progress bar would jump a few bars to catch up for the missing updates. But it doesn't. It just continues from where it was left off....
But maybe the GUI updates the bar relatively and not absolutely?
In order to understand the threading concept I think I'll have to throw out all the common controls stuff and replace the progress bar with my random label or similar. Right now there is too much overhead to really see what is going on.
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by leifmb
Thanks. I have the demo project running and my first observation is that the progress bar stops updating if I left click (and hold) the title bar?
In this case I would assume the background worker kept running and as soon as the GUI was freed (by releasing mouse button) the progress bar would jump a few bars to catch up for the missing updates. But it doesn't. It just continues from where it was left off....
But maybe the GUI updates the bar relatively and not absolutely?
In order to understand the threading concept I think I'll have to throw out all the common controls stuff and replace the progress bar with my random label or similar. Right now there is too much overhead to really see what is going on.
BR
LeifMB
I have a possible solution:
The 'BackgroundProcedure' is the worker thread and it calls 'StatusCallback' which is the GUI thread where the progress bar is set and there it hungs up when you drag form.
Solutions:
1. Make a PostMessage to some Window in the worker thread and as soon the subclassed GUI window is responsive it catches up
2. Try to set the progress bar directly in the worker thread. (Though not recommended)
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by Krool
I have a possible solution:
The 'BackgroundProcedure' is the worker thread and it calls 'StatusCallback' which is the GUI thread where the progress bar is set and there it hungs up when you drag form.
Solutions:
1. Make a PostMessage to some Window in the worker thread and as soon the subclassed GUI window is responsive it catches up
2. Try to set the progress bar directly in the worker thread. (Though not recommended)
My background worker is now running like clockwork at 50 Hz. I've confirmed it's not affected by any GUI hang ups through a number of tests. This looks pretty solid.
But I'm unable to refresh the GUI with the left mouse button clicked. The standard Timer control does refresh the form during dragging, although it had this first initial freeze which was my original question. Right now my Multi Threaded project is not an initial freeze, but a permanent one.... (while dragging that is)
I tried method 2 as pr your suggestion above. Not sure how to go about with method 1.
Also tried to add the standard timer on the MainForm as a GUI refresher since that worked before, but it's no longer working.
A few DoEvents and MainForm.Refresh here and there has also not helped
Re: Can I get rid of this short freeze when clicking the form?
leifmb,
Method 2 is not recommended.
Can you share an encapsulated demo showing your issue?
I can try around and also show you then per reply demo with method 1 usage.
Re: Can I get rid of this short freeze when clicking the form?
In vb.net they have a BufferedGraphics object which can be used to update an area of the screen from any thread, so I use that when I want to update something continuously and not do that hangup when you click on the titlebar.
Perhaps in VB6, if you do your own drawing using api calls, probably a couple of bitblts to do a progress bar, to the hdc of a picturebox (with AutoRedraw set false) from the background thread, you can update that area of the screen in a similar manner from the background thread without causing the main thread (GUI) from having an issue.
Re: Can I get rid of this short freeze when clicking the form?
I have the same problem, but solved.
Code:
Private Function ISubclass_WindowProc(ByVal hwnd As Long, _
ByVal iMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Const HTCAPTION As Long = 2&
Const HTMINBUTTON As Long = 8&
Const HTMAXBUTTON As Long = 9&
Const HTCLOSE As Long = 20&
Dim Window_Rect As RECT
Dim MousePoint As POINTAPI
Static DragActive As Boolean
Static Start_X As Long
Static Start_Y As Long
Static Window_Left As Long
Static Window_Top As Long
'--------------------------------------------------
Select Case iMsg
Case WM_LBUTTONUP
ReleaseCapture
DragActive = False
Case WM_MOUSEMOVE
If DragActive Then
GetCursorPos MousePoint
SetWindowPos Me.hwnd, 0, _
Window_Left + (MousePoint.X - Start_X), _
Window_Top + (MousePoint.Y - Start_Y), _
0, 0, _
SWP_NOSIZE Or SWP_NOZORDER Or SWP_ASYNCWINDOWPOS
End If
Case WM_NCLBUTTONDOWN
Select Case wParam
Case HTCAPTION
SetCapture Me.hwnd
GetWindowRect Me.hwnd, Window_Rect
GetCursorPos MousePoint
DragActive = True
Start_X = MousePoint.X
Start_Y = MousePoint.Y
Window_Left = Window_Rect.Left
Window_Top = Window_Rect.Top
Case HTMINBUTTON
Me.WindowState = vbMinimized
Case HTMAXBUTTON
Select Case Me.WindowState
Case vbNormal: Me.WindowState = vbMaximized
Case vbMaximized: Me.WindowState = vbNormal
End Select
Case HTCLOSE
IsUnloading = True
Unload Me
End Select
End Select
End Function
Re: Can I get rid of this short freeze when clicking the form?
I guess that solves it by forcing it into drag mode immediately. Does that disable the capability of double-clicking on the titlebar to maximize or restore the form's size?
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by passel
I guess that solves it by forcing it into drag mode immediately. Does that disable the capability of double-clicking on the titlebar to maximize or restore the form's size?
No, that still works.
Here is something to play with: Drag titlebar.zip
Rem:
That approach was not my idea.
I don't remember who it was.
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by Krool
leifmb,
Method 2 is not recommended.
Can you share an encapsulated demo showing your issue?
I can try around and also show you then per reply demo with method 1 usage.
I just noticed the compiled version will update while the form is dragged! I was previously running it in the IDE.
It still has this short initial freeze which I believe is a Windows thing, but I can work with that since the background worker looks unaffected.
I have attached the project in case someone is interested. The DLL project is not included as it's unchanged from the original demo.
But before I head off on my own, I have one last question:
Can you explain the mechanics behind Argument1 and Argument2 in the callback procedure?
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by Karl77
No, that still works.
Here is something to play with: Drag titlebar.zip
Rem:
That approach was not my idea.
I don't remember who it was.
Interesting... Your code updates while the form is stationary (left click and hold) but shows signs of freezing when I start moving it around?
Sort of opposite of my initial problem.
Anyway, now that the Krool code looks to work for me, I'll go with that.
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by Karl77
I can't see that here... for me it works ok.
Good you have a working solution!
I've double checked in my end. It's lagging both in the IDE and when compiled, although much more apparent in the IDE.
Try dragging the form slowly horizontally while in the IDE.
PS: My computer isn't super fast.
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by leifmb
I just noticed the compiled version will update while the form is dragged! I was previously running it in the IDE.
It still has this short initial freeze which I believe is a Windows thing, but I can work with that since the background worker looks unaffected.
I have attached the project in case someone is interested. The DLL project is not included as it's unchanged from the original demo.
But before I head off on my own, I have one last question:
Can you explain the mechanics behind Argument1 and Argument2 in the callback procedure?
In the IDE the DebugMode will be set to True, which means that there is no multithreading in place as this is risky in IDE.
The StatusCallback is the interface to switch between worker thread and GUI thread.
I used now method 1 but without PostMessage instead with a VB.Timer. The worker thread sets at begin via StatusCallback the Timer on and at end off.
The Timer event will set the Label1.Caption (when responsive but that doesn't matter)
Also the Timer will use the shared variable from the worker thread. So when the GUI thread hangs the worker will still increment the variable and then when the GUI thread is responsive again will catch up accordingly.
Attached is the edited version (and recommended way)
Last edited by Krool; Aug 15th, 2018 at 02:34 AM.
Reason: remove attach to save upload limit
Re: Can I get rid of this short freeze when clicking the form?
Hi again,
Thanks for the example with the Timer control. I think I understand enough now to get moving on my real project.
But before I do that I would appreciate if you could have a quick look at my updated demo project (attached).
My real project is a navigation simulator and I have added 2 slider controls for heading and rate-of-turn to reflect some of what I'm doing there.
I use your Timer control to update the GUI and I use the slider control events to update parameters used by the background worker.
It looks like the system is working as intended. Both sliders can be used dynamically and the updates and values look good.
Is this an acceptable way of handling the two way GUI interaction (read and write) or do you see any problems with this approach?
Re: Can I get rid of this short freeze when clicking the form?
Hi Again,
I've ran into another problem when I try to extend the functionality of the demo simulator...
I use a winsock replacement library called CSocktMaster in order to transmit the simulated data.
The Background worker must be able to transmit data while the form is frozen, but right now my socket freezes with the form.
An updated demo is attached. This runs a TCP server on port 8888. Use i.e. putty.exe to test.
(NB: CSocketMaster doesn't like 127.0.0.1, use your proper IP in putty.exe when connecting or else it will not work)
Once running right click and hold the title bar - this will freeze the form and the socket output. You will see putty stop updating.
I guess one solution could be to look for another networking component, but CSocketMaster is what I have always been using.
Any chance of getting this to work without freezing with the form?
I'm thinking the event handling of CSocketMaster needs to be moved out of the main form, but not sure how to do that.
PS: I made slight modification to the gui update timer. It's now self disabling. I just enable it through the callback the end of each system update. It will then trigger at first opportunity to redraw the gui, then disable itself until next update. This should according to my understanding lead to faster updates and less overhead compared to running the timer permanently at 10 or 20 Hz.
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by Krool
You need to create a New CSocketMaster instance within BackgroundProcedure.
Not having much luck....
Still freezes in IDE and compiled version crashes. Buggy version attached.
Can I "dim withevents" in the main form? Won't that "tie" the SocketMaster object to the form?
Can I move the events handling to the background procedure? If so how?
Re: Can I get rid of this short freeze when clicking the form?
In IDE there is only 1 thread as .DebugMode is True. Do not count how it runs in the IDE.
You can create a new class, e.g. CSocketMasterWrapper which you Dim As New in the background procedure.
In the wrapper class you then can declare a Private WithEvents.
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by Krool
In IDE there is only 1 thread as .DebugMode is True. Do not count how it runs in the IDE.
You can create a new class, e.g. CSocketMasterWrapper which you Dim As New in the background procedure.
In the wrapper class you then can declare a Private WithEvents.
Thank you for your suggestion. My problem is that I have absolutely no experience in working with classes.
I'm keen to learn though so I have made an initial attempt (see attachment) but the IDE say "Cannot handle events for the object specified"
Any pointers from here would be greatly appreciated.
Re: Can I get rid of this short freeze when clicking the form?
Please adapt the CSocketMasterWrapper as following:
Code:
Option Explicit
Private WithEvents socket As CSocketMaster
Private Sub Class_Initialize()
socket = New CSocketMaster
End Sub
Private Sub Class_Terminate()
socket = Nothing
End Sub
'Wrapped Events
Private Sub socket_CloseSck()
socket.CloseSck
End Sub
Private Sub socket_ConnectionRequest(ByVal requestID As Long)
socket.CloseSck
socket.Accept requestID
End Sub
'Wrapped Properties
Public Property Get Protocol() As Long
Protocol = socket.Protocol
End Property
Public Property Let Protocol(Protocol As Long)
socket.Protocol = Protocol
End Property
Public Property Get State() As Long
State = socket.State
End Property
'Wrapped functions
Public Sub Bind(Optional LocalPort As Variant, Optional LocalIP As Variant)
Call socket.Bind(LocalPort, LocalIP)
End Sub
Public Sub Listen()
Call socket.Listen
End Sub
Public Sub SendData(Data As Variant)
Call socket.SendData(Data)
End Sub
Public Sub CloseSck()
socket.CloseSck
End Sub
Re: Can I get rid of this short freeze when clicking the form?
Hi,
I have updated my project with your code, but I get an "object variable or With block variable not set" error when I try to run it.
When I create my wrapped socket object in the background worker with this
Code:
Set WrappedSocket = New CSocketMasterWrapper
It will first call CSocketMaster Class_Initialize() which looks to complete successfully, but then CSocketMaster Class_Terminate()
is called immediately following this?
Re: Can I get rid of this short freeze when clicking the form?
leifmb,
please remove im Form code red marked
Code:
Public DialogClosed As Boolean
Public WrappedSocket As CSocketMasterWrapper
and include green marked
Code:
Case "BackgroundWorker"
Dim IsDebugMode As Boolean
IsDebugMode = Data.DebugMode
'Always access VB controls in a sync callback.
'StatusCallback.Raise True
If Data.CancellationPending = False Then
'Setup our socket object
Dim WrappedSocket As CSocketMasterWrapper
Set WrappedSocket = New CSocketMasterWrapper
Re: Can I get rid of this short freeze when clicking the form?
Originally Posted by Krool
leifmb,
please remove im Form code red marked
Code:
Public DialogClosed As Boolean
Public WrappedSocket As CSocketMasterWrapper
and include green marked
Code:
Case "BackgroundWorker"
Dim IsDebugMode As Boolean
IsDebugMode = Data.DebugMode
'Always access VB controls in a sync callback.
'StatusCallback.Raise True
If Data.CancellationPending = False Then
'Setup our socket object
Dim WrappedSocket As CSocketMasterWrapper
Set WrappedSocket = New CSocketMasterWrapper
Hi Krool,
Thanks, but unfortunately I get the same error as before. Did you manage to run it?
My latest version is attached.
Can it be that I need to extend my wrapper class? I haven't wrapped up everything, only what I believe is in use.
BR
Leif MB
Edit: Corrected the included example
Last edited by leifmb; Aug 16th, 2018 at 04:24 AM.
Re: Can I get rid of this short freeze when clicking the form?
Hi Again,
I'm still struggling with this project.
My wrapped CSocketMaster class works fine in the IDE, but the compiled version crashes for no obvious reason.
It looks like I've come to a dead end here. There must be a conflict between the VBMThread concept and the subclassing of CSocketMaster?
I believe my wrapper is correct. I've even made separate test program for that and it works both in the IDE and compiled.
Is there any other Multi Thread approach I could try in order to get my socket running in a a background process independent of the GUI thread?