Making sure the app can run well without a console window

Only applicable when a PID is specified.
This commit is contained in:
Den Delimarsky 2021-05-11 20:50:02 -07:00
parent 95bac54c0e
commit ff486e5a61
No known key found for this signature in database
GPG Key ID: E1BE1355085F0BCF
4 changed files with 66 additions and 19 deletions

View File

@ -3,12 +3,15 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Win32; using Microsoft.Win32;
using NLog; using NLog;
#pragma warning disable SA1116 // Split parameters should start on line after declaration
namespace Espresso.Shell.Core namespace Espresso.Shell.Core
{ {
[Flags] [Flags]
@ -27,6 +30,11 @@ namespace Espresso.Shell.Core
public class APIHelper public class APIHelper
{ {
private const string BuildRegistryLocation = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; private const string BuildRegistryLocation = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
public const int StdOutputHandle = -11;
public const int StdInputHandle = -10;
public const int StdErrorHandle = -12;
public const uint GenericWrite = 0x40000000;
public const uint GenericRead = 0x80000000;
private static readonly Logger _log; private static readonly Logger _log;
private static CancellationTokenSource _tokenSource; private static CancellationTokenSource _tokenSource;
@ -36,12 +44,45 @@ namespace Espresso.Shell.Core
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags); private static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] uint access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
static APIHelper() static APIHelper()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_tokenSource = new CancellationTokenSource(); _tokenSource = new CancellationTokenSource();
} }
public static void AllocateConsole()
{
AllocConsole();
var outputHandle = GetStdHandle(StdOutputHandle);
var outputFilePointer = CreateFile("CONOUT$", GenericRead | GenericWrite, FileShare.Write, IntPtr.Zero, FileMode.OpenOrCreate, 0, IntPtr.Zero);
if (outputFilePointer != outputHandle)
{
SetStdHandle(StdOutputHandle, outputFilePointer);
Console.SetOut(new StreamWriter(Console.OpenStandardOutput(), Console.OutputEncoding) { AutoFlush = true });
}
}
/// <summary> /// <summary>
/// Sets the computer awake state using the native Win32 SetThreadExecutionState API. This /// Sets the computer awake state using the native Win32 SetThreadExecutionState API. This
/// function is just a nice-to-have wrapper that helps avoid tracking the success or failure of /// function is just a nice-to-have wrapper that helps avoid tracking the success or failure of

View File

