我的OCX控件是利用LIBVLC库写的,为了实现鼠标双击放大缩小功能,折腾了我一天时间。下面记录下解决经历。
1、首先,在网上能找到的方法是用钩子的方法:
钩子生成方法:
/*定义全局钩子*/
static HHOOK hHook = NULL;
/*回调接口*/
LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());//模块切换时的状态保护指向当前模块状态
LPMSG lpMsg = (LPMSG)lParam;
if ((nCode >= 0) && (PM_REMOVE == wParam) &&
AfxGetApp()->PreTranslateMessage(lpMsg))
{
lpMsg->message = WM_NULL;
lpMsg->lParam = 0L;
lpMsg->wParam = 0;
}
// 将钩子消息传递给当前钩子链中的下一个钩子
return ::CallNextHookEx(hHook, nCode, wParam, lParam);
}
接着可以在你的对话框ONCREATE内加入设置钩子的实现:
// CMainDialog message handlers
int CMainDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
hHook = ::SetWindowsHookEx(WH_GETMESSAGE, GetMessageProc,
AfxGetInstanceHandle(), GetCurrentThreadId());
return 0;
}
OK,钩子设置好了,后面重载pretranslateMessage方法:
BOOL CMainDialog::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
//MessageBox(_T("PreTranslateMessage"));
if (pMsg->message == WM_LBUTTONDBLCLK) //处理鼠标左键双击事件响应
{
CWnd *pWnd = FromHandle(pMsg->hwnd);
//MessageBox(_T("Get hook"));
if (!m_bFullScreen)
{
m_hWndParent = GetParent()->m_hWnd;
m_bFullScreen = TRUE;
}
::SendMessage(m_hWndParent, WM_LBUTTONDBLCLK, 0, 0);//在此向父窗口传递双击事件消息,交给父窗口来处理
}
return CDialog::PreTranslateMessage(pMsg);//其它消息在此不响应,交给父对话框处理
}
最后,在父窗口类里加入双击事件的响应即可。 至此,钩子生成完毕,测试时发现,当插件没有播放RTSP视频流数据时,它能正确的将消息捕获,这没什么问题。然而,当我进行了视频流播放时,发现双击事件怎么都不响应,钩子并没有捕获到此事件。
后来经过大量的GOOGLE后,才找到原因,原来我用的控件是用VLC库实现的,而VLC在播放时,它有自己的一套消息处理机制,或者直白的说就是消息被VLC给吞掉了,所以在顶层窗口上捕获不到。
GOOGLE给出的方法如下:
// First step is start a timer when you play a video,
// Second step : in the timer function i call :
EnumChildWindows(MyWindow_HWND, EnumerateVLC, NULL);
// Third step : if EnumerateVlc get some child window these window is the VlcEventWindow, and need disable it, to reach mouse events on MyWindow
BOOL CALLBACK EnumerateVLC(HWND hWndvlc, LPARAM lParam) {
EnableWindow(hWndvlc, FALSE);
// And kill timer, i only need get this handle one time.
return TRUE;
}
// When EnumerateVLC is called all mouse events are redirected to MyWindow
OK,照着前人的步骤进行,1、先新建一个定时器,作用是VLC在播放实例是需要时间的,留出些时间给它正常开始播放。2、在定时器处理函数里,用EnumChildWindows接口去找到其所有的子窗口,回调接口EnumerateVLC,禁用子窗口来响应事件,即可由本窗口进行响应了。 3、关闭定时器。
这样子修改后,发现在播放过程中,小窗口能响应我的双击事件了,哈哈。
双击处理接口也给出吧:
ULONG CMyActiveXCtrl::setVideoFullscreen(void)
{
SetFocus();
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加调度处理程序代码
if(video_mp == NULL)
return -1;
if( libvlc_media_player_has_vout(video_mp) < 1)
return -1;
if(!m_bFullScreen)
{//双击窗口时,放大到全屏
//SetFocus();
//获取控件窗口的绝对位置
GetWindowRect(m_rcCtrlRect);
::SetParent(m_MainDialog.GetSafeHwnd(),::GetDesktopWindow());//将父窗口设置成桌面窗口,即顶层显示
int cx = ::GetSystemMetrics(SM_CXFULLSCREEN);//屏幕分辨率
int cy = ::GetSystemMetrics(SM_CYFULLSCREEN);
MoveWindow(0, 0, cx, cy);
::SetWindowPos(m_MainDialog.GetSafeHwnd(),HWND_TOPMOST,0,0,cx,cy,SWP_FRAMECHANGED|SWP_DEFERERASE); //全屏
m_bFullScreen = TRUE;
}
else
{//双击全屏窗口时,缩小到原来的尺寸
::SetParent(m_MainDialog.GetSafeHwnd(),m_hWnd); //
//MoveWindow使用相对窗口坐标,所以必须先转化为相对坐标
CPoint LeftTop(m_rcCtrlRect.TopLeft());
CPoint BottomRight(m_rcCtrlRect.BottomRight());
::ScreenToClient(m_hWnd,&LeftTop);
::ScreenToClient(m_hWnd,&BottomRight);
::MoveWindow(m_hWnd,LeftTop.x,LeftTop.y,m_rcCtrlRect.Width(),m_rcCtrlRect.Height(),TRUE);
::SetWindowPos(m_MainDialog.GetSafeHwnd(),HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);//缩小到正常尺寸
m_bFullScreen = FALSE;
}
InvalidateRect(false);//刷新显示
return 0;
}