diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 015702f..0000000 --- a/.editorconfig +++ /dev/null @@ -1,139 +0,0 @@ -[*.cs] - -# SA1200: Using directives should be placed correctly -dotnet_diagnostic.SA1200.severity = none - -# SA1600: Elements should be documented -dotnet_diagnostic.SA1600.severity = none - -# SA1400: Access modifier should be declared -dotnet_diagnostic.SA1400.severity = none - -# SA1633: File should have header -dotnet_diagnostic.SA1633.severity = none - -# SA1101: Prefix local calls with this -dotnet_diagnostic.SA1101.severity = none - -# SA1201: Elements should appear in the correct order -dotnet_diagnostic.SA1201.severity = none - -# SA1204: Static elements should appear before instance elements -dotnet_diagnostic.SA1204.severity = none - -# SA1602: Enumeration items should be documented -dotnet_diagnostic.SA1602.severity = none - -# CA1303: Do not pass literals as localized parameters -dotnet_diagnostic.CA1303.severity = none - -# SA0001: XML comment analysis is disabled due to project configuration -dotnet_diagnostic.SA0001.severity = none - -# WTG1001: Do not use the 'private' keyword. -dotnet_diagnostic.WTG1001.severity = warning - -# WTG1002: Use the 'var' keyword instead of an explicit type where possible. -dotnet_diagnostic.WTG1002.severity = warning - -# WTG1003: Do not leave whitespace on the end of the line. -dotnet_diagnostic.WTG1003.severity = warning - -# WTG1004: Indent with tabs rather than spaces. -dotnet_diagnostic.WTG1004.severity = none - -# WTG1005: Use consistent line endings. -dotnet_diagnostic.WTG1005.severity = warning - -# WTG1006: Do not use the 'internal' keyword for non-nested type definitions. -dotnet_diagnostic.WTG1006.severity = warning - -# WTG1007: Do not compare bool to a constant value. -dotnet_diagnostic.WTG1007.severity = warning - -# WTG1008: Do not compare bool to a constant value in an expression. -dotnet_diagnostic.WTG1008.severity = warning - -# WTG1009: Using directives must be ordered by kind. -dotnet_diagnostic.WTG1009.severity = warning - -# WTG1010: Use the 'var' keyword instead of an explicit type where possible. -dotnet_diagnostic.WTG1010.severity = warning - -# WTG1011: Use the 'var' keyword once when deconstructing an object. -dotnet_diagnostic.WTG1011.severity = warning - -# WTG1012: Use the 'var' keyword instead of an explicit type where possible. -dotnet_diagnostic.WTG1012.severity = warning - -# WTG1013: Don't use tuple types in public interfaces. -dotnet_diagnostic.WTG1013.severity = warning - -# WTG1014: Don't nest conditional operators. -dotnet_diagnostic.WTG1014.severity = warning - -# WTG1015: Conditional operators should not have multiline values. -dotnet_diagnostic.WTG1015.severity = warning - -# WTG1016: Avoid the discard-coalesce-throw pattern. -dotnet_diagnostic.WTG1016.severity = warning - -# WTG1017: Don't define variables that could be confused with discards. -dotnet_diagnostic.WTG1017.severity = warning - -# WTG2001: Do not use ConfigureAwait from an async void method. -dotnet_diagnostic.WTG2001.severity = warning - -# WTG2002: Avoid conditional compilation based on DEBUG. -dotnet_diagnostic.WTG2002.severity = warning - -# WTG2003: Flags enums should specify explicit values. -dotnet_diagnostic.WTG2003.severity = warning - -# WTG2004: This project does not use Code Contracts. -dotnet_diagnostic.WTG2004.severity = warning - -# WTG2005: Use Correct Emit Overload -dotnet_diagnostic.WTG2005.severity = warning - -# WTG2006: Do not pass the Compiled option into static methods on Regex. -dotnet_diagnostic.WTG2006.severity = warning - -# WTG3001: Remove orphaned suppressions. -dotnet_diagnostic.WTG3001.severity = warning - -# WTG3002: Prefer direct member access over linq. -dotnet_diagnostic.WTG3002.severity = warning - -# WTG3003: Prefer direct member access over linq in an expression. -dotnet_diagnostic.WTG3003.severity = warning - -# WTG3004: Prefer Array.Empty() over creating a new empty array. -dotnet_diagnostic.WTG3004.severity = warning - -# WTG3005: Don't call ToString() on a string. -dotnet_diagnostic.WTG3005.severity = warning - -# WTG3006: Prefer nameof over calling ToString on an enum literal. -dotnet_diagnostic.WTG3006.severity = warning - -# WTG3007: Overrides should not simply call base. -dotnet_diagnostic.WTG3007.severity = warning - -# WTG3008: Do not compare value types with null. -dotnet_diagnostic.WTG3008.severity = warning - -# WTG3009: Prefer Task.CompletedTask when applicable. -dotnet_diagnostic.WTG3009.severity = warning - -# WTG3010: Don't await a trivially completed task. -dotnet_diagnostic.WTG3010.severity = warning - -# WTG3101: Do not nest regions. -dotnet_diagnostic.WTG3101.severity = warning - -# WTG3102: Regions should not split structures. -dotnet_diagnostic.WTG3102.severity = warning - -# WTG3103: Conditional compilation directives should not split structures. -dotnet_diagnostic.WTG3103.severity = warning diff --git a/Notepad.Extensions.Logging.FunctionalTest/Notepad.Extensions.Logging.FunctionalTest.csproj b/Notepad.Extensions.Logging.FunctionalTest/Notepad.Extensions.Logging.FunctionalTest.csproj index 3630bcf..4e854f5 100644 --- a/Notepad.Extensions.Logging.FunctionalTest/Notepad.Extensions.Logging.FunctionalTest.csproj +++ b/Notepad.Extensions.Logging.FunctionalTest/Notepad.Extensions.Logging.FunctionalTest.csproj @@ -1,4 +1,4 @@ - + Exe @@ -6,20 +6,8 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/Notepad.Extensions.Logging.FunctionalTest/Program.cs b/Notepad.Extensions.Logging.FunctionalTest/Program.cs index d59181e..b8deb88 100644 --- a/Notepad.Extensions.Logging.FunctionalTest/Program.cs +++ b/Notepad.Extensions.Logging.FunctionalTest/Program.cs @@ -6,13 +6,13 @@ namespace Notepad.Extensions.Logging.FunctionalTest { class Program { - static void Main() + static void Main(string[] args) { var sc = new ServiceCollection(); sc.AddLogging(lb => { lb.AddConsole(); - lb.AddNotepad(o => o.WindowName = "mylog - Notepad++"); + lb.AddNotepad(); }); var sp = sc.BuildServiceProvider(); @@ -29,7 +29,7 @@ namespace Notepad.Extensions.Logging.FunctionalTest { throw new InvalidOperationException("Wheeeeeeee"); } - catch (InvalidOperationException ex) + catch (Exception ex) { return ex; } diff --git a/Notepad.Extensions.Logging.sln b/Notepad.Extensions.Logging.sln index e6f47f8..8b0ca14 100644 --- a/Notepad.Extensions.Logging.sln +++ b/Notepad.Extensions.Logging.sln @@ -3,14 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Notepad.Extensions.Logging", "Notepad.Extensions.Logging\Notepad.Extensions.Logging.csproj", "{859E3EAB-04EB-42EE-86B2-A6FE90D71731}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notepad.Extensions.Logging", "Notepad.Extensions.Logging\Notepad.Extensions.Logging.csproj", "{859E3EAB-04EB-42EE-86B2-A6FE90D71731}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Notepad.Extensions.Logging.FunctionalTest", "Notepad.Extensions.Logging.FunctionalTest\Notepad.Extensions.Logging.FunctionalTest.csproj", "{82E1E9EF-9284-4E7B-B350-0318F9B7B411}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1B0EB23C-7148-453B-BC03-AD132DF7E13B}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notepad.Extensions.Logging.FunctionalTest", "Notepad.Extensions.Logging.FunctionalTest\Notepad.Extensions.Logging.FunctionalTest.csproj", "{82E1E9EF-9284-4E7B-B350-0318F9B7B411}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Notepad.Extensions.Logging/IWindowFinder.cs b/Notepad.Extensions.Logging/IWindowFinder.cs deleted file mode 100644 index 76dd275..0000000 --- a/Notepad.Extensions.Logging/IWindowFinder.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Notepad.Extensions.Logging -{ - interface IWindowFinder - { - WindowInfo FindNotepadWindow(string windowName); - } -} \ No newline at end of file diff --git a/Notepad.Extensions.Logging/LoggingBuilderExtensions.cs b/Notepad.Extensions.Logging/LoggingBuilderExtensions.cs index 2b76a45..5caaf34 100644 --- a/Notepad.Extensions.Logging/LoggingBuilderExtensions.cs +++ b/Notepad.Extensions.Logging/LoggingBuilderExtensions.cs @@ -1,8 +1,4 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Logging.Configuration; -using Notepad.Extensions.Logging; +using Notepad.Extensions.Logging; namespace Microsoft.Extensions.Logging { @@ -10,32 +6,7 @@ namespace Microsoft.Extensions.Logging { public static ILoggingBuilder AddNotepad(this ILoggingBuilder builder) { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } - - builder.AddConfiguration(); - - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); - LoggerProviderOptions.RegisterProviderOptions(builder.Services); - return builder; - } - - public static ILoggingBuilder AddNotepad(this ILoggingBuilder builder, Action configure) - { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (configure is null) - { - throw new ArgumentNullException(nameof(configure)); - } - - AddNotepad(builder); - builder.Services.Configure(configure); + builder.AddProvider(NotepadLoggerProvider.Instance); return builder; } } diff --git a/Notepad.Extensions.Logging/NativeMethods.cs b/Notepad.Extensions.Logging/NativeMethods.cs index 86de38f..873b5de 100644 --- a/Notepad.Extensions.Logging/NativeMethods.cs +++ b/Notepad.Extensions.Logging/NativeMethods.cs @@ -22,12 +22,12 @@ namespace Notepad.Extensions.Logging [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); - public delegate bool EnumWindowsDelegate(IntPtr hWnd, object lParam); + public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll")] - public static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, object lParam); + public static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lParam); - [DllImport("User32.dll", CharSet = CharSet.Unicode)] + [DllImport("User32.dll")] public static extern int GetWindowText(IntPtr hWndParent, StringBuilder sb, int maxCount); [DllImport("kernel32.dll", SetLastError = true)] diff --git a/Notepad.Extensions.Logging/Notepad.Extensions.Logging.csproj b/Notepad.Extensions.Logging/Notepad.Extensions.Logging.csproj index c6023b6..b0ec7db 100644 --- a/Notepad.Extensions.Logging/Notepad.Extensions.Logging.csproj +++ b/Notepad.Extensions.Logging/Notepad.Extensions.Logging.csproj @@ -11,29 +11,12 @@ Apache-2.0 true true - 1.0.3 + 1.0.1 - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - \ No newline at end of file diff --git a/Notepad.Extensions.Logging/NotepadLogger.cs b/Notepad.Extensions.Logging/NotepadLogger.cs index a591365..7679e7b 100644 --- a/Notepad.Extensions.Logging/NotepadLogger.cs +++ b/Notepad.Extensions.Logging/NotepadLogger.cs @@ -1,5 +1,6 @@ using System; using System.Buffers; +using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; @@ -11,18 +12,14 @@ namespace Notepad.Extensions.Logging { class NotepadLogger : ILogger { - public NotepadLogger(ObjectPool stringBuilderPool, IWindowFinder windowFinder, string categoryName, string windowName) + public NotepadLogger(ObjectPool stringBuilderPool, string categoryName) { - this.stringBuilderPool = stringBuilderPool ?? throw new ArgumentNullException(nameof(stringBuilderPool)); - this.windowFinder = windowFinder ?? throw new ArgumentNullException(nameof(windowFinder)); + this.stringBuilderPool = stringBuilderPool; this.categoryName = categoryName; - this.windowName = windowName; } readonly ObjectPool stringBuilderPool; - readonly IWindowFinder windowFinder; readonly string categoryName; - readonly string windowName; public IDisposable BeginScope(TState state) => NullDisposable.Instance; @@ -77,42 +74,46 @@ namespace Notepad.Extensions.Logging { stringBuilderPool.Return(builder); } - - static string GetLogLevelString(LogLevel logLevel) => logLevel switch - { - LogLevel.Trace => "trce", - LogLevel.Debug => "dbug", - LogLevel.Information => "info", - LogLevel.Warning => "warn", - LogLevel.Error => "fail", - LogLevel.Critical => "crit", - _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), - }; } - void WriteToNotepad(string message) + static string GetLogLevelString(LogLevel logLevel) => logLevel switch { - var info = windowFinder.FindNotepadWindow(windowName); - switch (info.Kind) + LogLevel.Trace => "trce", + LogLevel.Debug => "dbug", + LogLevel.Information => "info", + LogLevel.Warning => "warn", + LogLevel.Error => "fail", + LogLevel.Critical => "crit", + _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), + }; + + static void WriteToNotepad(string message) + { + var (kind, hwnd) = WindowFinder.FindNotepadWindow(); + switch (kind) { case WindowKind.Notepad: - SendMessage(info.Handle, EM_REPLACESEL, (IntPtr)1, message); + SendMessage(hwnd, EM_REPLACESEL, (IntPtr)1, message); break; - case WindowKind.Notepad2: + case WindowKind.NotepadPlusPlus: - WriteToNotepadPlusPlus(info.Handle, message); + { + WriteToNotepadPlusPlus(hwnd, message); break; + } } } - static void WriteToNotepadPlusPlus(IntPtr hwnd, string message) + unsafe static void WriteToNotepadPlusPlus(IntPtr hwnd, string message) { var dataLength = Encoding.UTF8.GetByteCount(message); + // // HERE BE DRAGONS // We need to copy the message into Notepad++'s memory so that it can read it. // Look away now, before its too late. - + // + /* unused thread ID */ _ = GetWindowThreadProcessId(hwnd, out var remoteProcessId); using var remoteProcess = Process.GetProcessById(remoteProcessId); var mem = VirtualAllocEx(remoteProcess.Handle, IntPtr.Zero, (IntPtr)dataLength, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); @@ -128,7 +129,7 @@ namespace Notepad.Extensions.Logging { var idx = Encoding.UTF8.GetBytes(message, 0, message.Length, data, 0); - _ = WriteProcessMemory(remoteProcess.Handle, mem, data, (IntPtr)dataLength, out var bytesWritten); + WriteProcessMemory(remoteProcess.Handle, mem, data, (IntPtr)dataLength, out var bytesWritten); SendMessage(hwnd, SCI_ADDTEXT, (IntPtr)dataLength, mem); } finally @@ -138,7 +139,7 @@ namespace Notepad.Extensions.Logging } finally { - VirtualFreeEx(remoteProcess.Handle, mem, IntPtr.Zero, MEM_RELEASE); + VirtualFreeEx(remoteProcess.Handle, IntPtr.Zero, IntPtr.Zero, MEM_RELEASE); } } } diff --git a/Notepad.Extensions.Logging/NotepadLoggerOptions.cs b/Notepad.Extensions.Logging/NotepadLoggerOptions.cs deleted file mode 100644 index b5eb08f..0000000 --- a/Notepad.Extensions.Logging/NotepadLoggerOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Microsoft.Extensions.Logging -{ - public class NotepadLoggerOptions - { - /// - /// Name of window to search. - /// - public string WindowName { get; set; } - } -} diff --git a/Notepad.Extensions.Logging/NotepadLoggerProvider.cs b/Notepad.Extensions.Logging/NotepadLoggerProvider.cs index e3b40c0..601cb8a 100644 --- a/Notepad.Extensions.Logging/NotepadLoggerProvider.cs +++ b/Notepad.Extensions.Logging/NotepadLoggerProvider.cs @@ -1,52 +1,25 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Text; +using System.Text; using Microsoft.Extensions.Logging; using Microsoft.Extensions.ObjectPool; -using Microsoft.Extensions.Options; namespace Notepad.Extensions.Logging { - [ProviderAlias("Notepad")] - [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Instantiated via DI container")] class NotepadLoggerProvider : ILoggerProvider { - public NotepadLoggerProvider(IOptionsMonitor options) - : this() - { - // Filter would be applied on LoggerFactory level - optionsReloadToken = options.OnChange(ReloadLoggerOptions); - ReloadLoggerOptions(options.CurrentValue); - } - - public NotepadLoggerProvider(NotepadLoggerOptions options) - : this() - { - this.options = options; - } - NotepadLoggerProvider() { var poolProvider = new DefaultObjectPoolProvider(); stringBuilderPool = poolProvider.CreateStringBuilderPool(); - windowFinder = new WindowFinder(); } - readonly ObjectPool stringBuilderPool; - readonly IWindowFinder windowFinder; - readonly IDisposable optionsReloadToken; - NotepadLoggerOptions options; + public static ILoggerProvider Instance { get; } = new NotepadLoggerProvider(); - public ILogger CreateLogger(string categoryName) => new NotepadLogger(stringBuilderPool, windowFinder, categoryName, options.WindowName); + readonly ObjectPool stringBuilderPool; + + public ILogger CreateLogger(string categoryName) => new NotepadLogger(stringBuilderPool, categoryName); public void Dispose() { - optionsReloadToken?.Dispose(); - } - - void ReloadLoggerOptions(NotepadLoggerOptions options) - { - this.options = options; } } } diff --git a/Notepad.Extensions.Logging/WindowEnumerationState.cs b/Notepad.Extensions.Logging/WindowEnumerationState.cs deleted file mode 100644 index 982fbf8..0000000 --- a/Notepad.Extensions.Logging/WindowEnumerationState.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.ComponentModel; -using System.Text; -using System.Text.RegularExpressions; - -namespace Notepad.Extensions.Logging -{ - class WindowEnumerationState - { - public WindowEnumerationState() - { - sb = new StringBuilder(capacity: 4096); - } - - readonly StringBuilder sb; - - public IntPtr Handle { get; private set; } - - public WindowKind WindowKind { get; private set; } - - public string WindowName { get; set; } - - public void Reset() - { - Handle = default; - WindowKind = default; - WindowName = default; - sb.Clear(); - } - - public bool ExamineWindow(IntPtr hWnd) - { - var result = NativeMethods.GetWindowText(hWnd, sb, sb.Capacity); - if (result < 0) - { - throw new Win32Exception(result); - } - - Handle = hWnd; - - if (sb.Length > 0 && sb[0] == '*') - { - // Notepad and Notepad++ both mark dirty documents by adding a leading asterisk to the window name. - sb.Remove(0, 1); - } - - if (sb.Length > 0 && sb[0] == ' ') - { - // Notepad2 added space after asterisk - sb.Remove(0, 1); - } - - if (IsKnownNotepadWindow(sb.ToString())) - { - return false; - } - - return true; - } - - static Regex notepadPlusPlusRegex = new Regex(@"^new \d+ - Notepad\+\+$", RegexOptions.Compiled); - - bool IsKnownNotepadWindow(string titleText) - { - if (!string.IsNullOrWhiteSpace(WindowName)) - { - if (WindowName.Equals(titleText, StringComparison.Ordinal)) - { - if (titleText.EndsWith(" - Notepad++", StringComparison.Ordinal)) - { - WindowKind = WindowKind.NotepadPlusPlus; - } - else if (titleText.EndsWith(" - Notepad2", StringComparison.Ordinal)) - { - WindowKind = WindowKind.Notepad2; - } - else - { - WindowKind = WindowKind.Notepad; - } - } - } - else if (titleText.Equals("Untitled - Notepad", StringComparison.Ordinal)) - { - WindowKind = WindowKind.Notepad; - } - else if (titleText.Equals("Untitled - Notepad2", StringComparison.Ordinal)) - { - WindowKind = WindowKind.Notepad2; - } - else if (notepadPlusPlusRegex.IsMatch(titleText)) - { - WindowKind = WindowKind.NotepadPlusPlus; - } - - Handle = FindInnerWindow(WindowKind); - - return WindowKind != default; - } - - IntPtr FindInnerWindow(WindowKind windowKind) - { - switch (windowKind) - { - case WindowKind.Notepad: - return NativeMethods.FindWindowEx(Handle, IntPtr.Zero, "EDIT", null); - case WindowKind.Notepad2: - case WindowKind.NotepadPlusPlus: - return NativeMethods.FindWindowEx(Handle, IntPtr.Zero, "Scintilla", null); - default: - return Handle; - } - } - } -} diff --git a/Notepad.Extensions.Logging/WindowEnumerationStatePoolingPolicy.cs b/Notepad.Extensions.Logging/WindowEnumerationStatePoolingPolicy.cs deleted file mode 100644 index 7056cab..0000000 --- a/Notepad.Extensions.Logging/WindowEnumerationStatePoolingPolicy.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.Extensions.ObjectPool; - -namespace Notepad.Extensions.Logging -{ - class WindowEnumerationStatePoolingPolicy : IPooledObjectPolicy - { - public static IPooledObjectPolicy Instance { get; } = new WindowEnumerationStatePoolingPolicy(); - - public WindowEnumerationState Create() => new WindowEnumerationState(); - - public bool Return(WindowEnumerationState obj) - { - obj.Reset(); - return true; - } - } -} diff --git a/Notepad.Extensions.Logging/WindowFinder.cs b/Notepad.Extensions.Logging/WindowFinder.cs index 54d0ff8..b716ec9 100644 --- a/Notepad.Extensions.Logging/WindowFinder.cs +++ b/Notepad.Extensions.Logging/WindowFinder.cs @@ -1,38 +1,89 @@ using System; -using Microsoft.Extensions.ObjectPool; +using System.ComponentModel; +using System.Text; +using System.Text.RegularExpressions; namespace Notepad.Extensions.Logging { - class WindowFinder : IWindowFinder + static class WindowFinder { - public WindowFinder() + public static (WindowKind kind, IntPtr hwnd) FindNotepadWindow() { - statePool = new DefaultObjectPool(WindowEnumerationStatePoolingPolicy.Instance); - } + sb ??= new StringBuilder(4096); - readonly ObjectPool statePool; - - public WindowInfo FindNotepadWindow(string windowName) - { - var stateObject = statePool.Get(); try { - stateObject.WindowName = windowName; - NativeMethods.EnumWindows(enumWindowsDelegate, stateObject); - return new WindowInfo(stateObject.WindowKind, stateObject.Handle); + FindMainWindow(); + return (windowKind, handle); } finally { - statePool.Return(stateObject); + handle = IntPtr.Zero; + sb.Clear(); + windowKind = WindowKind.Invalid; } } + static IntPtr FindMainWindow() + { + NativeMethods.EnumWindows(enumWindowsDelegate, IntPtr.Zero); + return handle; + } + static NativeMethods.EnumWindowsDelegate enumWindowsDelegate = new NativeMethods.EnumWindowsDelegate(EnumWindowsCallback); - static bool EnumWindowsCallback(IntPtr hWnd, object lParam) + static bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam) { - var state = (WindowEnumerationState)lParam; - return state.ExamineWindow(hWnd); + var result = NativeMethods.GetWindowText(hWnd, sb, sb.Capacity); + if (result < 0) + { + throw new Win32Exception(result); + } + + WindowFinder.handle = hWnd; + + if (sb.Length > 0 && sb[0] == '*') + { + // Notepad and Notepad++ both mark dirty documents by adding a leading asterisk to the window name. + sb.Remove(0, 1); + } + + if (IsKnownNotepadWindow(sb.ToString())) + { + return false; + } + return true; + } + + [ThreadStatic] + static IntPtr handle; + + [ThreadStatic] + static WindowKind windowKind; + + [ThreadStatic] + static StringBuilder sb; + + static Regex notepadPlusPlusRegex = new Regex(@"^new \d+ - Notepad\+\+$", RegexOptions.Compiled); + + static bool IsKnownNotepadWindow(string titleText) + { + switch (titleText) + { + case "Untitled - Notepad": + windowKind = WindowKind.Notepad; + handle = NativeMethods.FindWindowEx(handle, IntPtr.Zero, "EDIT", null); + return true; + } + + if (notepadPlusPlusRegex.IsMatch(titleText)) + { + windowKind = WindowKind.NotepadPlusPlus; + handle = NativeMethods.FindWindowEx(handle, IntPtr.Zero, "Scintilla", null); + return true; + } + + return false; } } } diff --git a/Notepad.Extensions.Logging/WindowInfo.cs b/Notepad.Extensions.Logging/WindowInfo.cs deleted file mode 100644 index b7081db..0000000 --- a/Notepad.Extensions.Logging/WindowInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Notepad.Extensions.Logging -{ - struct WindowInfo - { - public WindowInfo(WindowKind kind, IntPtr handle) - { - Kind = kind; - Handle = handle; - } - - public WindowKind Kind { get; } - - public IntPtr Handle { get; } - } -} \ No newline at end of file diff --git a/Notepad.Extensions.Logging/WindowKind.cs b/Notepad.Extensions.Logging/WindowKind.cs index 23fe66d..d79e383 100644 --- a/Notepad.Extensions.Logging/WindowKind.cs +++ b/Notepad.Extensions.Logging/WindowKind.cs @@ -1,10 +1,9 @@ namespace Notepad.Extensions.Logging { - enum WindowKind + public enum WindowKind { Invalid, Notepad, - NotepadPlusPlus, - Notepad2, + NotepadPlusPlus } } diff --git a/README.md b/README.md index 3e1216a..3540414 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ public void ConfigureServices(IServiceCollection services) } ``` -3. Open a new Notepad, Notepad++, or Notepad2 window. +3. Open a new Notepad window. 4. Run your application. ## Source Material