A new method for disabling the Mouse Scroll Wheel in Access forms

by Wayne Phillips, 12 August 2009
Applies to: Access 97+
User Level: Beginner
Views: 12,178
Rating: ***** (52 votes)
Comments: 56   Have your say...

- without needing a DLL and without VBA subclassing

Download Demonstration MDB (Access 2000+) (Zipped)
Download Demonstration MDB (Access 97) (Zipped)

 UPDATE v1.3: 29/08/2009 -- Improved VBA loader code - now supports VBA5 and VBA6.
 UPDATE v1.4: 01/09/2009 -- Scroll wheel now disabled by default, for convenience.
 UPDATE v1.5: 04/12/2009 -- now the Init and Scroll object methods are case insensitive (fixes potential "Object does not support..." errors)

This solution is being provided for free and you can use this solution in your applications freely provided you leave the copyright notices in place in the source code.

- The problem
- The old solutions
- The new solution
- Instructions for use
- How this works -- Warning, quite technical!

The problem

In Access versions prior to v2007, when you use the mouse scroll wheel on a form, Access will always cycle through the current records in your forms dataset. This is quite an annoyance when you might not want this behaviour, and Microsoft never offered a simple solution to turn this feature off (until v2007).

The old solutions

There have been several solutions to this over the years, including;

  1. A DLL based solution (such as MouseWheelOnOff by Stephen Lebans)
  2. A VBA subclassing solution (as described here)

There are other solutions such as forcing the currently displayed record to always be in edit mode, but we won't discuss them here - they have their own drawbacks too.

Each of these current solutions have drawbacks to them

For #1, you need to distribute a DLL alongside your product.

For #2, as discussed in the Microsoft KB article, the VBA subclassing solution is not advisable due to problems that can occur if the VBE window is open. Also, your forms will take a performance hit since every window message goes through the relatively inefficient VBA subsystem.

The new solution

My solution is to use the subclassing method as described in the VBA article above, but rather than use a VBA subroutine as the callback procedure, I use a native machine code compiled procedure (read the technical stuff below if you want to understand how this is achieved). The result is extremely efficient and best of all: there is no DLL to re-distribute - the whole functionality is implemented in a single VBA module.

This solution is being provided for free and you can use this solution in your applications freely provided you leave the copyright notices in place in the source code.

Instructions for use

  1. Import the code module basMouseHook into your database application from the demonstration database: Access 2000 version - Access 97 version.

  2. From within your forms OnOpen event, add this simple code:

    Private Sub Form_Open(Cancel As Integer)
         Static MouseHook As Object
         Set MouseHook = NewMouseHook(Me)
    End Sub

And that's all you need to know to make use of this new technique. Hopefully some of you will find this new method useful - if you do, feel free to leave your comments at the end of the article.

If you like this solution and appreciate the effort that went into creating it, please consider donating:

If you want further details on how the basMouseHook VBA module works... read on.

How this works -- Warning, quite technical!

VBA allows us, with the help of the RtlMoveMemory API call, to cast any numeric value into a VBA variable. So for instance, we can cast a Long integer into an Object variable, if we so wish. Ordinarily by doing this you will cause an instant crash since VBA expects the Object variable to contain a pointer to a valid in-memory COM object, and so will attempt to call the QueryInterface / AddRef / Release methods of the COM IUnknown interface when attempting to do anything at all with the variable.

However, if we setup an area of memory that replicates the layout, interfaces and machine code that VBA expects to find in an Object variable and then cast a pointer to that in-memory object into an Object variable, then VBA will treat our Object as any ordinary COM object.

This approach has several issues that have to be addressed:

  1. We need a way of setting up that area of memory correctly, including all the normal things that COM objects need - i.e. the actual native machine code, VTables and their pointers for all of the interfaces we support, and associated data sections.
  2. Adjusting relative memory offsets at runtime.
  3. Native machine code written in-memory like this has no way of interacting with the Operating System by default since it has no "handles" to any DLL libraries - it is just pure x86 machine code. So at first we have no method to call any APIs ... so we're quite limited in what we can offer. Fortunately this can be solved.

To solve #1 and #2

The approach I use is to have a "loader" or "shell code" which is written in pure assembly using only x86 opcodes that fall into the ASCII character set (so that we can reliably contain this machine code within a String variable in VBA). The loader code will be instantiated by VBA on it's first call to IUnknown::QueryInterface on our object.

