TSpeedButton在DLL中无法响应CM_MOUSEENTER,CM_MOUSELEAVE事件详解
Posted on 2009-09-11 14:39 Murphy(土豆) 阅读(1042) 评论(0) 编辑 收藏 举报TSpeedButton在DLL中无法响应CM_MOUSEENTER,CM_MOUSELEAVE事件详解
使用DELPHI进行DLL开发时,如果在DLL的Form窗体内使用了TSpeedButton控件,便能发现,在运行时TSpeedButton无法响应CM_MOUSEENTER,CM_MOUSELEAVE事件!
首先,跟踪一下VCL源码,发现这两个VCL自定义消息源于TApplication.DoMouseIdle:
function TApplication.DoMouseIdle: TControl;
var
CaptureControl: TControl;
P: TPoint;
begin
GetCursorPos(P); //获取当前鼠标所在位置
Result := FindDragTarget(P, True); //获取当前位置座标的控件
CaptureControl := GetCaptureControl;
if FMouseControl <> Result then //判断以前记录的鼠标所指向控件和当前位置的控件是否相同
begin
if ((FMouseControl <> nil) and (CaptureControl = nil)) or
((CaptureControl <> nil) and (FMouseControl = CaptureControl)) then
FMouseControl.Perform(CM_MOUSELEAVE, 0, 0); //发送鼠标离开消息
FMouseControl := Result;
if ((FMouseControl <> nil) and (CaptureControl = nil)) or
((CaptureControl <> nil) and (FMouseControl = CaptureControl)) then
FMouseControl.Perform(CM_MOUSEENTER, 0, 0); //发送鼠标进入消息
end;
end;
DoMouseIdle方法完成了上述消息的发送过程,而该方法由TApplication.Idle调用,Idle是TApplication.HandleMessage的消息处理循环的一部分。最终HandleMessage消息处理循环由TApplication.Run启动;
再来看看TApplication这个类,这个最常用最基础的类在Forms单元定义,但却是在Controls单元中的InitControls方法中被创建,也就是说,只要在DLL中使用Form,TApplication就会被创建!但是这个TApplication却和主程序中的TApplication是两个东西,同时DLL中创建的TApplication对象也不会调用Run方法;
看来,只需要在DLL中构建一个消息循环处理的流程,就可以很好的解决这个问题。
在网络上常用的方法是直接将主程序的TApplication的Handle替换DLL中的Handle,虽然这样一来DLL中的消息可以响应了,但可想而知,被替换后主程序已经无法响应消息。因为主程序接收消息的句柄已经被替换成DLL中的Application.Handle了;
分析其中的原因后,得出结论是:只需要在主程序的消息循环中,加上处理上述鼠标消息的机制,就能很完美的解决上述问题;
具体的实现为:响应主程序的TApplication.OnMessage事件,在该事件中完成DLL的TApplication.DoMouseIdle处理即可,DoMouseIdle方法为私有方法,所以,在实现的示例代码中,拷贝了该方法
示例代码点击这里: 下载