Click here to Skip to main content
Click here to Skip to main content

Middle Mouse Button (or Wheel) to Doubleclick (VC6)

By , 26 Jul 2010
 

Config GUI

  • Displays function state
  • Enables / disables doubleclick function
  • Chooses whether to confirm exit of the application
Screenshot - MBtn2DblClickVC.jpg

Introduction

Once I had a logitech mouse and everything was fine!

Logitech provided you with a handy application called EMEXEC.exe, which would allow you to set up the behavior of the available buttons. The function I most used was the converter for the middle mouse button/wheel which would turn a single click from the specific button into a left mouse button double click. But what if you uninstalled mouseware, or what if you have to work on a machine without LogiMouse. You're not able to install MouseWare until you have a LogiMouse connected. Pretty soon I began to miss this cool feature from Logitech and said to myself that I am going to write a tool which implements exactly this feature. And here we go....

Problem

The trickiest part I was facing was that the application had to get the clicks from all the other applications running at this time. Therefore a global mousehook had to be set and that might be a little bit tricky. You have to place the code for the mousehook callback in a external DLL with a custom code-segment. I used MouseHook.cpp - I found this a while ago while surfing the web, but I don't remember where I got it from (sorry!). If this is your MouseHook.cpp, please drop a line.

Setting up the MouseHook

__declspec(dllexport) BOOL setMyHook(HWND hWnd) {
    if(hWndServer != NULL)
        return FALSE; // already hooked!
    
    hook = SetWindowsHookEx(WH_MOUSE_LL,
        (HOOKPROC)msghook,
               hInst,
               0);
    if(hook != NULL) { /* success */
        hWndServer = hWnd;
        return TRUE;
    } /* success */

    return FALSE; // failed to set hook
} // setMyHook

...
//Hook-Function:

...

static LRESULT CALLBACK msghook(UINT nCode, WPARAM wParam, LPARAM lParam) {
    if(nCode < 0) { /* pass it on */
        CallNextHookEx(hook, nCode, wParam, lParam);
        return 0;
    } /* pass it on */
    
    if(wParam == WM_MBUTTONUP ){
        PostMessage(hWndServer, UWM_MBTNCLICK, 0, 0);
    }
    
    return CallNextHookEx(hook, nCode, wParam, lParam);
} // msghook

Clearing the Hook

__declspec(dllexport) BOOL clearMyHook(HWND hWnd) {
    if(hWnd != hWndServer || hWnd == NULL)
        return FALSE;
    
    BOOL unhooked = UnhookWindowsHookEx(hook);
    
    if(unhooked)
        hWndServer = NULL;
    
    return unhooked;
} // clearMyHook

Converting the Click

BEGIN_MESSAGE_MAP(CMBtn2DblClickDlg, CDialog)
    ON_REGISTERED_MESSAGE(UWM_MBTNCLICK, OnMyMbtnClick)

....
LRESULT CMBtn2DblClickDlg::OnMyMbtnClick(WPARAM wParam, LPARAM lParam) {
    CPoint pt;
    GetCursorPos(&pt);
    
    // width
    int cx = GetSystemMetrics(SM_CXSCREEN);
    // height
    int cy = GetSystemMetrics(SM_CYSCREEN);
  
    //Reformat screen coordinates
    int x2 = ( 65535 * pt.x ) / cx;
    int y2 = ( 65535 * pt.y ) / cy;
 
    INPUT input[2];
    
    input[0].type = INPUT_MOUSE;
    input[0].mi.dx = x2;
    input[0].mi.dy = y2;
    input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN;
    input[0].mi.mouseData   = 0L; 
    input[0].mi.time        = 0L; 
    input[0].mi.dwExtraInfo = 0L;

    input[1].type = INPUT_MOUSE;
    input[1].mi.dx = x2;
    input[1].mi.dy = y2;
    input[1].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP;
    input[1].mi.mouseData   = 0L; 
    input[1].mi.time        = 0L; 
    input[1].mi.dwExtraInfo = 0L;
    
    SendInput( 1, &input[0], sizeof(input[0]));
    SendInput( 1, &input[1], sizeof(input[1]));

    Sleep(40);
    
    SendInput( 1, &input[0], sizeof(input[0]));
    SendInput( 1, &input[1], sizeof(input[1]));
    
    return 0;
} // MBtn2DblClickDlg::OnMyMouseMove

Credits

  • CReadOnlyEdit by Kevin Bond
  • CRegistry by Shane Martin
  • CSimpleTray by T1TAN / SprdSoft Inc.
  • CWinStartup (by unknown)

I hope this article is useful to anyone or helps to understand how mousehooks work. If you are interested in the source of a good MouseHook DLL, I'd recommend you to download this source since the original location of MouseHookManager is gone and I couldn't find it on the net anymore so far. So hope you enjoy and all the best - hope you'll be reading my next articles as well.

Cheers, Kim

History

  • 9th July, 2007: Initial post
  • 15th April, 2010: Corrected a bug with the persitence of Registry settings enabled at startup and other config settings
  • 24th July, 2010: Updated the source and application so they work with Windows 7 on x64 systems

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

kim.david.hauser
Software Developer (Senior)
Switzerland Switzerland
Member
programmer and software junkie since 1991 zurich switzerland

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionmfc100d.dll is missing?memberNoxolos12 Mar '13 - 3:37 
Nice tool! I used your previous .NET 2.0 version of this one with Windows XP.
With Windows 7 x64 and your actual x64 version (VC6) I get the error that the file "mfc100d.dll is missing".
Do I miss something? Any Idea how to fix this?
QuestionEnable after re-boot ?memberFunnySurfnow14 Aug '11 - 5:24 
I have this issue that the program won't "Run at Startup" neither will it be enabled.
 
