From 7817230979547952ea94fe3b098548b449f9a500 Mon Sep 17 00:00:00 2001 From: quanljh Date: Sun, 18 Feb 2024 21:07:37 +0800 Subject: [PATCH] Add timeout feature and use Quan.ControlLibrary --- App.xaml | 9 - KeepTeamsAlive.csproj | 17 -- KeepTeamsAlive.sln | 2 +- MainWindow.xaml | 15 -- MainWindow.xaml.cs | 114 ----------- src/KeepTeamsAlive/App.xaml | 14 ++ App.xaml.cs => src/KeepTeamsAlive/App.xaml.cs | 7 +- .../KeepTeamsAlive/AssemblyInfo.cs | 0 .../KeepTeamsAlive/Helpers}/NativeMethods.cs | 0 .../KeepTeamsAlive/Helpers}/NativeType.cs | 0 src/KeepTeamsAlive/KeepTeamsAlive.csproj | 34 ++++ .../Properties/Settings.Designer.cs | 26 +++ .../Properties/Settings.settings | 6 + .../Resources/Icons/KeepTeamsAlive.ico | Bin .../ViewModels/MainWindowViewModel.cs | 178 ++++++++++++++++++ .../ViewModels/ViewModelBase.cs | 17 ++ src/KeepTeamsAlive/Views/MainWindow.xaml | 93 +++++++++ src/KeepTeamsAlive/Views/MainWindow.xaml.cs | 82 ++++++++ 18 files changed, 457 insertions(+), 157 deletions(-) delete mode 100644 App.xaml delete mode 100644 KeepTeamsAlive.csproj delete mode 100644 MainWindow.xaml delete mode 100644 MainWindow.xaml.cs create mode 100644 src/KeepTeamsAlive/App.xaml rename App.xaml.cs => src/KeepTeamsAlive/App.xaml.cs (78%) rename AssemblyInfo.cs => src/KeepTeamsAlive/AssemblyInfo.cs (100%) rename {Helpers => src/KeepTeamsAlive/Helpers}/NativeMethods.cs (100%) rename {Helpers => src/KeepTeamsAlive/Helpers}/NativeType.cs (100%) create mode 100644 src/KeepTeamsAlive/KeepTeamsAlive.csproj create mode 100644 src/KeepTeamsAlive/Properties/Settings.Designer.cs create mode 100644 src/KeepTeamsAlive/Properties/Settings.settings rename KeepTeamsAlive.ico => src/KeepTeamsAlive/Resources/Icons/KeepTeamsAlive.ico (100%) create mode 100644 src/KeepTeamsAlive/ViewModels/MainWindowViewModel.cs create mode 100644 src/KeepTeamsAlive/ViewModels/ViewModelBase.cs create mode 100644 src/KeepTeamsAlive/Views/MainWindow.xaml create mode 100644 src/KeepTeamsAlive/Views/MainWindow.xaml.cs diff --git a/App.xaml b/App.xaml deleted file mode 100644 index 8310128..0000000 --- a/App.xaml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/KeepTeamsAlive.csproj b/KeepTeamsAlive.csproj deleted file mode 100644 index 817d4d2..0000000 --- a/KeepTeamsAlive.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - WinExe - net6.0-windows - enable - true - KeepTeamsAlive - Quanljh - 0.0.0.1 - KeepTeamsAlive.ico - © 2022 Quanljh - You don't need to work very hard for capitalism. - True - - - diff --git a/KeepTeamsAlive.sln b/KeepTeamsAlive.sln index 4d24fe4..3dbcac7 100644 --- a/KeepTeamsAlive.sln +++ b/KeepTeamsAlive.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32616.157 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeepTeamsAlive", "KeepTeamsAlive.csproj", "{7F1ECE3A-11BD-4175-B81C-8C08A0F37A89}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeepTeamsAlive", "src\KeepTeamsAlive\KeepTeamsAlive.csproj", "{7F1ECE3A-11BD-4175-B81C-8C08A0F37A89}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/MainWindow.xaml b/MainWindow.xaml deleted file mode 100644 index 67ac76b..0000000 --- a/MainWindow.xaml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs deleted file mode 100644 index d72e178..0000000 --- a/MainWindow.xaml.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Windows; -using System.Windows.Input; -using System.Windows.Threading; -using KeepTeamsAlive.Helpers; - -namespace KeepTeamsAlive -{ - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : Window - { - private DispatcherTimer _timer; - - private IntPtr _teamsHWND; - - private bool _isTeamsRunning; - - public MainWindow() - { - InitializeComponent(); - - var processes = Process.GetProcessesByName("Teams"); - - if (processes.Length == 0) - { - processes = Process.GetProcessesByName("ms-teams"); - - if (processes.Length == 0) - { - _isTeamsRunning = false; - MessageBox.Show("Teams is not running!", "Warning"); - Close(); - return; - } - } - - _isTeamsRunning = true; - - _teamsHWND = processes[0].MainWindowHandle; - - var windowPlacement = new WINDOWPLACEMENT - { - length = Marshal.SizeOf() - }; - if (NativeMethods.GetWindowPlacement(_teamsHWND, ref windowPlacement)) - { - if (windowPlacement.showCmd == ShowWindowCommands.ShowMinimized) - { - NativeMethods.ShowWindow(_teamsHWND, (int)ShowWindowCommands.Normal); - } - else - { - NativeMethods.ShowWindow(_teamsHWND, (int)ShowWindowCommands.ShowDefault); - } - } - - } - - private void MainWindow_OnClosed(object? sender, EventArgs e) - { - if (!_isTeamsRunning) - { - return; - } - _timer.Stop(); - NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS); - } - - private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) - { - if (!_isTeamsRunning) - { - return; - } - - - if (_teamsHWND == IntPtr.Zero) - { - Message.Text = "Starting teams..."; - Process process = new Process(); - process.StartInfo.FileName = "ms-teams.exe"; - process.StartInfo.WindowStyle = ProcessWindowStyle.Normal; - process.Start(); - Message.Text = "Close this application if you want to back to work."; - } - - NativeMethods.SetForegroundWindow(_teamsHWND); - - _timer = new DispatcherTimer - { - Interval = TimeSpan.FromMinutes(4) - }; - - - _timer.Tick += (o, args) => - { - NativeMethods.SendKeyboardInput(_teamsHWND, Key.F15); - - // Set new state to prevent system sleep - var fPreviousExecutionState = NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS | NativeMethods.ES_SYSTEM_REQUIRED | NativeMethods.ES_DISPLAY_REQUIRED); - if (fPreviousExecutionState == 0) - { - Debug.WriteLine("SetThreadExecutionState failed. Do something here..."); - } - }; - - _timer.Start(); - } - } -} diff --git a/src/KeepTeamsAlive/App.xaml b/src/KeepTeamsAlive/App.xaml new file mode 100644 index 0000000..340ecd0 --- /dev/null +++ b/src/KeepTeamsAlive/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/App.xaml.cs b/src/KeepTeamsAlive/App.xaml.cs similarity index 78% rename from App.xaml.cs rename to src/KeepTeamsAlive/App.xaml.cs index 96ba80f..642665e 100644 --- a/App.xaml.cs +++ b/src/KeepTeamsAlive/App.xaml.cs @@ -1,8 +1,10 @@ using System.Diagnostics; using System.Reflection; using System.Windows; +using Reactive.Bindings; +using Reactive.Bindings.Schedulers; -namespace MessAround +namespace KeepTeamsAlive { /// /// Interaction logic for App.xaml @@ -19,7 +21,10 @@ protected override void OnStartup(StartupEventArgs e) Current.Shutdown(EXIT_CODE_ALREADY_RUNNING); return; } + base.OnStartup(e); + + ReactivePropertyScheduler.SetDefault(new ReactivePropertyWpfScheduler(Dispatcher)); } } } diff --git a/AssemblyInfo.cs b/src/KeepTeamsAlive/AssemblyInfo.cs similarity index 100% rename from AssemblyInfo.cs rename to src/KeepTeamsAlive/AssemblyInfo.cs diff --git a/Helpers/NativeMethods.cs b/src/KeepTeamsAlive/Helpers/NativeMethods.cs similarity index 100% rename from Helpers/NativeMethods.cs rename to src/KeepTeamsAlive/Helpers/NativeMethods.cs diff --git a/Helpers/NativeType.cs b/src/KeepTeamsAlive/Helpers/NativeType.cs similarity index 100% rename from Helpers/NativeType.cs rename to src/KeepTeamsAlive/Helpers/NativeType.cs diff --git a/src/KeepTeamsAlive/KeepTeamsAlive.csproj b/src/KeepTeamsAlive/KeepTeamsAlive.csproj new file mode 100644 index 0000000..9627214 --- /dev/null +++ b/src/KeepTeamsAlive/KeepTeamsAlive.csproj @@ -0,0 +1,34 @@ + + + + WinExe + net6.0-windows;net8.0-windows + true + KeepTeamsAlive + Quanljh + 0.0.0.1 + Resources\Icons\KeepTeamsAlive.ico + © 2022 Quanljh + You don't need to work very hard for capitalism. + True + + + + + + + + + + + + + + + + + + + + + diff --git a/src/KeepTeamsAlive/Properties/Settings.Designer.cs b/src/KeepTeamsAlive/Properties/Settings.Designer.cs new file mode 100644 index 0000000..14ef7f1 --- /dev/null +++ b/src/KeepTeamsAlive/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace KeepTeamsAlive.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.4.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/src/KeepTeamsAlive/Properties/Settings.settings b/src/KeepTeamsAlive/Properties/Settings.settings new file mode 100644 index 0000000..049245f --- /dev/null +++ b/src/KeepTeamsAlive/Properties/Settings.settings @@ -0,0 +1,6 @@ + + + + + + diff --git a/KeepTeamsAlive.ico b/src/KeepTeamsAlive/Resources/Icons/KeepTeamsAlive.ico similarity index 100% rename from KeepTeamsAlive.ico rename to src/KeepTeamsAlive/Resources/Icons/KeepTeamsAlive.ico diff --git a/src/KeepTeamsAlive/ViewModels/MainWindowViewModel.cs b/src/KeepTeamsAlive/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..8487137 --- /dev/null +++ b/src/KeepTeamsAlive/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,178 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Windows.Input; +using KeepTeamsAlive.Helpers; +using Reactive.Bindings; +using Reactive.Bindings.Extensions; + +namespace KeepTeamsAlive.ViewModels; + +public class MainWindowViewModel : ViewModelBase +{ + public ReactivePropertySlim SelectedTime { get; set; } + + public ReactivePropertySlim CountDownTimeText { get; set; } + + public ReactivePropertySlim CountDownTimeTextVisible { get; set; } + + public ReactivePropertySlim Message { get; set; } + + public ReactiveCommandSlim StartCountDownCommand { get; set; } + + public ReactiveCommandSlim StopCountDownCommand { get; set; } + + public ReactiveCommandSlim LoadWindowCommand { get; set; } + + public ReactiveCommandSlim CloseWindowCommand { get; set; } + + public Action ShowMessageBox { get; set; } + + public Action CloseWindowAction { get; set; } + + public Action CountTimerControl { get; set; } + + public Action TimerControl { get; set; } + + public Action CloseWindow { get; set; } + + private IntPtr _teamsHwnd; + + private bool _isTeamsRunning; + + private Process[] _processes { get; set; } + + + public MainWindowViewModel() + { + InitializeReactiveProperties(); + + InitializeReactiveCommands(); + + } + + private void InitializeReactiveProperties() + { + SelectedTime = new ReactivePropertySlim(DateTime.Now.AddHours(1)).AddTo(Disposables); + CountDownTimeText = new ReactivePropertySlim().AddTo(Disposables); + CountDownTimeTextVisible = new ReactivePropertySlim(false).AddTo(Disposables); + Message = new ReactivePropertySlim("Close this application if you want to back to work.").AddTo(Disposables); + } + + private void InitializeReactiveCommands() + { + StartCountDownCommand = new ReactiveCommandSlim().WithSubscribe(() => + { + if (SelectedTime.Value.TimeOfDay < DateTime.Now.TimeOfDay) + { + ShowMessageBox.Invoke("Cannot set a time before current date time", "Error"); + return; + } + CountTimerControl.Invoke(true); + CountDownTimeText.Value = (SelectedTime.Value.TimeOfDay - DateTime.Now.TimeOfDay).ToString(@"hh\:mm\:ss"); + CountDownTimeTextVisible.Value = true; + }).AddTo(Disposables); + + StopCountDownCommand = new ReactiveCommandSlim().WithSubscribe(() => + { + CountTimerControl.Invoke(false); + CountDownTimeTextVisible.Value = false; + }); + + LoadWindowCommand = new ReactiveCommandSlim().WithSubscribe(() => + { + _processes = Process.GetProcessesByName("Teams"); + + if (_processes.Length == 0) + { + _processes = Process.GetProcessesByName("ms-teams"); + + if (_processes.Length == 0) + { + _isTeamsRunning = false; + ShowMessageBox.Invoke("Teams is not running!", "Warning"); + CloseWindow.Invoke(); + return; + } + } + + _isTeamsRunning = true; + + _teamsHwnd = _processes[0].MainWindowHandle; + + var windowPlacement = new WINDOWPLACEMENT + { + length = Marshal.SizeOf() + }; + if (NativeMethods.GetWindowPlacement(_teamsHwnd, ref windowPlacement)) + { + if (windowPlacement.showCmd == ShowWindowCommands.ShowMinimized) + { + NativeMethods.ShowWindow(_teamsHwnd, (int)ShowWindowCommands.Normal); + } + else + { + NativeMethods.ShowWindow(_teamsHwnd, (int)ShowWindowCommands.ShowDefault); + } + } + + if (_teamsHwnd == IntPtr.Zero) + { + Message.Value = "Starting teams..."; + Process process = new Process(); + process.StartInfo.FileName = "ms-teams.exe"; + process.StartInfo.WindowStyle = ProcessWindowStyle.Normal; + process.Start(); + Message.Value = "Close this application if you want to back to work."; + } + + NativeMethods.SetForegroundWindow(_teamsHwnd); + + + //TimerControl.Invoke(true); + }).AddTo(Disposables); + + CloseWindowCommand = new ReactiveCommandSlim().WithSubscribe(() => + { + if (!_isTeamsRunning) + { + return; + } + + TimerControl.Invoke(false); + NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS); + + Disposables.Dispose(); + }).AddTo(Disposables); + } + + public void CountTimerEventHandler() + { + var timeSpan = (SelectedTime.Value.TimeOfDay - DateTime.Now.TimeOfDay); + if (timeSpan <= TimeSpan.Zero) + { + if (_processes.Length > 0) + { + foreach (var process in _processes) + { + process.Kill(true); + } + } + CloseWindow.Invoke(); + } + CountDownTimeText.Value = timeSpan.ToString(@"hh\:mm\:ss"); + + } + + public void TimerEventHandler() + { + NativeMethods.SendKeyboardInput(_teamsHwnd, Key.F15); + + // Set new state to prevent system sleep + var fPreviousExecutionState = NativeMethods.SetThreadExecutionState(NativeMethods.ES_CONTINUOUS | NativeMethods.ES_SYSTEM_REQUIRED | NativeMethods.ES_DISPLAY_REQUIRED); + if (fPreviousExecutionState == 0) + { + Debug.WriteLine("SetThreadExecutionState failed. Do something here..."); + } + } +} \ No newline at end of file diff --git a/src/KeepTeamsAlive/ViewModels/ViewModelBase.cs b/src/KeepTeamsAlive/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..9c415ad --- /dev/null +++ b/src/KeepTeamsAlive/ViewModels/ViewModelBase.cs @@ -0,0 +1,17 @@ +using System; +using System.ComponentModel; +using Reactive.Bindings.Disposables; + +namespace KeepTeamsAlive.ViewModels; + +public class ViewModelBase : INotifyPropertyChanged, IDisposable +{ + protected CompositeDisposable Disposables = new(); + + public event PropertyChangedEventHandler PropertyChanged; + + public void Dispose() + { + Disposables.Dispose(); + } +} \ No newline at end of file diff --git a/src/KeepTeamsAlive/Views/MainWindow.xaml b/src/KeepTeamsAlive/Views/MainWindow.xaml new file mode 100644 index 0000000..6edc299 --- /dev/null +++ b/src/KeepTeamsAlive/Views/MainWindow.xaml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/KeepTeamsAlive/Views/MainWindow.xaml.cs b/src/KeepTeamsAlive/Views/MainWindow.xaml.cs new file mode 100644 index 0000000..595c169 --- /dev/null +++ b/src/KeepTeamsAlive/Views/MainWindow.xaml.cs @@ -0,0 +1,82 @@ +using System; +using System.Windows; +using System.Windows.Threading; +using KeepTeamsAlive.ViewModels; +using Quan.ControlLibrary.Controls; + +namespace KeepTeamsAlive.Views +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : QuanWindow + { + + private DispatcherTimer _countTimer; + + private DispatcherTimer _timer; + + + public MainWindow() + { + InitializeComponent(); + + + var vm = new MainWindowViewModel(); + vm.ShowMessageBox = ShowMessageBox; + vm.CountTimerControl = CountTimerControl; + vm.TimerControl = TimerControl; + vm.CloseWindow = CloseWindow; + DataContext = vm; + + _countTimer = new DispatcherTimer() + { + Interval = TimeSpan.FromSeconds(1) + }; + + _countTimer.Tick += (sender, args) => vm.CountTimerEventHandler(); + + _timer = new DispatcherTimer + { + Interval = TimeSpan.FromMinutes(4) + }; + + _timer.Tick += (sender, args) => vm.TimerEventHandler(); + + } + + private void CloseWindow() + { + Dispatcher.InvokeShutdown(); + } + + private void TimerControl(bool start) + { + if (start) + { + _timer.Start(); + } + else + { + _timer.Stop(); + } + } + + private void CountTimerControl(bool start) + { + if (start) + { + _countTimer.Start(); + } + else + { + _countTimer.Stop(); + } + } + + private void ShowMessageBox(string text, string caption) + { + MessageBox.Show(text, caption); + } + } +}