The loader code is responsible for setting up the area of memory for our COM-compatible object and for adjusting the relative memory/pointer offsets before finally calling IUnknown::QueryInterface. The machine code that our object uses is encoded with a Base64-style encoding, which is also decoded by our loader on the first call to IUnknown:QueryInterface - this allows us full freedom to use all x86 opcodes once the loader has done its job.

To solve #3

The solution I use is to pass a handle to the kernel32 library and a pointer to the GetProcAddress API function from VBA to our COM object via a Init() method call. Once our COM object has the kernel32 handle and can call the GetProcAddress function, it can then call any other API functions at its discretion.

So there you go - a little insider info into how this technique all works. No doubt some people will call this approach a "hack", but to me this is just a "new technique" - the end result is a perfectly valid COM-compatible object created in memory dynamically from VBA code.


Regards,

Wayne Phillips
http://www.everythingaccess.com



Rate this article:   Your rating: Poor Your rating: Not so good Your rating: Average Your rating: Good Your rating: Excellent

Have your say - comment on this article.

What did you think of 'A new method for disabling the Mouse Scroll Wheel in Access forms'?


1.

Alan Cossey says...

12 August 2009

 
Lovely bit of work and perfect timing for me too to produce it now as it will save me from having to distribute a dll with some work.

2.

AcoustiMan says...

13 August 2009

 
Hi Wayne,
impressive example of software engineering! I was seachin a long time for a working solution. I'm gonna try your solution right away.
Thank you very much! Keep up the great work.
Regards,
Julien

3.

Bruce Jonson says...

13 August 2009

 
Awesome work! I have always used Stephan's DLL solution but this will be a great replacement.

Thanks for this great piece of code

4.

Wayne Phillips says...

14 August 2009

 
Thanks everyone! I'm glad it's of use to you!

Check back soon for some other products using this new in-memory native-code Objects technique.

5.

Dan Waters says...

22 August 2009

 
Excellent!

My Customers and I thank you!!

Dan

6.

Jaafar tribak says...

24 August 2009

 
Great technique !

I have adapted the code to work for excel worksheets and worked beautifully.

Can you apply this technique to other events such as Mouse Clicks, Keystrokes etc ... ?

Regards.

7.

Wayne Phillips says...

24 August 2009

 
Thanks for the info Jaafar - great to know it works in Excel too.

With regard to the second part of your comment -- in particular, what were you looking to achieve? Pretty much anything is possible with this technique, although I'm not as familiar with Excel as I am with Access, so I am unsure as to what limitations you might be trying to overcome.

Regards,
Wayne

8.

Jaafar tribak says...

25 August 2009

 
Thanks for responding Wayne.

I am not as familiar with Access as with Excel but I guess subclassing the 2 applications should be similar.

As you may know subclassing office applications directly in VBA without resorting to a DLL can be problematique (potential crashing). The technique you have used here seems to overcome this issue by using machine code.

My question is : Can one use this same technique to intercept other window messages directed to access or excel other than the WM_MOUSEWHEEL msg ? that would be really amazing .

Again. Thank you

9.

Wayne Phillips says...

25 August 2009

 
Hi Jaafar,

Indeed, it is technically possible to intercept any of the windows messages using the same technique with very little to change to the code at my end.

So, what specific limitation in Excel do you want to apply this technique for? For example, we can intercept mouse clicks and key strokes messages using this technique, but it would be of limited use in Access since we have form events for those messages anyway -- so I'd like to know what limitation in Excel you would like to use this technique on, then perhaps I can provide a solution for you.

Kind regards,
Wayne

10.

Jaafar tribak says...

26 August 2009

 
Thanks Wayne.

As you may know there is no event associated with scrolling excel worksheets. I have managed to intercept the scrolling msg via subclassing before but as i mentioned in my previous post, this is potentially dangerous at best and under some OS like Win XP SP3 the whole application frezes(works on SP2 though).

So my goal is to have some code that will safely prevent scrolling the active worksheet past Row(50) for example. Obviously using this new thecnique.

I guess the core of the code you provided that disables the Mousewheel is hardcoded in the "NativeCode" string. Would adapting this approach to meet other needs mean having to learn machine code ? and If so, where can i learn it ?