@ -63,45 +63,45 @@ namespace Espresso.Shell.Core
}; };
} }
private static Action KeepDisplayOnCallback(string text) private static Action KeepDisplayOnCallback(string moduleName)
{ {
return () => return () =>
{ {
// Just changing the display mode. // Just changing the display mode.
var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(text); var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(moduleName);
currentSettings.Properties.KeepDisplayOn.Value = !currentSettings.Properties.KeepDisplayOn.Value; currentSettings.Properties.KeepDisplayOn.Value = !currentSettings.Properties.KeepDisplayOn.Value;
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), text); ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
}; };
} }
private static Action<int, int> TimedKeepAwakeCallback(string text) private static Action<int, int> TimedKeepAwakeCallback(string moduleName)
{ {
return (hours, minutes) => return (hours, minutes) =>
{ {
// Set timed keep awake. // Set timed keep awake.
var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(text); var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(moduleName);
currentSettings.Properties.Mode = EspressoMode.TIMED; currentSettings.Properties.Mode = EspressoMode.TIMED;
currentSettings.Properties.Hours.Value = hours; currentSettings.Properties.Hours.Value = hours;
currentSettings.Properties.Minutes.Value = minutes; currentSettings.Properties.Minutes.Value = minutes;
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), text); ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
}; };
} }
private static Action IndefiniteKeepAwakeCallback(string text) private static Action IndefiniteKeepAwakeCallback(string moduleName)
{ {
return () => return () =>
{ {
// Set indefinite keep awake. // Set indefinite keep awake.
var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(text); var currentSettings = ModuleSettings.GetSettings<EspressoSettings>(moduleName);
currentSettings.Properties.Mode = EspressoMode.INDEFINITE; currentSettings.Properties.Mode = EspressoMode.INDEFINITE;
ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), text); ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName);
}; };
} }
internal static void SetTray(string text, bool keepDisplayOn, EspressoMode mode, Action indefiniteKeepAwakeCallback, Action<int, int> timedKeepAwakeCallback, Action keepDisplayOnCallback, Action exitCallback) public static void SetTray(string text, bool keepDisplayOn, EspressoMode mode, Action indefiniteKeepAwakeCallback, Action<int, int> timedKeepAwakeCallback, Action keepDisplayOnCallback, Action exitCallback)
{ {
var contextMenuStrip = new ContextMenuStrip(); var contextMenuStrip = new ContextMenuStrip();

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Version.props" /> <Import Project="..\..\..\Version.props" />
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\Espresso</OutputPath> <OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\Espresso</OutputPath>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>

View File

@ -55,10 +55,10 @@ namespace Espresso.Shell
var configOption = new Option<bool>( var configOption = new Option<bool>(
aliases: new[] { "--use-pt-config", "-c" }, aliases: new[] { "--use-pt-config", "-c" },
getDefaultValue: () => true, getDefaultValue: () => false,
description: "Specifies whether Espresso will be using the PowerToys configuration file for managing the state.") description: "Specifies whether Espresso will be using the PowerToys configuration file for managing the state.")
{ {
Argument = new Argument<bool>(() => true) Argument = new Argument<bool>(() => false)
{ {
Arity = ArgumentArity.ZeroOrOne, Arity = ArgumentArity.ZeroOrOne,
}, },
@ -130,17 +130,22 @@ namespace Espresso.Shell
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, long timeLimit, int pid) private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, long timeLimit, int pid)
{ {
if (pid == 0)
{
APIHelper.AllocateConsole();
}
_log.Info($"The value for --use-pt-config is: {usePtConfig}"); _log.Info($"The value for --use-pt-config is: {usePtConfig}");
_log.Info($"The value for --display-on is: {displayOn}"); _log.Info($"The value for --display-on is: {displayOn}");
_log.Info($"The value for --time-limit is: {timeLimit}"); _log.Info($"The value for --time-limit is: {timeLimit}");
_log.Info($"The value for --pid is: {pid}"); _log.Info($"The value for --pid is: {pid}");
#pragma warning disable CS8604 // Possible null reference argument.
TrayHelper.InitializeTray(AppName, new Icon(Application.GetResourceStream(new Uri("/Images/Espresso.ico", UriKind.Relative)).Stream));
#pragma warning restore CS8604 // Possible null reference argument.
if (usePtConfig) if (usePtConfig)
{ {
#pragma warning disable CS8604 // Possible null reference argument.
TrayHelper.InitializeTray(AppName, new Icon(Application.GetResourceStream(new Uri("/Images/Espresso.ico", UriKind.Relative)).Stream));
#pragma warning restore CS8604 // Possible null reference argument.
// Configuration file is used, therefore we disregard any other command-line parameter // Configuration file is used, therefore we disregard any other command-line parameter
// and instead watch for changes in the file. // and instead watch for changes in the file.
try try
@ -177,13 +182,14 @@ namespace Espresso.Shell
} }
else else
{ {
if (timeLimit <= 0) var mode = timeLimit <= 0 ? EspressoMode.INDEFINITE : EspressoMode.TIMED;
if (mode == EspressoMode.INDEFINITE)
{ {
SetupIndefiniteKeepAwake(displayOn); SetupIndefiniteKeepAwake(displayOn);
} }
else else
{ {
// Timed keep-awake.
SetupTimedKeepAwake(timeLimit, displayOn); SetupTimedKeepAwake(timeLimit, displayOn);
} }
} }