zoukankan      html  css  js  c++  java
  • 具有自动恢复功能的通知栏图标控件

    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.

  • 相关阅读:
    控制asp.net 中文本框中只能输入数字
    数据导出到Excel的方法
    NET 2.0中泛型
    DateTime类常用技巧
    CodeSmith是一个基于模板的代码生成器
    WCF学习第一天
    asp.net mvc中DropDownList,CheckBox,RadioButton
    wcf实现可靠性传输
    cookie
    asp.net mvc3 异步Controller
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/364609.html
Copyright © 2011-2022 走看看