Regards.

11.

Wayne Phillips says...

26 August 2009

 
Hi Jafaar,

Unfortunately it's not as simple as just learning machine code, since there are many more hurdles that you need to overcome (as discussed in the article) before you eventually get down to the NativeCode string used by this technique.

If you can provide me with an Excel spreadsheet (to myname@everythingaccess.com) that demonstrates what you'd like the NativeCode to do (i.e. an Excel file containing your VBA subclassing code), then I'll see what I can do for you.

Regards,
Wayne

12.

Kurt says...

28 August 2009

 
Wayne,

thanks for this great tool, superb.

Kurt

13.

Alyssia says...

28 August 2009

 
Great tool! Thanks for sharing

14.

Dennis Thomas says...

28 August 2009

 
Our company still uses Access 97; does this code work with that version as well? I am currently using Stephan's DLL where you must place the Mouse Hook DLL in the affected directory. Not successful for all users on our shared server drive.

If you have your version working in Access 97 I would appreciate gaining access to it.

Thanks

15.

Wayne Phillips says...

28 August 2009

 
Dennis,

I've just updated the article to include a link to an Access 97 .MDB file as well. I had to adjust the routines slightly in order to work around the way that VBA5 doesn't allow one to coerse COM interfaces to an Object variable, yet VBA6 does. The updated routine now works in both VBA5 and VBA6, so I've also changed the Access 2000 version to match.

Do please let me know how you get on with it.

Regards,
Wayne

16.

Dennis Thomas says...

28 August 2009

 
Thank you very much for the Access 97 code. It works very well.

17.

beth says...

31 August 2009

 
Thank you for the code! How can I make the "disabled scroll" the default?

18.

Wayne Phillips says...

01 September 2009

 
Hi Beth,

Please follow the instructions in the article section titled "Instructions for use", which shows you how to do that.

Regards,
Wayne

19.

Neil Sargent says...

01 September 2009

 
Wow, what a fantastic fix to a long standing problem!

I believe that many people will want to simply disable the mouse wheel and will not need switch scolling on and off.

Therefore, I recommend that the scrolling is disabled by default in the NewMouseHook procedure with the final line

NewMouseHook.Scroll = False

It is then possible to disable the mouse wheel for a form with the following single insertion of code in the Form_Open() or Form_Load() events:

Static MouseHook As Object
Set MouseHook = NewMouseHook(Me)


20.

Wayne Phillips says...

01 September 2009

 
Hi Neil,

Thanks - excellent suggestions.

I've now updated the VBA module so that the Scroll property is now False by default, as you suggested. I've also updated the article to make use of the Static local variable rather than the module level variable -- another great suggestion.

Again -- many thanks for your input!

21.

Dave H says...

16 September 2009

 
Finally, a great solution with no DLL hell!

My prior solution was the MouseWheel.dll. It worked great on a single form. Issues arose when users navigated from a detail form (single record) to as list form, then on to another detail form. The DLL prevented the mouse from scrolling records, but also locked up all mouse events on the first detail form when you navigated back. Too many object calls on the stack making the mouse event processing freeze.

Your solution works as intended. Thanks!

22.

Ken says...

29 September 2009

 
This is really nice. I'd love more info on how you came about creating the string of native code though. Did you write code in assembly or did you convert it from something else. I'm aware that you can convert the .net MSIL code to native machine code but I just have vague ideas.

23.

Wayne Phillips says...

29 September 2009

 
Hi Ken,

To give a little more info;

The native code string is actually generated from a custom compiler that I wrote - it's essentially a mix of assembly and C++, but generates an assembled code block that is completely self contained.

Just in case you're thinking that I'm a bit mad going to the expense of writing a compiler just for this mouse hook routine... indeed that was certainly not the primary reason ;-)... there's something much larger, and much more useful that I'm working on!

(... to be continued...)

24.

Alicia H says...

29 September 2009

 
It works beautifully. Thanks for this simple solution.

25.

Wayne Phillips says...

30 September 2009

 
You're welcome, Alicia. Thanks for your feedback :)

26.

--=Franky=-- says...

06 October 2009

 
thx a lot for it.
I understand nothing about the Code but it works well :-)

27.

Leah says...

