MouseWheel.mak
PROJ = MouseWheel
OBJS = $(PROJ).OBJ
CFLAGS = $(CFLAGS) /W3 /O1 /DWIN32_LEAN_AND_MEAN /D_WIN32_WINNT=0x400
LFLAGS =/DLL /MERGE:.idata=.data /MERGE:.rdata=.text /SECTION:.shared,RWS \
/EXPORT:DoNothing /BASE:0x20000000
LIBS = /OUT:$(PROJ).DLL kernel32.lib user32.lib advapi32.lib
!if "$(DEBUG)" == "1"
CFLAGS = $(CFLAGS) /YX /Fp"$(PROJ).PCH" /D_DEBUG /Zi /Fd"$(PROJ).PDB"
LFLAGS = $(LFLAGS) /DEBUG /DEBUGTYPE:CV
!else
CFLAGS = $(CFLAGS) /DNDEBUG
!endif
$(PROJ).DLL: $(OBJS)
echo >NUL @<<$(PROJ).CRF
$(LFLAGS) $(OBJS) $(LIBS)
<<
link @$(PROJ).CRF
.cpp.obj:
CL $(CFLAGS) /c $<
MouseWheel.h
#define MW_SCROLL_INCREMENT_MASK 0x0000FFFF #define MW_SCROLL_PAGE 0x00010000 // Dummy routine in MouseWheel.DLL to allow implicit importing extern "C" void DoNothing(void);MouseWheel.cpp
//==========================================
// Matt Pietrek
// Microsoft Systems Journal, June 1997
//==========================================
#include <windows.h>
#include "mousewheel.h"
//=============================================================================
// Data
//=============================================================================
// Shared Data
#pragma data_seg(".shared") // Make a new section that we'll make shared
HHOOK g_hHook=0; // HHOOK from SetWindowsHook
UINT g_WM_MOUSEWHEEL = 0; // Window message for mousewheel scrolling
// Back to regular, nonshared data
char g_szRegPath[] = "Software\\WheatyProductions\\MouseWheel";
#pragma data_seg()
// Per process data
BOOL g_IsHookingProcess = FALSE;
BOOL g_okToAct = FALSE;
BOOL g_pageIncrements = FALSE;
unsigned g_incrementAmount = 1;
//=============================================================================
// Start of code
//=============================================================================
LRESULT CALLBACK GetMsgProc(
int code, // hook code
WPARAM wParam, // removal flag
LPARAM lParam) // address of structure with message
{
LRESULT retValue = 0;
// Be a good citizen, and call the other hooks
retValue = CallNextHookEx( g_hHook, code, wParam, lParam );
if ( FALSE == g_okToAct ) // Bail out if this process isn't one that
return retValue; // we care about
LPMSG pMsg = (LPMSG)lParam; // Make a ptr to the MSG structure for exam
// If it's not a MOUSEWHEEL message, or if the app is just PEEK'ing,
// bail out now.
if ( g_WM_MOUSEWHEEL != pMsg->message || (wParam == PM_NOREMOVE) )
return retValue;
// By this point, we know a WM_MOUSEWHEEL message will be delivered.
// Synthesize the appropriate WM_VSCROLL message(s) and post them
WPARAM upDown;
if ( g_pageIncrements )
upDown = (short)HIWORD(pMsg->wParam) > 0 ? SB_PAGEUP : SB_PAGEDOWN;
else
upDown = (short)HIWORD(pMsg->wParam) > 0 ? SB_LINEUP : SB_LINEDOWN;
for ( unsigned i = 0; i < g_incrementAmount; i++ )
PostMessage(pMsg->hwnd, WM_VSCROLL, upDown, 0 );
return 1;
}
UINT GetMouseWheelMsg( void )
{
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
if ( !GetVersionEx(&osvi) )
return WM_MOUSEWHEEL; // Got a better idea?
// NT 4 and later supports WM_MOUSEWHEEL
if ( VER_PLATFORM_WIN32_NT == osvi.dwPlatformId )
if ( osvi.dwMajorVersion >= 4 )
return WM_MOUSEWHEEL;
// Future Win32 versions ( >= 5.0 ) should support WM_MOUSEWHEEL
if ( osvi.dwMajorVersion >= 5 )
return WM_MOUSEWHEEL;
// Hmmm... an older version. The mouse driver support app should
// have registered a window message for it. By registering the
// same message, we should get back the same message number.
// Note that "MSWHEEL_ROLLMSG" below is a #define taken from ZMOUSE.H,
// which is from the "Intellimouse SDK".
return RegisterWindowMessage( "MSWHEEL_ROLLMSG" );
}
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved) // reserved
{
//=========================================================================
// DLL Process attach
//=========================================================================
if ( fdwReason == DLL_PROCESS_ATTACH )
{
// We don't need thread notifications for what we're doing. Thus, get
// rid of them, thereby eliminating some of the overhead of this DLL,
// which will end up in nearly every GUI process anyhow.
DisableThreadLibraryCalls( hinstDLL );
if ( lpvReserved ) // Is this main process that sets the hook and
{ // loads this DLL initially ???
if ( g_hHook ) // If we've already hooked, fail the DllMain
return FALSE;
g_IsHookingProcess = TRUE;
// Set a global GetMessage hook
g_hHook = SetWindowsHookEx( WH_GETMESSAGE, (HOOKPROC)GetMsgProc,
hinstDLL, 0 );
g_WM_MOUSEWHEEL = GetMouseWheelMsg();
}
// Get the name of the parent process EXE, and uppercase it
char szExeName[MAX_PATH];
GetModuleFileName( 0, szExeName, sizeof(szExeName) );
CharUpperBuff( szExeName, lstrlen(szExeName) );
//
// Determine if the parent process EXE's name is in the registry, under
// our special key. If not, we won't bother translating mousewheel
// scroll messages into WM_VSCROLL messsages.
//
HKEY hKey;
if (ERROR_SUCCESS==RegOpenKey( HKEY_CURRENT_USER, g_szRegPath, &hKey))
{
DWORD dwValue = 0;
DWORD dType, cbValue = sizeof(dwValue);
if ( ERROR_SUCCESS == RegQueryValueEx( hKey, szExeName, 0, &dType,
(PBYTE)&dwValue, &cbValue))
{
g_incrementAmount = dwValue & MW_SCROLL_INCREMENT_MASK;
g_pageIncrements = dwValue & MW_SCROLL_PAGE ? TRUE : FALSE;
g_okToAct = TRUE;
}
RegCloseKey( hKey );
}
// else.... This process's EXE wasn't under our key. Do nothing.
}
//=========================================================================
// DLL Process detach
//=========================================================================
else if ( fdwReason == DLL_PROCESS_DETACH )
{
if ( g_IsHookingProcess && g_hHook ) // The main EXE that loaded
{ // this DLL is shutting down,
UnhookWindowsHookEx( g_hHook ); // so remove the hook
g_hHook = 0;
}
}
return TRUE;
}
//-------------------------------------------------------------
// Dummy startup routine that does nothing except call DllMain
// This cuts out all of the standard startup code crud that
// bloats the DLL, and makes it take longer to load
//-------------------------------------------------------------
extern "C" BOOL __stdcall _DllMainCRTStartup(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved) // reserved
{
return DllMain( hinstDLL, fdwReason, lpvReserved );
}
//----------------------------------------------------------------------------
// Dummy routine that allows the main EXE to have an implicit import of
// this DLL.
//----------------------------------------------------------------------------
void DoNothing(void)
{
}
Figure 2 MW.EXE
AddProgram.cpp
// AddProgram.cpp : implementation file
//
•
•
•
/////////////////////////////////////////////////////////////////////////////
// CAddProgram message handlers
void CAddProgram::OnBrowseButton()
{
// TODO: Add your control notification handler code here
CFileDialog dlg( TRUE, "EXE", "*.EXE", OFN_FILEMUSTEXIST, 0, this );
if ( IDOK != dlg.DoModal() )
return;
((CWnd*)GetDlgItem(IDC_NEW_PROGRAM_NAME))->SetWindowText(dlg.GetPathName());
}
BOOL CAddProgram::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
CSpinButtonCtrl * pSpinButton = (CSpinButtonCtrl *)GetDlgItem( IDC_SPIN1 );
pSpinButton->SetBuddy( GetDlgItem(IDC_EDIT_INCREMENT_AMOUNT) );
pSpinButton->SetRange( 1, 100 );
// Set the line scrolling BOOL to TRUE (by default), and check
// the "line scrolling" checkbox to reflect this
m_fLineScrolling = TRUE;
((CButton *)GetDlgItem(IDC_RADIO_LINE))->SetCheck( 1 );
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CAddProgram::OnRadioLine()
{
// TODO: Add your control notification handler code here
m_fLineScrolling = TRUE;
}
void CAddProgram::OnRadioPage()
{
// TODO: Add your control notification handler code here
m_fLineScrolling = FALSE;
}
void CAddProgram::OnOK()
{
// TODO: Add extra validation here
CEdit * pEdit = (CEdit *)GetDlgItem( IDC_NEW_PROGRAM_NAME );
if ( 0 == pEdit->GetWindowTextLength() )
{
MessageBox( "Must specify a program name" );
return;
}
CDialog::OnOK();
}
MWDlg.cpp
// MWDlg.cpp : implementation file
//
•
•
•
/////////////////////////////////////////////////////////////////////////////
// CMWDlg message handlers
BOOL CMWDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CMenu* pSysMenu = GetSystemMenu(FALSE);
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
m_notifyIconData.cbSize = sizeof(m_notifyIconData);
m_notifyIconData.hWnd = this->m_hWnd;
m_notifyIconData.uID = WM_MY_TRAY_NOTIFICATION;
m_notifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
m_notifyIconData.uCallbackMessage = WM_MY_TRAY_NOTIFICATION;
m_notifyIconData.hIcon = m_hIcon;
lstrcpy( m_notifyIconData.szTip, "MouseWheel" );
Shell_NotifyIcon( NIM_ADD, &m_notifyIconData );
RefreshProgramListbox();
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMWDlg::OnPaint()
{
// If the user hasn't asked to see the dialog yet, hide it. It would be
// better to not show the dialog in the first place, but MFC seems bound
// and determined to get that dialog on the screen!
if ( !m_okToShow )
{
ShowWindow( SW_HIDE );
}
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMWDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CMWDlg::OnAddProgram()
{
// TODO: Add your control notification handler code here
CAddProgram addDialog( this );
if ( IDOK != addDialog.DoModal() )
return;
// Low WORD is the number of units to scroll
DWORD dwRegValue = addDialog.m_incrementAmount;
// High word is flags. Right now, the only flag is the "scroll by pages" flag
if ( FALSE == addDialog.m_fLineScrolling )
dwRegValue |= MW_SCROLL_PAGE;
HKEY hKey = ((CMWApp *)AfxGetApp())->GetRegKey();
if ( !hKey )
return;
if ( 0 == RegSetValueEx(hKey,
addDialog.m_newProgramName,
0,
REG_DWORD,
(PBYTE)&dwRegValue,
sizeof(dwRegValue) ) )
{
RefreshProgramListbox();
MessageBox("You must shut down and restart MW.EXE for this change to "
"take affect");
}
else
MessageBox( "Error adding program to registry" );
RegCloseKey( hKey );
}
BOOL CMWDlg::RefreshProgramListbox( void )
{
CListBox * pListBox = (CListBox *)GetDlgItem(IDC_PROGRAM_LIST);
pListBox->ResetContent(); // Clear anything that's in there now
HKEY hKey = ((CMWApp *)AfxGetApp())->GetRegKey();
if ( !hKey )
return FALSE;
for ( unsigned i =0; ; i++ )
{
LONG hResult;
char szValueName[MAX_PATH];
DWORD dwValue;
DWORD type, cbValueName = sizeof(szValueName), cbValue = sizeof(dwValue);
hResult = RegEnumValue( hKey, i, szValueName, &cbValueName, 0,
&type, (PBYTE)&dwValue, &cbValue );
if ( ERROR_NO_MORE_ITEMS == hResult )
break;
char szLB_Line[ MAX_PATH + 32 ];
wsprintf( szLB_Line, "%s\t%s %u",
szValueName,
dwValue & MW_SCROLL_PAGE ? "PAGE" : "LINE",
dwValue & MW_SCROLL_INCREMENT_MASK );
pListBox->AddString( szLB_Line );
}
RegCloseKey( hKey );
return TRUE;
}
void CMWDlg::OnDeleteProgram()
{
// TODO: Add your control notification handler code here
CListBox * pListBox = (CListBox *)GetDlgItem(IDC_PROGRAM_LIST);
int curSel = pListBox->GetCurSel();
if ( LB_ERR == curSel )
{
MessageBox( "Must select an program to delete" );
return;
}
CString cLBText;
pListBox->GetText( curSel, cLBText );
CString cProgramName = cLBText.SpanExcluding( "\t" );
HKEY hKey = ((CMWApp *)AfxGetApp())->GetRegKey();
if ( !hKey )
return;
RegDeleteValue( hKey, cProgramName );
RegCloseKey( hKey );
RefreshProgramListbox();
}
void CMWDlg::OnOK()
{
// Default OK behavior should hide the dialog, not terminate the app
ShowWindow( SW_HIDE );
}
void CMWDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
m_notifyIconData.uFlags = NIF_ICON;
Shell_NotifyIcon( NIM_DELETE, &m_notifyIconData );
CDialog::OnClose();
}
//////////////////
// Handle notification from tray icon: display a message.
//
LRESULT CMWDlg::OnTrayNotification(WPARAM uID, LPARAM lEvent)
{
if ( lEvent == WM_LBUTTONDBLCLK )
{
m_okToShow = TRUE; // Allow the dialog to be shown
if ( !IsWindowVisible() )
ShowWindow( SW_SHOW );
if ( IsIconic() )
ShowWindow( SW_RESTORE );
}
#if 1
else if ( lEvent == WM_RBUTTONUP )
{
CMenu * pSysMenu = GetSystemMenu( FALSE );
if ( pSysMenu )
{
CPoint mouse;
GetCursorPos(&mouse);
pSysMenu->TrackPopupMenu( TPM_CENTERALIGN, mouse.x, mouse.y, this );
// delete pSysMenu;
}
}
#endif
return 0;
}
BOOL CMWDlg::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
// If it's the WM_SYSCOMMAND SC_CLOSE message, repost it as a WM_CLOSE message
// so that our normal WM_CLOSE handler will handle it.
if ( SC_CLOSE == nID )
PostMessage( WM_CLOSE );
return CDialog::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}