Discussion:
Scroll VBA listbox with mousewheel
(too old to reply)
RB Smissaert
2005-06-01 16:43:19 UTC
Permalink
Trying to use the Windows API to make a listbox on a VBA userform scroll
with the mousewheel.
I got this code from Jim Rech that works perfectly fine when the userform is
loaded as a normal
modal userform (vbModal). When the userform however is loaded as modeless
userform (vbModeless)
it crashes even when just loading the form.
I know this is not really VB territory, but I thought there would be more
chance getting some insight in
this in the API group than in a VBA group.
Thanks for any advice.

RBS


Option Explicit

Private Declare Function CallWindowProc _
Lib "user32.dll" _
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 Declare Function SetWindowLong _
Lib "user32.dll" _
Alias "SetWindowLongA" (ByVal hWnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long

Private Declare Function FindWindow _
Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long

Private Const GWL_WNDPROC = -4
Private Const WM_MOUSEWHEEL = &H20A

Dim collUF As New Collection
Dim collPrevHdl As New Collection
Dim collUFHdl As New Collection

Private Function WindowProc(ByVal Lwnd As Long, _
ByVal Lmsg As Long, _
ByVal Wparam As Long, _
ByVal Lparam As Long) As Long

Dim Rotation As Long
Dim Btn As Long

If Lmsg = WM_MOUSEWHEEL Then
Rotation = Wparam / 65536 ''High order word indicates
direction
Btn = Abs(Wparam) And 15 ''Low order word indicates various
virtual keys held down
MouseWheel collUF(CStr(Lwnd)), Rotation, Btn
WindowProc = 0 ''We handled event, no need to pass on
(right?)
Else
WindowProc = CallWindowProc(collPrevHdl(CStr(Lwnd)), _
Lwnd, _
Lmsg, _
Wparam, _
Lparam)
End If

End Function

''Need both userform and its caption because Userform1.Caption is empty for
some reason
Sub UserformHook(PassedForm As UserForm, Cap As String)

Dim LocalHwnd As Long
Dim LocalPrevWndProc As Long
Dim ErrCounter As Integer
Dim Counter As Integer

LocalHwnd = FindWindow("ThunderDFrame", Cap)
LocalPrevWndProc = SetWindowLong(LocalHwnd, GWL_WNDPROC, AddressOf
WindowProc)

On Error GoTo DupKey ''In case Windows assigns the same handle to
a subsequent userform (altho it doesn't seem to do this)...
TryAgain:
collUF.Add PassedForm, CStr(LocalHwnd)
collPrevHdl.Add LocalPrevWndProc, CStr(LocalHwnd)
collUFHdl.Add LocalHwnd
Exit Sub
DupKey:
If ErrCounter = 0 Then ''Avoid infinite error loop
For Counter = 1 To collUFHdl.Count
If collUFHdl(Counter) = LocalHwnd Then
collUFHdl.Remove Counter
collUF.Remove Counter
collPrevHdl.Remove Counter
End If
Next
ErrCounter = 1
Resume TryAgain
End If

End Sub

''Scrolls listbox 1 row or a full page if Ctrl is down
Sub MouseWheel(UF As UserForm, _
ByVal Rotation As Long, _
ByVal Btn As Long)

Dim LinesToScroll As Integer
Dim ListRows As Integer
Dim Idx As Integer

With UF
If TypeName(.ActiveControl) = "ListBox" Then
ListRows = .ActiveControl.ListCount
If Btn = 8 Then ''Ctrl
LinesToScroll = Int(.ActiveControl.Height / 10)
''Seems to work for font size 8
Else
LinesToScroll = 1
End If
If Rotation > 0 Then
'Scroll up
Idx = .ActiveControl.TopIndex - LinesToScroll
If Idx < 0 Then Idx = 0
.ActiveControl.TopIndex = Idx
Else
'Scroll down
Idx = .ActiveControl.TopIndex + LinesToScroll
If Idx > ListRows Then Idx = ListRows
.ActiveControl.TopIndex = Idx
End If
End If
End With

End Sub

Private Sub UserForm_Initialize()
Dim Counter As Integer
For Counter = 1 To 20
ListBox1.AddItem Counter
ListBox2.AddItem Counter * 10
Next
UserformHook Me, Me.Caption
End Sub
Sam Hobbs
2005-06-02 15:26:37 UTC
Permalink
When the userform however is loaded as modeless userform (vbModeless)
it crashes even when just loading the form.
Usually it is not sufficient to use words such as "crash" without details.
What do you mean by "crashes"? Is there an error message displayed? What is
the VB source code line that the error occurs on? I know that it is usually
more difficult to determine things like that in VB programs then in programs
developed using other IDEs, but it usually is necessary to determine things
like that for (WinAPI) problems such as this.
RB Smissaert
2005-06-02 15:51:23 UTC
Permalink
There is no VBA error, otherwise I would have mentioned it.
The Excel application freezes (it becomes unresponsive) and the
only way out is Ctrl + Alt + Del.
I just can't determine what, if any particular line is causing the problem.
You say VB, but note that this is Excel VBA.

RBS
Post by Sam Hobbs
When the userform however is loaded as modeless userform (vbModeless)
it crashes even when just loading the form.
Usually it is not sufficient to use words such as "crash" without details.
What do you mean by "crashes"? Is there an error message displayed? What
is the VB source code line that the error occurs on? I know that it is
usually more difficult to determine things like that in VB programs then
in programs developed using other IDEs, but it usually is necessary to
determine things like that for (WinAPI) problems such as this.
Sam Hobbs
2005-06-02 16:40:34 UTC
Permalink
Is it possible to single-step through the code until the problem occurs?

This might not be possible or effective, but if it is, then it can be very
effective.
Post by RB Smissaert
There is no VBA error, otherwise I would have mentioned it.
The Excel application freezes (it becomes unresponsive) and the
only way out is Ctrl + Alt + Del.
I just can't determine what, if any particular line is causing the problem.
You say VB, but note that this is Excel VBA.
RB Smissaert
2005-06-02 16:54:07 UTC
Permalink
I will try that.

RBS
Post by Sam Hobbs
Is it possible to single-step through the code until the problem occurs?
This might not be possible or effective, but if it is, then it can be very
effective.
Post by RB Smissaert
There is no VBA error, otherwise I would have mentioned it.
The Excel application freezes (it becomes unresponsive) and the
only way out is Ctrl + Alt + Del.
I just can't determine what, if any particular line is causing the problem.
You say VB, but note that this is Excel VBA.
RB Smissaert
2005-06-02 17:02:16 UTC
Permalink
I am not sure how can single-step through this. The trouble
seems to be in Private Function WindowProc.
When I put a msgbox after loading the form I can scroll the listbox, but it
Excel freezes
after the mouse leaves the listbox.

RBS
Post by Sam Hobbs
Is it possible to single-step through the code until the problem occurs?
This might not be possible or effective, but if it is, then it can be very
effective.
Post by RB Smissaert
There is no VBA error, otherwise I would have mentioned it.
The Excel application freezes (it becomes unresponsive) and the
only way out is Ctrl + Alt + Del.
I just can't determine what, if any particular line is causing the problem.
You say VB, but note that this is Excel VBA.
Sam Hobbs
2005-06-02 20:53:07 UTC
Permalink
Debugging things like this can be tricky and a little creativity can help. I
am sorry for not studying your code totally but I will try to make a few
suggestions based on some guesses about what you are doing.

One possibility might be simply the volume of WM_MOUSEWHEEL messages. I
don't know but I suspect that it is sent very frequently and the problem
might simply be that the system is overloaded attempting to process all of
them. Windows is highly optimized to process mouse messages. If this is a
possible problem, then you can add a global or static counter and increment
it every time a WM_MOUSEWHEEL message is processed. When a certain number of
messages have been processed, don't do anything more with them. This is not
a permanent solution but it could help diagnose the problem without getting
the "crash".

Another possibility is that the "collPrevHdl(CStr(Lwnd))" that you have in
your call to CallWindowProc might not be working correctly. I don't know you
should diagnose that, but if it were me, I think I would at least ensure
that the lookup works. Is it possible that there is not an element in the
collPrevHdl collection for the Lwnd? I suppose not; I suppose VB would
complain if that was a problem, correct? Are you sure that the value in the
collection is a valid pointer to a previous window procedure? You also use
the collection when you call your MouseWheel function; this might be a
problem and probably is is logical that the problem occurs when the mouse
leaves the listbox, right?

Would it work to comment out the code that processes the WM_MOUSEWHEEL
messages? This would be only for temporary diagnostic purposes. Allow the
WM_MOUSEWHEEL messages to be processed by CallWindowProc the same as the
other messages. If that works, then you can assume that that is not part of
the problem. It will be easier to diagnose if you don't have to debug the
branch of code that processes most of the messages.
Post by RB Smissaert
I am not sure how can single-step through this. The trouble
seems to be in Private Function WindowProc.
When I put a msgbox after loading the form I can scroll the listbox, but
it Excel freezes
after the mouse leaves the listbox.
RBS
Post by Sam Hobbs
Is it possible to single-step through the code until the problem occurs?
This might not be possible or effective, but if it is, then it can be
very effective.
Post by RB Smissaert
There is no VBA error, otherwise I would have mentioned it.
The Excel application freezes (it becomes unresponsive) and the
only way out is Ctrl + Alt + Del.
I just can't determine what, if any particular line is causing the problem.
You say VB, but note that this is Excel VBA.
RB Smissaert
2005-06-02 22:02:15 UTC
Permalink
Post by Sam Hobbs
One possibility might be simply the volume of WM_MOUSEWHEEL messages
The volume of messages in general could well be the problem. This was
mentioned by
Jim Rech. There are more messages with a modeless userform.
As far as I can see (put: Cells(65535, 1).End(xlUp).Offset(1, 0).Value =
Lmsg
in the function WindowProc) there are no more WM_MOUSEWHEEL messages
with a modeless userform though. The problem is not with these messages, but
with
mouse generated messages when the mouse moves out of the listbox.
I can do a mouse scroll by loading the form, clicking in the Excel sheet (so
the focus is
away from the form) move the mouse to the listbox and click an item and do a
mouse scroll.
Loading the form and moving the mouse to the listbox when the form has focus
makes it crash.

Maybe UserformHook Me, Me.Caption should only happen when the mouse enters
the listbox
it should UnHook when it leaves the listbox. Not sure this is possible.

Will just keep trying different things.

RBS
Post by Sam Hobbs
Debugging things like this can be tricky and a little creativity can help.
I am sorry for not studying your code totally but I will try to make a few
suggestions based on some guesses about what you are doing.
One possibility might be simply the volume of WM_MOUSEWHEEL messages. I
don't know but I suspect that it is sent very frequently and the problem
might simply be that the system is overloaded attempting to process all of
them. Windows is highly optimized to process mouse messages. If this is a
possible problem, then you can add a global or static counter and
increment it every time a WM_MOUSEWHEEL message is processed. When a
certain number of messages have been processed, don't do anything more
with them. This is not a permanent solution but it could help diagnose the
problem without getting the "crash".
Another possibility is that the "collPrevHdl(CStr(Lwnd))" that you have in
your call to CallWindowProc might not be working correctly. I don't know
you should diagnose that, but if it were me, I think I would at least
ensure that the lookup works. Is it possible that there is not an element
in the collPrevHdl collection for the Lwnd? I suppose not; I suppose VB
would complain if that was a problem, correct? Are you sure that the value
in the collection is a valid pointer to a previous window procedure? You
also use the collection when you call your MouseWheel function; this might
be a problem and probably is is logical that the problem occurs when the
mouse leaves the listbox, right?
Would it work to comment out the code that processes the WM_MOUSEWHEEL
messages? This would be only for temporary diagnostic purposes. Allow the
WM_MOUSEWHEEL messages to be processed by CallWindowProc the same as the
other messages. If that works, then you can assume that that is not part
of the problem. It will be easier to diagnose if you don't have to debug
the branch of code that processes most of the messages.
Post by RB Smissaert
I am not sure how can single-step through this. The trouble
seems to be in Private Function WindowProc.
When I put a msgbox after loading the form I can scroll the listbox, but
it Excel freezes
after the mouse leaves the listbox.
RBS
Post by Sam Hobbs
Is it possible to single-step through the code until the problem occurs?
This might not be possible or effective, but if it is, then it can be
very effective.
Post by RB Smissaert
There is no VBA error, otherwise I would have mentioned it.
The Excel application freezes (it becomes unresponsive) and the
only way out is Ctrl + Alt + Del.
I just can't determine what, if any particular line is causing the problem.
You say VB, but note that this is Excel VBA.
Loading...