06 October 2009

 
Worked like a charm! THANK YOU! Tried to use the other methods, but this one worked the best and it is much, much easier to use!

28.

Dawie Poolman says...

07 October 2009

 
Hi Wayne
This is a phenomenal piece of work. It can potentially solve a long standing issue in Excel with embedded list boxes that crashes Excel when you try and scroll in them. See the Forum below (one of many out there)
http://www.xtremevbtalk.com/archive/index.php/t-226433.html

Please share with if you believe that would be possible? Just need a handle on the listbox first?
Regards
Dawie

29.

Wayne Phillips says...

07 October 2009

 
Leah & Franky… thanks for your kind words. You're welcome.

Dawie, can you provide a simple Excel file demonstrating the issue? (e-mail <MyFirstName>@everythingaccess.com) It certainly sounds as if it is something that could be solvable in the same manner, so If I think it will help, I'll certainly try.

30.

Wayne Phillips says...

08 October 2009

 
Dewie,

Please find the solution to the Excel ListBox crash issue, using the same technique to the Excel file you sent me, here:

http://www.everythingaccess.com/downloads/ExcelListBoxMouseHookExample.zip

HTH,
Wayne

31.

Dawie Poolman says...

08 October 2009

 
Wayne
Your incorporation of this solution to Excel's listbox scroll crash issue is THE neatest solution that I have seen in a long time. You should let Bill Gates know about how you are fixing his bugs :-)

Awesome, brilliant, innovative, old school hacking solution.
Hats off and respect!

32.

Wayne Phillips says...

08 October 2009

 
You're welcome, Dewie, and thanks :)

33.

anthony mosco says...

16 October 2009

 
This is the best solution to this problem that I have found in the last year. Implemented another version and was fine until an upgrade was performed on my XP, and the old method was producing odd breakpoints that were not set.

This is so much easier and your work on this is greatly appreciated, as I had to change the call to 10 different complex forms, and had one small piece to replace code in several places!

34.

Wayne Phillips says...

19 October 2009

 
I keep getting asked "what is it that you're working on" with regards to my earlier teasers... so here's a snippet to whet your appetite:

Think "flexible data grid control" and you aren't far wrong.

Using the native assembly technique shown in this article ultimately means that you get a new control without needing any third party DLL.

Key Features;

Supports tree views and list views natively.
Supports live grouping and sorting.
Supports HTML cells (including hyperlinks)
Supports in-built cell controls such as buttons, check boxes, combo boxes.
Supports many different data sources (including SQL/Recordset, optimized internal lists/trees, "on-demand" using VBA callback, and more)
Supports unlimited conditional formatting throughout using a built-in expression compiler for optimal performance.
Supports complex expressions in conditional formatting and for cell sources.
Supports conditional formatting for all available cell properties (not just simple Bold/Underline options like Access).
Supports auto-fit cell widths and/or item heights.
Supports variable item heights.
Supports images / pictures with a variety of transparency options (even for bitmaps).
Supports several gradient options for background fills (for cells and master control).
Supports changing the opacity of fills, allowing for a modern "bleed-through" effect.
Supports rounded corners for items and groups.
Supports the concept of bindings and expressions for all conditional formatting options. For example you can easily bind the background fill color to a field in your datasource, if you wanted to.
Supports conditional formatting inheritence (conditional formatting can be set on each display column and can inherit from a "master" defaults class)
Supports drag 'n' drop.
Supports multi-select.

Sounds interesting? If you would like to be a part of the beta team, please e-mail me now. Beta is starting very soon!

beta@everythingaccess.com

I can only offer a limited number of people to participate in the beta, so please let me know ASAP if you are interested.

Wayne Phillips

35.

Ingo says...

20 October 2009

 
Perfect - it is simple and works!
Thanks a lot - you helped me :-)
Regards

36.

Steve says...

02 November 2009

 
WOW WOW HOLY COW WOW!!!

Sorry for the excitement but after spending months developing a new form for data entry that help calculate the time needed to process a job, it all came to a crashing halt when the test users hit the scroll wheel. It cycled thru records that did not exist and royally messed thing up.

I tried the "Old" solution but it just crashes my application.

Wayne this solution has saved me from having to rewrite my application. Thanks a million and what a great simple fix to a terrible problem.

Steve

37.

Mark says...