For now, the best way i found to make it start, is by the Startup folder in the start menu. Using Vista here. Each time the computer starts, then i have to "enable".
 
is there a better way to make this all automatically ?
 
thanx for any help.
GeneralMy vote of 5memberGPUToaster8 Sep '10 - 2:08 
Nice util!!
GeneralMy vote of 4memberSharjith2 Aug '10 - 9:49 
Good!
Generalhave a look atmemberTibor Blazko19 Apr '10 - 19:49 
for inspiration have a look at freeware http://www.highrez.co.uk/downloads/XMouseButtonControl.htm[^]
where you can do much more (f.e. assign feature to concrete application only - unfortunately to (keyboard) focused, not mouse-over one)
GeneralHook Procedure alternativememberFrank_Cheng14 Apr '10 - 12:38 
Here is an alternate method for generating double-click instead of posting message back to WndProc.
 
LRESULT WINAPI MouseHook(int code,WPARAM wp,LPARAM lp)
{
	DWORD dwStyle;
	INPUT ip[2];
	HWND hWnd;
	if (code==HC_ACTION)
	{
		switch (wp)
		{
			case WM_MBUTTONDOWN:
			case WM_NCMBUTTONDOWN:
				ZeroMemory(ip,sizeof(INPUT)*2);
				ip[0].type = INPUT_MOUSE;
				ip[0].mi.dx = ((LPMOUSEHOOKSTRUCT)lp)->pt.x;
				ip[0].mi.dy = ((LPMOUSEHOOKSTRUCT)lp)->pt.y;
				ip[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
				ip[1].type = INPUT_MOUSE;
				ip[1].mi.dx = ((LPMOUSEHOOKSTRUCT)lp)->pt.x;
				ip[1].mi.dy = ((LPMOUSEHOOKSTRUCT)lp)->pt.y;
				ip[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;
				SendInput(2,ip,sizeof(INPUT));
				return 1;
			case WM_MBUTTONUP:
			case WM_NCMBUTTONUP:
				ZeroMemory(ip,sizeof(INPUT)*2);
				ip[0].type = INPUT_MOUSE;
				ip[0].mi.dx = ((LPMOUSEHOOKSTRUCT)lp)->pt.x;
				ip[0].mi.dy = ((LPMOUSEHOOKSTRUCT)lp)->pt.y;
				ip[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
				ip[1].type = INPUT_MOUSE;
				ip[1].mi.dx = ((LPMOUSEHOOKSTRUCT)lp)->pt.x;
				ip[1].mi.dy = ((LPMOUSEHOOKSTRUCT)lp)->pt.y;
				ip[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;
				SendInput(2,ip,sizeof(INPUT));
				return 1;
		}
	}
	return CallNextHookEx(MouseHookID,code,wp,lp);
}

GeneralMany thanks!memberfishophile30 Jun '09 - 10:51 
Just upgraded to Vista and this was my main gripe. Now fixed easily!
 
May rose petals strew your every path and may all your children get the freedom of Baku.
 
~Fish
QuestionThank you..memberFarzad Eskafi30 Apr '09 - 17:11 
Thank you. This is exactly what I was looking for. I'm a bit rusty on coding and trying to get back into it. This is also perfect for elderly and people with disability. Some of the folks are not able to double-click nor hold the left-click down to drag the items. This program would be very handy.
 
I was wondering, do you know of anyone who could help me with the second part? It should work like the following:
 
1. Move the pointer on a file/folder
2. Press the Wheel Click
3. Move the pointer and it moves/drags the file/folder.
4. Press the click again and releases the file/ folder
 
Does this make sense?
 
Thank you again,
AnswerRe: Thank you..memberf.eskafi1 May '09 - 3:57 
To clarify the steps:
 
1. Move the pointer on a file/folder
2. Press & release the Wheel Click
3. Move the pointer and it moves/drags the file/folder.
4. Press and release the click again and the pointer releases the file/ folder
 
This program would be very handy for people who can not hold the left-mouse click down. Does this make sense?
 
Thanks again,
Generalleft-handed issue [modified]memberHatod6 Nov '07 - 22:33 
Hi again!
 
Recently I've discovered a bug in my similar software, and for curiosity I've tested yours too.
 
If one changes the mouse settings (from Control Panel) to left-handed usage, then our programs generate double right clicks.
In Win2000 and XP this left-handed feature is called "switch primary mouse buttons", and it seems, that this switching happens _after_ the low level hook runs.
 
It is embarrassing for me, because I am left-handed, but usually using the mouse as right-handed.
 
There is a kind of workaround: search MSDN's Platform SDK section for GetSystemMetrics function, and call with the SM_SWAPBUTTON parameter.
 
I will check the "swap state" just before setting the hook. It will not be "real-time", but with two clicks on the SysTray icon the behaviour gets corrected. (My app enables/disables the double click simulation if you click the icon.)
And it is very rare to swap those primary buttons. If one swaps them, then it probably stays swapped for long periods.
 

(Back in the times, under Windows 98 there was no low level mouse hook, and I used the GetMessage hook to do the simulation. There this bug didn't exist, probably because that hook is not low level. Anyway, it is clear that this low level hook must be used, because the GetMessage hook caused serious crashes in MSOffice.)
 

 
-- modified at 12:19 Wednesday 7th November, 2007

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 26 Jul 2010
Article Copyright 2007 by kim.david.hauser
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid