当前位置: 首页 > news >正文

具有自动恢复功能的通知栏图标控件

1 外壳Explorer重启时通知栏图标的自动恢复
相信很多Windows用户都碰到过这种情况:运行某个程序时出现意外错误,导致外壳程序Explorer.exe崩溃而发生重启(即Explorer.exe被关闭后重新运行),任务栏也在消失后重新生成,但应用程序在通知栏添加的图标消失了,虽然这些程序仍在运行,但再也无法通过通知栏图标与用户交互。为避免这种情况出现,Windows提供了相应的机制。
在安装了Internet Explorer 4.0及以上版本的Windows操作系统中,当任务栏建立后,外壳会向所有顶层的应用程序发出通知消息,该消息是外壳以字符串"TaskbarCreated"为参数向系统注册获得的,应用程序窗口接收到该消息后就应该重新添加的通知栏图标。
在Delphi中实现过程如下:
1). 定义一个整型变量MsgTaskbarRestart,用以保存任务栏重建的消息。
2). 在主程序的initialization部分或者是在OnCreate事件中以"TaskbarCreated"为参数向系统注册消息(也即是询问"TaskbarCreated"是哪条消息,因为以相同的参数注册会得到相同的消息,而"TaskbarCreated"在Windows启动的时候就已经被外壳注册)。

initialization
  MsgTaskbarRestart := RegisterWindowMessage('TaskbarCreated');

3). 重载主窗口的消息处理过程,拦截任务栏重建消息,进行重新添加图标的操作。

procedure TMainForm.WndProc(var Message: TMessage);
begin
  ……
  if Message.Msg = MsgTaskbarRestart then
  begin
    TrayIcon.Active := False;      //删除通知栏图标
    TrayIcon.Active := True;       //添加通知栏图标
  end;
  ……
  inherited WndProc(Message);
end; //end of WndProc

2 自动恢复功能的封装
由于外壳只向所有顶层的应用程序发送通知,这为封装自动恢复功能带来了一定的困难。因为通知栏图标的回调函数只能接收WM_XBUTTONDOWN、WM_XBUTTONUP等有限的几个消息,并不能接收所有的窗口消息。本节介绍的方法将使得在控件中能够接收窗口消息,从而实现自动恢复功能的封装。
解决问题的关键是SetWindowLong函数,向它传入GWL_WNDPROC参数,可以改变一个窗口的窗口过程。只需在创建控件时将应用程序窗口的窗口过程指针保存起来,并指向为控件中的某个新的窗口处理过程,在控件中就能够响应所有的窗口消息了(包括任务栏重建的消息);当控件销毁的时候再将保存的原始窗口过程指针恢复即可。实现代码如下(其中"……"的地方略去容易实现的添加、删除通知栏图标等函数及过程):

  TEoCSysTray = class(TComponent)
  Private
    ……
    FActive: boolean;
    FParentWindow: TWinControl;     //父窗口
    FNewWndProc: Pointer;     //新的父窗口过程指针
    FPrevWndProc: Pointer;     //原先的父窗口过程指针
    FTaskBarCreated: TNotifyEvent;     //任务栏重建事件
    ……
    procedure SetActive(Value: boolean);   //设置控件是否起作用
    procedure HookParentForm;     //替换父窗口的窗口过程
    procedure UnHookParentForm;     //还原父窗口的窗口过程
    procedure HookWndProc(var AMsg: TMessage);  //新的父窗口过程
  protected
    procedure DoTaskBarCreated; dynamic;   //触发任务栏重建事件
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property Active: boolean read FActive write SetActive;
    property OnTaskBarCreated: TNotifyEvent read FTaskBarCreated
      write FTaskBarCreated;

implementation

type
  THack = class(TWinControl);   //用以访问位于父窗口保护域的默认窗口处理过程

var
  MsgTaskbarCreated  : Integer;   //由系统注册的任务栏重建消息

constructor TEoCSysTray.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ……
  FActive := false;
  FNewWndProc := MakeObjectInstance(HookWndProc);//建立新的窗口过程指针
  FPrevWndProc := nil;
  if (AOwner <> nil) and (AOwner is TForm) then  //获得父窗口
    FParentWindow := TWinControl(AOwner)
  else
    FParentWindow := Application.MainForm;
  ……
end;//end of Contructor

destructor TEoCSysTray.Destroy;
begin
  ……
  FDestroying := True;
  FParentWindow := nil;
  FreeObjectInstance(FNewWndProc);
  FNewWndProc := nil;
  ……
  inherited Destroy;
end; //end of destructor

procedure TEoCSysTray.SetActive(Value: boolean);
begin
  if Value <> FActive then
  begin
    FActive := Value;
    if not (csDesigning in ComponentState) then //控件未处于设计状态
      case Value of
        True:
          begin
            ……
            HookParentForm;     //替换父窗口的窗口过程
            ……
          end;
        False:
          begin
            ……
            UnHookParentForm;     //还原父窗口的窗口过程
            ……
          end;
      end;
  end;