12 November 2009

 
This solution makes so much crazy work-arounds obsolete :)
Simply great!

Thanks,
Mark

38.

Enia Rrolyu says...

17 November 2009

 
i found microsoft script for disabling mouse wheel so annoying. everytime i need to update vba code behind the form, i would have to close and re-open otherwise it would hang. so annoying!

thanks so much wayne for this. big help!!

39.

Jonny Moir says...

18 November 2009

 
Nice concept and I like where you're going with it. With a bunch of work this could lead to controls creating their own reference support rather dynamically. Imagine that instead of having bulky linked DLLs with potentially useless sections, a skilled programmer could design their own slim COM-compatible support for their own controls dynamically. But I'm dreaming now...

Anywho, congrats on this. This beats the hell out of my solution which was to store the MouseHook.DLL in a common share and point the module to look for it in that location so as not to have to distribute extra files.

40.

Wayne Phillips says...

19 November 2009

 
Thanks to all for the kind comments.

Jonny:

Indeed, custom controls that require no extra references, is where I'm heading with this technique. Firstly it will be controls designed by me, but then later I might just release the compiler too... so maybe your dream might just come true ;)

All: please keep the beta request e-mails coming in (see post #34 above). Beta testing starting very soon.

41.

Matt Hawke says...

16 December 2009

 
Worked flawlessly with Access 2003 - I just dropped in the module and made the call as specified above. A brilliant, no-nonsense workaround for a terribly frustrating issue.

Thanks Wayne for making your work available for all to use!

42.

Sadaf says...

30 December 2009

 
Perfect...

Thank you

43.

Ralf Lepelmann says...

11 January 2010

 
Greetings from Germany

It works great!!! I'm enthusiastic! Thank you ...

44.

Michael says...

12 January 2010

 
F A N T A S T I C.
I have been using Lebans solution for a while but found it didn't work perfectly. Your solution appears to do the trick.

Thank you

45.

Kate says...

10 February 2010

 
Thank you so much! Very simple to implement, really appreciated by a beginner!

46.

Greg says...

11 February 2010

 
This is a great solution to the problem. In today's world rarely can I actually find something that works!

Thank you very much.

47.

solyluna says...

25 February 2010

 
Thank you so much!! Great!

48.

Jacron says...

05 March 2010

 
Beautiful. Just _beautiful_. Simple, easy to use, works across computers with no hassle. Very well done to you, sir.

49.

alan honorio says...

11 March 2010

 
many thanks for this, it really works great!!!

50.

Tomolena says...

10 April 2010

 
Magnificent bit of work Wayne. Stumbled upon your solution at utteraccess.com. I too have been using Mr. Lebans' solution for years. Worked perfectly for me, but always having to include the required DLL file with my apps is a bit cumbersome. You've provided the Access community with something that's neat, reliable, and very useful. Thankyou for sharing and CONGRADULATIONS on this.

51.

Wayne says...

10 April 2010

 
Thanks for your kind words, Tomolena. :)

52.

Tom van Stiphout says...

08 May 2010

 
Very interesting technique. When I started reading I figured you would have gotten the in-memory object using a debugger - if you know where to look you can find them, and then convert to string. That would have given me some hope I could adopt your technique to my issue, which is to subclass a window and process WM_PAINT. Since your compiler is private I will resort to the Lebans technique.
Hats of for a great and creative solution. You're going places.

53.

Wayne says...

08 May 2010

 
Thanks for your kind words, Tom.

Let me know if the Lebans solution doesn't work out for you -- perhaps I can implement something for you...

Cheers,

Wayne

54.

Oli says...

26 May 2010

 
Thank you so much for this solution that took me not even a minute to implement into my database. I found it via Google after a while of search.

55.

Paul says...

02 June 2010

 
Thanks, works perfectly!

56.

Peter says...

22 June 2010

 
Thanks so much for this code. I don't understand the details of how it works, but the thing is - IT WORKS, and oh so easy to use. Very clever!

Have your say...

Name
E-mail (e-mail address will be kept private)
Comments
Verify Code
Verification Code
Please note: It may take 30 seconds after clicking submit - please be patient to avoid duplicate submissions.   All comments must be approved before being displayed on this web page.  This process may take up to 24 hours.


Site tools

SmartAccess
300+ articles.
Promo code EA-45K2D46TVS