M src/Local/NosSmooth.Comms.Inject/DllMain.cs => src/Local/NosSmooth.Comms.Inject/DllMain.cs +21 -9
@@ 26,14 26,10 @@ namespace NosSmooth.Comms.Inject;
/// </summary>
public class DllMain
{
- private static IHost? _host;
+ private const uint StdOutputHandle = 0xFFFFFFF5;
- /// <summary>
- /// Allocate console.
- /// </summary>
- /// <returns>Whether the operation was successful.</returns>
- [DllImport("kernel32")]
- public static extern bool AllocConsole();
+ private static bool _consoleAllocated;
+ private static IHost? _host;
/// <summary>
/// Enable named pipes server.
@@ 46,14 42,30 @@ public class DllMain
host =>
{
var manager = host.Services.GetRequiredService<ServerManager>();
- return manager.RunManagerAsync(host.Services.GetRequiredService<IHostApplicationLifetime>().ApplicationStopping);
+ return manager.RunManagerAsync
+ (host.Services.GetRequiredService<IHostApplicationLifetime>().ApplicationStopping);
}
);
}
+ /// <summary>
+ /// Open a console.
+ /// </summary>
+ public static void OpenConsole()
+ {
+ WinConsole.Initialize(false);
+ }
+
+ /// <summary>
+ /// Close a console.
+ /// </summary>
+ public static void CloseConsole()
+ {
+ WinConsole.Close();
+ }
+
private static void Main(Func<IHost, Task<Result>> host)
{
- AllocConsole();
new Thread
(
() =>
A src/Local/NosSmooth.Comms.Inject/MessageResponders/ConsoleResponder.cs => src/Local/NosSmooth.Comms.Inject/MessageResponders/ConsoleResponder.cs +30 -0
@@ 0,0 1,30 @@
+//
+// ConsoleResponder.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using NosSmooth.Comms.Data.Responders;
+using NosSmooth.Comms.Inject.Messages;
+using Remora.Results;
+
+namespace NosSmooth.Comms.Inject.MessageResponders;
+
+/// <inheritdoc />
+public class ConsoleResponder : IMessageResponder<ConsoleMessage>
+{
+ /// <inheritdoc />
+ public Task<Result> Respond(ConsoleMessage message, CancellationToken ct = default)
+ {
+ if (message.Open)
+ {
+ WinConsole.Initialize(false);
+ }
+ else
+ {
+ WinConsole.Close();
+ }
+
+ return Task.FromResult(Result.FromSuccess());
+ }
+}<
\ No newline at end of file
A src/Local/NosSmooth.Comms.Inject/Messages/ConsoleMessage.cs => src/Local/NosSmooth.Comms.Inject/Messages/ConsoleMessage.cs +9 -0
@@ 0,0 1,9 @@
+//
+// ConsoleMessage.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace NosSmooth.Comms.Inject.Messages;
+
+public record ConsoleMessage(bool Open);<
\ No newline at end of file
A src/Local/NosSmooth.Comms.Inject/WinConsole.cs => src/Local/NosSmooth.Comms.Inject/WinConsole.cs +152 -0
@@ 0,0 1,152 @@
+//
+// WinConsole.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Runtime.InteropServices;
+using Microsoft.Win32.SafeHandles;
+
+namespace NosSmooth.Comms.Inject;
+
+/// <summary>
+/// A class for managing Windows console.
+/// </summary>
+internal static class WinConsole
+{
+ /// <summary>
+ /// Initialize a console.
+ /// </summary>
+ /// <param name="alwaysCreateNewConsole">Whether to create (true) or attach (false) to a console.</param>
+ public static void Initialize(bool alwaysCreateNewConsole = true)
+ {
+#if WINDOWS
+ bool consoleAttached = true;
+ if (alwaysCreateNewConsole
+ || (AttachConsole(ATTACH_PARRENT) == 0
+ && Marshal.GetLastWin32Error() != ERROR_ACCESS_DENIED))
+ {
+ consoleAttached = AllocConsole() != 0;
+ }
+
+ if (consoleAttached)
+ {
+ InitializeOutStream();
+ InitializeInStream();
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Close a console.
+ /// </summary>
+ public static void Close()
+ {
+#if WINDOWS
+ FreeConsole();
+#endif
+ }
+#if WINDOWS
+ private static void InitializeOutStream()
+ {
+ var fs = CreateFileStream("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, FileAccess.Write);
+ if (fs != null)
+ {
+ var writer = new StreamWriter(fs) { AutoFlush = true };
+ Console.SetOut(writer);
+ Console.SetError(writer);
+ }
+ }
+
+ private static void InitializeInStream()
+ {
+ var fs = CreateFileStream("CONIN$", GENERIC_READ, FILE_SHARE_READ, FileAccess.Read);
+ if (fs != null)
+ {
+ Console.SetIn(new StreamReader(fs));
+ }
+ }
+
+ private static FileStream? CreateFileStream
+ (
+ string name,
+ uint win32DesiredAccess,
+ uint win32ShareMode,
+ FileAccess dotNetFileAccess
+ )
+ {
+ var file = new SafeFileHandle
+ (
+ CreateFileW
+ (
+ name,
+ win32DesiredAccess,
+ win32ShareMode,
+ IntPtr.Zero,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ IntPtr.Zero
+ ),
+ true
+ );
+ if (!file.IsInvalid)
+ {
+ var fs = new FileStream(file, dotNetFileAccess);
+ return fs;
+ }
+ return null;
+ }
+
+ [DllImport
+ (
+ "kernel32.dll",
+ EntryPoint = "AllocConsole",
+ SetLastError = true,
+ CharSet = CharSet.Auto,
+ CallingConvention = CallingConvention.StdCall
+ )]
+ private static extern int AllocConsole();
+
+ [DllImport
+ (
+ "kernel32.dll",
+ EntryPoint = "AttachConsole",
+ SetLastError = true,
+ CharSet = CharSet.Auto,
+ CallingConvention = CallingConvention.StdCall
+ )]
+ private static extern uint AttachConsole(uint dwProcessId);
+
+ [DllImport
+ (
+ "kernel32.dll",
+ EntryPoint = "CreateFileW",
+ SetLastError = true,
+ CharSet = CharSet.Auto,
+ CallingConvention = CallingConvention.StdCall
+ )]
+ private static extern IntPtr CreateFileW
+ (
+ string lpFileName,
+ uint dwDesiredAccess,
+ uint dwShareMode,
+ IntPtr lpSecurityAttributes,
+ uint dwCreationDisposition,
+ uint dwFlagsAndAttributes,
+ IntPtr hTemplateFile
+ );
+
+ [DllImport("kernel32")]
+ private static extern bool FreeConsole();
+#endif
+
+ private const uint GENERIC_WRITE = 0x40000000;
+ private const uint GENERIC_READ = 0x80000000;
+ private const uint FILE_SHARE_READ = 0x00000001;
+ private const uint FILE_SHARE_WRITE = 0x00000002;
+ private const uint OPEN_EXISTING = 0x00000003;
+ private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
+ private const uint ERROR_ACCESS_DENIED = 5;
+
+ private const uint ATTACH_PARRENT = 0xFFFFFFFF;
+}<
\ No newline at end of file
M src/Local/NosSmooth.Comms.Local/CommsInjector.cs => src/Local/NosSmooth.Comms.Local/CommsInjector.cs +36 -7
@@ 9,6 9,7 @@ using Microsoft.Extensions.DependencyInjection;
using NosSmooth.Comms.Core;
using NosSmooth.Comms.Core.NamedPipes;
using NosSmooth.Comms.Data;
+using NosSmooth.Comms.Inject;
using NosSmooth.Injector;
using NosSmooth.LocalBinding;
using NosSmooth.LocalBinding.Options;
@@ 124,13 125,7 @@ public class CommsInjector
public async Task<Result<Comms>> EstablishNamedPipesConnectionAsync
(Process process, CancellationToken stopToken, CancellationToken ct)
{
- var injectResult = _injector.Inject
- (
- process,
- Path.GetFullPath("NosSmooth.Comms.Inject.dll"),
- "NosSmooth.Comms.Inject.DllMain, NosSmooth.Comms.Inject",
- "EnableNamedPipes"
- );
+ var injectResult = Inject(process, nameof(DllMain.EnableNamedPipes));
if (!injectResult.IsSuccess)
{
return Result<Comms>.FromError(injectResult);
@@ 151,4 146,38 @@ public class CommsInjector
var nostaleClient = _resolver.Resolve(handler);
return new Comms(process, handler, nostaleClient);
}
+
+ /// <summary>
+ /// Open a console in the target process.
+ /// </summary>
+ /// <remarks>
+ /// Log of inject will be printed to the console.
+ /// </remarks>
+ /// <param name="process">The process.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result OpenConsole(Process process)
+ {
+ return Inject(process, nameof(DllMain.OpenConsole));
+ }
+
+ /// <summary>
+ /// Close a console in the target process.
+ /// </summary>
+ /// <param name="process">The process.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result CloseConsole(Process process)
+ {
+ return Inject(process, nameof(DllMain.CloseConsole));
+ }
+
+ private Result Inject(Process process, string method)
+ {
+ return _injector.Inject
+ (
+ process,
+ Path.GetFullPath("NosSmooth.Comms.Inject.dll"),
+ "NosSmooth.Comms.Inject.DllMain, NosSmooth.Comms.Inject",
+ method
+ );
+ }
}=
\ No newline at end of file