end; //end of procedure SetActive

procedure TEoCSysTray.HookParentForm;    //替换父窗口的窗口过程
var
  P                                     : Pointer;
begin
  if Assigned(FParentWindow) and
    not ((csDesigning in FParentWindow.ComponentState) or
    (csDestroying in FParentWindow.ComponentState) or FDestroying) then
  begin
    FParentWindow.HandleNeeded;
    P := Pointer(GetWindowLong(FParentWindow.Handle, GWL_WNDPROC));
    if (P <> FNewWndProc) then
    begin
      FPrevWndProc := P;
      SetWindowLong(FParentWindow.Handle,
        GWL_WNDPROC, LongInt(FNewWndProc));  //替换父窗口的窗口过程
    end;
  end;
end; //end of procedure HookParentForm

procedure TEoCSysTray.UnHookParentForm;   //还原父窗口的窗口过程
begin
  if Assigned(FParentWindow) then
  begin
    if Assigned(FPrevWndProc) and FParentWindow.HandleAllocated and
      (Pointer(GetWindowLong(FParentWindow.Handle, GWL_WNDPROC)) = FNewWndProc) then
      SetWindowLong(FParentWindow.Handle,
        GWL_WNDPROC, LongInt(FPrevWndProc));  //还原父窗口的窗口过程
  end;
  FPrevWndProc := nil;
end; //end of procedure UnHookParentForm

procedure TEoCSysTray.HookWndProc(var AMsg: TMessage);
begin
  if Assigned(FParentWindow) then
  begin
    with AMsg do
    begin
      if Msg = MsgTaskbarCreated then    //接收到任务栏重建消息
        DoTaskBarCreated;     //触发任务栏重建事件
      if Assigned(FPrevWndProc) then     //调用原窗口的窗口过程
        Result := CallWindowProc(FPrevWndProc, FParentWindow.Handle,
          Msg, WParam, LParam)
      else
        Result := CallWindowProc(THack(FParentWindow).DefWndProc,
          FParentWindow.Handle, Msg, WParam, LParam);
      if Msg = WM_DESTROY then     //窗口正被销毁
        UnHookParentForm;     //还原父窗口的窗口过程
    end;
  end;
end; //end of procedure HookWndProc

procedure TEoCSysTray.DoTaskBarCreated;
begin
  ……    //在这里重新添加通知栏图标
  if Assigned(FTaskBarCreated) then
    FTaskBarCreated(Self);
end; //end of procedure DoTaskBarCreated

initialization
  //注册询问任务栏重建的消息
  MsgTaskbarCreated := RegisterWindowMessage('TaskbarCreated');

end.

转载于:https://www.cnblogs.com/MaxWoods/archive/2006/04/02/364609.html

相关文章:

  • Win7编程:在按钮中加入管理员权限运行
  • 反射概念
  • Office 2010 中的数字签名
  • OpenJ_Bailian - 2995-登山(两遍最长上升子序列+枚举顶点)
  • 今天还好
  • HDU - 1261-字串数 (排列组合+大数)
  • 【CSDN博客之星评选】我为什么坚持写博客
  • php io
  • 音视频学习之 - 基础概念
  • 注册“Oracle Provider for OLE DB”和创建链接服务器
  • SpringBoot Admin 使用指南
  • HDU-6608-Fansblog(威尔逊定理+快速乘)(多校)
  • scanf函数的返回值问题
  • 图论基本模板总结
  • 线性表完整代码
  • #Java异常处理
  • 10个最佳ES6特性 ES7与ES8的特性
  • CAP理论的例子讲解
  • css选择器
  • ES6 学习笔记(一)let,const和解构赋值
  • Gradle 5.0 正式版发布
  • IDEA 插件开发入门教程
  • IDEA常用插件整理
  • Java深入 - 深入理解Java集合
  • js中的正则表达式入门
  • Linux中的硬链接与软链接
  • orm2 中文文档 3.1 模型属性
  • Python学习之路13-记分
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 分享几个不错的工具
  • 关于Java中分层中遇到的一些问题
  • 简单易用的leetcode开发测试工具(npm)
  • 说说动画卡顿的解决方案
  • 微信小程序实战练习(仿五洲到家微信版)
  • 移动端解决方案学习记录
  • 在Docker Swarm上部署Apache Storm:第1部分
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • 阿里云API、SDK和CLI应用实践方案
  • ​Python 3 新特性:类型注解
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • #define与typedef区别
  • #宝哥教你#查看jquery绑定的事件函数
  • %@ page import=%的用法
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (zt)最盛行的警世狂言(爆笑)
  • (阿里云万网)-域名注册购买实名流程
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (强烈推荐)移动端音视频从零到上手(上)
  • (区间dp) (经典例题) 石子合并
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .NET简谈设计模式之(单件模式)