Visual Basic 6.0的集成开发环境中的代码编辑器不支持鼠标滚轮的操作,这给使用VB的人带来了很多不便,为了使它能支持鼠标滚轮操作,我制作了一个小程序帮它实现该功能。
这个程序实现的原理是,使用全局钩子截获窗口消息,通过相应滚轮消息,向VB编辑窗口发送视图滚动消息实现VB编辑窗口对鼠标滚轮的响应。
首先,建立一个MFC的Dll工程,向工程中添加下列函数:
下面这个函数用于安装钩子,之所以将其声明为这种形式,主要是考虑到其可扩展性。
BOOL __declspec(dllexport)__stdcall InstallHook(int nIDHook,const HINSTANCE hMod,DWORD dwThreadId)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
BOOL bResult;
HINSTANCE hInstance=NULL;
if(hMod==NULL)
{
hInstance=AfxGetInstanceHandle();
}else
{
hInstance=hMod;
}
switch(nIDHook)
{
case WH_CALLWNDPROC:
if(g_hWinProc!=NULL)
{
bResult=TRUE;
break;
}
g_hWinProc=SetWindowsHookEx(WH_CALLWNDPROC,(HOOKPROC)ProcessWndProc,hInstance,dwThreadId);
if(g_hWinProc!=NULL)
{
bResult=TRUE;
}
break;
case WH_GETMESSAGE:
if(g_hMsgProc!=NULL)
{
bResult=TRUE;
break;
}
g_hMsgProc=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)ProcessMsgProc,hInstance,dwThreadId);
if(g_hMsgProc!=NULL)
{
bResult=TRUE;
}
break;
default:
bResult=FALSE;
break;
}
return bResult;
}
下面这个函数用于卸载钩子,与前一个函数对应。
BOOL __declspec(dllexport)__stdcall UninstallHook(int nIDHook)
{
BOOL bResult;
switch(nIDHook)
{
case WH_CALLWNDPROC:
bResult=UnhookWindowsHookEx(g_hWinProc);
g_hWinProc=NULL;
break;
case WH_GETMESSAGE:
bResult=UnhookWindowsHookEx(g_hMsgProc);
g_hMsgProc=NULL;
break;
default:
bResult=TRUE;
}
return bResult;
}
下面的函数就是对PostMessage发送的消息的响应函数,其中对鼠标滚轮事件进行了响应,使VB支持滚轮。
LRESULT __declspec(dllexport)__stdcall CALLBACK ProcessMsgProc(
int nCode, // hook code
WPARAM wParam, // current-process flag
LPARAM lParam // address of structure with message data
)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
int n=0;
MSG *pMessage=(MSG*)lParam;
CWnd *pWnd=NULL;
CWnd *pScroll=NULL;
char buf[256];
if(pMessage->message==WM_MOUSEWHEEL && g_bVBHelper)
{
pWnd=CWnd::FromHandle(pMessage->hwnd);
if(pWnd!=NULL)
{
::GetClassName(pMessage->hwnd,buf,255);
CString sWndClass(buf);
if(sWndClass.Compare("VbaWindow")==0)
{
pScroll=pWnd->GetWindow(GW_CHILD);
while(pScroll!=NULL)
{
::GetClassName(pScroll->GetSafeHwnd(),buf,255);
sWndClass.Format("%s",buf);
if(sWndClass.Compare("ScrollBar")==0 && (pScroll->GetStyle()& SBS_VERT))
{
break;
}
pScroll=pScroll->GetWindow(GW_HWNDNEXT);
}
if(((short) HIWORD(pMessage->wParam)<0))
{
::SendMessage(pMessage->hwnd,WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),(LPARAM)(pScroll==NULL?0:pScroll->GetSafeHwnd()));
::SendMessage(pMessage->hwnd,WM_VSCROLL,MAKEWPARAM(SB_ENDSCROLL,0),(LPARAM)(pScroll==NULL?0:pScroll->GetSafeHwnd()));
}else if(((short) HIWORD(pMessage->wParam)>0))
{
::SendMessage(pMessage->hwnd,WM_VSCROLL,MAKEWPARAM(SB_LINEUP,0),(LPARAM)(pScroll==NULL?0:pScroll->GetSafeHwnd()));
::SendMessage(pMessage->hwnd,WM_VSCROLL,MAKEWPARAM(SB_ENDSCROLL,0),(LPARAM)(pScroll==NULL?0:pScroll->GetSafeHwnd()));
}
}
return 0;
}
}
return CallNextHookEx(g_hWinProc,nCode,wParam,lParam);
}
从上面的代码不难看出,解决问题的关键在于,截获所需要的消息(WM_MOUSEWHEEL),并将该消息转化为响应的消息(WM_VSCROLL)发送给目的窗口。其中,需要注意的是:我们使用的是全局钩子,所以,它会截获所有窗体的消息,因此,在程序中就要对消息和窗体进行判断,对于不是我们所要的,就放行,是我们要的,就处理。
通过该问题的分析,我们不难发现,可以使用类似的方法实现某些程序的最小化时隐藏等功能,即当收到特定窗口的最小化消息时,向它发送WM_SHOWWINDOW消息,然后在通知区生成一个对应图标,如果用户点击图标则显示窗口。