Есть ли способ заставить OnReceiveLocalNotification (TNotificationCenter) работать даже после закрытия приложения? - PullRequest
0 голосов
/ 10 января 2019

Мое приложение имеет несколько задач, которые нужно запускать в фоновом режиме. Я использую планировщик задач, чтобы запустить приложение, когда задача требуется, и приложение само закрывается, когда задача завершена. Пока все хорошо.

Задания могут занять некоторое время, поэтому я отправляю уведомления в центр действий в начале и в конце задания. Для первого у меня нет проблем, но во втором уведомлении я хочу открыть файл журнала, когда пользователь щелкает по нему.

Центр действий показывает оба уведомления, но файл журнала не открывается при нажатии на уведомление. Я думаю, это потому, что приложение уже закрыто.

Пока я пробовал следующее.

uses
  System.SysUtils, System.Classes, System.Notification,
  System.Generics.Collections;

type
  TActionCenter = class(TDataModule)
    NotificationCenter: TNotificationCenter;
    procedure NotificationCenterReceiveLocalNotification(Sender: TObject;
      ANotification: TNotification);
  strict private
    FNotifications: TDictionary<string, TOnReceiveLocalNotification>;
  public
    class procedure AppRegister(const ACompanyName: string);
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    procedure SendNotification(const ATitle, AMessage: string); overload;
    procedure SendNotification(const ATitle, AMessage: string; AOnClick: TOnReceiveLocalNotification); overload;
  end;

var
  _ActionCenter: TActionCenter;

...

uses
  System.Hash, System.Win.Registry, Winapi.Windows;

procedure TActionCenter.AfterConstruction;
begin
  inherited;
  FNotifications := TDictionary<string, TOnReceiveLocalNotification>.Create;
end;

class procedure TActionCenter.AppRegister(const ACompanyName: string);
const
  ActionCenterKey = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings\';
var
  ApplicationKey: string;
  Registry: TRegistry;
begin
  ApplicationKey := ACompanyName + '.DesktopToasts.' + THashBobJenkins.GetHashString(ParamStr(0));
  Registry := TRegistry.Create;
  try
    Registry.RootKey := HKEY_CURRENT_USER;
    if not Registry.KeyExists(ActionCenterKey + ApplicationKey) then
    begin
      Registry.Access := KEY_WRITE;
      if Registry.OpenKey(ActionCenterKey + ApplicationKey, True) then
        Registry.WriteInteger('ShowInActionCenter', 1);
    end;
  finally
    Registry.Free;
  end;
end;

procedure TActionCenter.BeforeDestruction;
begin
  inherited;
  FNotifications.Free;
end;

procedure TActionCenter.NotificationCenterReceiveLocalNotification(
  Sender: TObject; ANotification: TNotification);
var
  Pair: TPair<string, TOnReceiveLocalNotification>;
begin
  Pair := FNotifications.ExtractPair(ANotification.Name);
  if Assigned(Pair.Value) then
    Pair.Value(Sender, ANotification);
end;

procedure TActionCenter.SendNotification(const ATitle, AMessage: string);
begin
  SendNotification(ATitle, AMessage, nil);
end;

procedure TActionCenter.SendNotification(const ATitle, AMessage: string;
  AOnClick: TOnReceiveLocalNotification);
var
  Notification: TNotification;
begin
  Notification := NotificationCenter.CreateNotification;
  try
    Notification.Name := TGUID.NewGuid.ToString;
    Notification.Title := ATitle;
    Notification.AlertBody := AMessage;
    FNotifications.Add(Notification.Name, AOnClick);
    NotificationCenter.PresentNotification(Notification);
  finally
    Notification.Free;
  end;
end;

initialization
  TActionCenter.AppRegister('MyCompanyName');
  _ActionCenter := TActionCenter.Create(nil);

finalization
  _ActionCenter.Free;

...

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls, ActionCenter.Classes;

type
  TMain = class(TForm)
    Send: TButton;
    procedure SendClick(Sender: TObject);
  strict private
    procedure NotificationClick1(ASender: TObject; ANotification: TNotification);
    procedure NotificationClick2(ASender: TObject; ANotification: TNotification);
    procedure NotificationClick3(ASender: TObject; ANotification: TNotification);
    procedure OpenLog(FileName: TFileName);
  end;

....

uses
  Winapi.ShellAPI;

procedure TMain.NotificationClick1(ASender: TObject; ANotification: TNotification);
begin
  OpenLog('C:\First.txt');
end;

procedure TMain.NotificationClick2(ASender: TObject; ANotification: TNotification);
begin
  OpenLog('C:\Second.txt');
end;

procedure TMain.NotificationClick3(ASender: TObject; ANotification: TNotification);
begin
  OpenLog('C:\Third.txt');
end;

procedure TMain.OpenLog(FileName: TFileName);
var
  Handle: THandle;
begin
  Handle := GetStdHandle(STD_INPUT_HANDLE);
  ShellExecute(Handle, nil, PChar(FileName), nil, nil, SW_SHOWNORMAL);
end;

Есть ли способ сделать это? Есть ли событие OnReceiveLocalNotification, которое работает даже после закрытия приложения?

1 Ответ

0 голосов
/ 30 января 2019

Я решил свою проблему, заменив TNotificationCenter и OnReceiveLocalNotification на TTrayIcon и OnBaloonClick. Похоже, что уведомление TTrayIcon поддерживает работу приложения в течение нескольких секунд. Это не идеальное решение, но пока достаточно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...