Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How do I keep IDE from crashing using MouseWheel Example?

Status
Not open for further replies.

ezbroadcast

Programmer
Dec 22, 2007
6
US
Using the example in thread222-902639 or many other examples on this site, I seem to have a problem with my VB6 IDE closing when I exit my program that uses the following type of code to create a WM_MOUSEWHEEL hook:

LocalPrevWndProc = SetWindowLong(LocalHwnd, GWL_WNDPROC, AddressOf WindowProc)

Does anyone know how to get these examples to unhook "cleanly" so they don't generate this kind of behavior in the IDE?
 
Sure. On unloading the form/terminating the program you need

SetWindowLong hWnd, GWL_WNDPROC, lpPrevWndProc

and don't use the End button or the End option on the Run menu to end the program.

 
I did actually discover my problem, and as usual, it takes devising a hoop to jump through to fix it. I discovered that if I create a subclass for two separate forms, I have to keep both the form handles (hWnd's) and LocalPrevWndProc handles in separate variables and then ONLY unhook the subclass if they've been hooked. Hooking to the subclass only occurs if the MSFlexGrids have received focus. Doing a me.visible = false does not unload the form, so if I put the unhook code in the Form_Unload event, it doesn't ever get unhooked. Also, if more than one window has opened, it may be possible to try "unhooking" using the wrong handle for the appropiate window - which also crashes the IDE and most likely my app. Therefore, as I've discovered, it's vital to know the proper LocalPrevWndProc of all of the windows you want to subclass and unhook them individually. Also, attempting to unhook a Subclass if it was never hooked in the first place makes your program go stoned.

It's unfortunate that most of these MOUSEWHEEL examples aren't going to work in cases where multiple forms and flexgrids/datagrids are involved. I am going to try and code an addition to the example I initally used that tracks the hWnds and LocalPrevWndProc of each window or object in an array, and lets you hook and unhook them like a stack, so they are always unhooked with the appropriate handles in the correct order.

In the mean time, I've got to get flying on this project, that only uses two forms and three (msflex/data) grids, so just using four separate long variables is all I need and a separate true/false variable to track the hooking on each form.

Thanks for the suggestions... Merry Christmas.
Jesus is the reason for the Season
 
>it's vital to know the proper LocalPrevWndProc of all of the windows

It's important to mention that all similar windows / controls (for instances all MSFlexGrid controls in the application) share the same window procedure and hence, has the same LocalPrevWndProc value.

>It's unfortunate that most of these MOUSEWHEEL examples aren't going to work in cases where multiple forms and flexgrids/datagrids are involved.

Surely you can subclass as many controls in an application as you want, whether the controls are placed on the same form or are distributed in different forms in an application. You need to subclass all controls using the same window procedure. When the window procedure is called, the hWnd argument identifies to which instance this message belongs to and you can write code to modify the behaviour accordingly.

See the following examples where a single window procedure is used to subclass multiple controls.
thread222-588883
thread222-1070885

See also thread222-1364428 in which global subclassing is used to modify the behaviour of a control class. Using this method, you can modify the behaviour of a window class on application-wide basis. Once the global subclassing is in effect, all windows created subsequently will exhibit the modified behaviour without the need to subclass each window individually.
 
>It's unfortunate that most of these MOUSEWHEEL examples aren't going to work in cases where multiple forms

The point of an example is that is just that - an example, not a fully realised solution for all circumstances. They illustrate the basic point. It's up to you how you then use it.
 
Before everyone gets too confused, I made a mistake in which code example I am using... it is here: thread222-1043122

I suppose that global subclassing may be the way to go, since I still can't seem to get this to work on multiple forms/objects. I think where I'm running into trouble is that this proceedure:

Private Function WindowProc(ByVal Lwnd As Long, ByVal Lmsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long

Dim MouseKeys As Long
Dim Rotation As Long
Dim Xpos As Long
Dim Ypos As Long

If Lmsg = WM_MOUSEWHEEL Then
MouseKeys = wParam And 65535
Rotation = wParam / 65536
Xpos = lParam And 65535
Ypos = lParam / 65536
MyForm.MouseWheel MouseKeys, Rotation, Xpos, Ypos
End If
WindowProc = CallWindowProc(LocalPrevWndProc, Lwnd, Lmsg, wParam, lParam)
End Function

is referencing "MyForm" object which is set when the form is "hooked". Since I have two forms, getting the right message from the correct form and passing it to the correct MouseWheel event is where the problem lies - I would assume this code is executed each time the wheel is turned on the mouse. One way, I thought, could be to just have the code to do this in each form individually, just making sure the unhook gets called before the form is destroyed. Another would be to unhook whenever I open or hide the form(s).
 
Once you have hooked your controls, you don't need to unhook them when you hide or unload your form.

As I told above, if multiple controls are sharing the same window procedure, you can identify the "source" of the event by looking at the hWnd argument, which is unique to each instance of the control. From hWnd, you can obtain a reference to the corresponding MSFlexGrid object.

There are many ways to do this. For instance, you can create a two-dimensional array, pairing hWnd and the object reference together. Or if you go for API methods, you can use SetWindowLong / GetWindowLong / GWL_USERDATA or SetProp / GetProp functions to store the object pointer in window memory which can be retrieved in the window procedure to reconstruct the object reference from the window handle.

Another approach is to directly control the window using its hWnd, without using the object reference. This is mostly done using API functions which require a window handle instead of the object reference. See below the crude modification to the window procedure which explains the idea.
___
[tt]
Const SB_LINEDOWN = 1
Const SB_LINEUP = 0
Const WM_VSCROLL = &H115
Function WndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If uMsg = WM_MOUSEWHEEL Then
If wParam > 0 Then
SendMessage hWnd, WM_VSCROLL, SB_LINEUP, ByVal 0&
ElseIf wParam < 0 Then
SendMessage hWnd, WM_VSCROLL, SB_LINEDOWN, ByVal 0&
End If
End If
WndProc = CallWindowProc(lpPrevWndProc, hWnd, uMsg, wParam, lParam)
End Function[/tt]
___

Here the window is scrolled directly using WM_VSCROLL message without the need of an object reference.
 
Here's a new twist to this... I'm using thread222-1043122 exactly as it is posted here. On any of the forms that are NOT the startup form, I can hook and unhook to my hearts content and have no problem. On the main form of the project, if I unhook directly in the Form_QueryUnload event, and then end the program with "End", it crashes the IDE. Whenever I execute the WheelHook, I'm tracking the hooked/unhooked state using a separate variable, so I KNOW I'm not trying to unhook twice.

If I make it unhook on the MSFlexgrid_LostFocus event and then put a silly command like Command9.SetFocus in my Form_QueryUnload (which then causes the flex grid to lose focus, thus firing the event), then initiate a Timer that then ends the program the crazy thing always works - it's like something else has to happen in the program before messaging is re-established after the un-hook. I don't understand why this hoop works, but it does. The program is pretty huge, so there is a lot of stuff going on at the time of Form_Terminate, this may have something to do with it.
Doing the WheelUnhook in the Form_QueryUnload just prior to an End statement always crashes the IDE, in my case. I tried it with a form with nothing in it and it loads/unloads fine, so I'd imagine that it's a size/timing issue. I discovered this behavior by clicking on a Command button (causing the MSFlexGrid_LostFocus event to fire), and then I closed the program... voila! I always use an End statement to make sure the program is shut down, otherwise about 50% of the time the IDE is sitting there paused.

Also it seems the simplest way to handle hooking and unhooking is to always unhook before showing a new form. If the flex grid on the main form has never recieved focus and your user opens a different window, then knowing if your form has been hooked is important, so you don't accidentally unhook it when showing the new form.

It might be nice to know the mechanics behind why this happens, but as long as it basically works, that's what I need. :)

Thank you to everyone! Merry Christmas!
 
>I always use an End statement to make sure the program is shut down

Let me be the first in what I suspect will be a queue of people to advise you:

do not use End in VB

>otherwise about 50% of the time the IDE is sitting there paused

which simply means you have not cleaned up properly
 
Is there any particular "way" to clean up? I have a program with a command button, a commondialog control and a bit of code to open a cart chunk on a wave file. It has all of the varaibles DIM'ed properly and the file is closed once the routine reads data out of the file and assigns them to about 15 variables, as long as there is data in the variables, the program does not end properly. So how do I clean up the garbage collection? (Maybe you can refer me to a different post that covers this?)
 
Sure! (this will fire all the code for you. No END needed.
<code>
Option Explicit

Private Sub cmdExit_Click()
Dim frm As Form
For Each frm In Forms
If frm.Name <> Me.Name Then ' Unload this form LAST
Unload frm
Set frm = Nothing
End If
Next
Unload Me
End Sub

It always closes the current form last!
</code>


-David
2006 & 2007 Microsoft Most Valuable Professional (MVP)
2006 Dell Certified System Professional (CSP)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top