From c658b437b58bdb21faa22a937b2f81959e331ee6 Mon Sep 17 00:00:00 2001 From: Yaakov Date: Sat, 23 May 2020 19:05:09 +1000 Subject: [PATCH] Remove statics and threadstatic so that we can pass options around --- Notepad.Extensions.Logging/IWindowFinder.cs | 7 ++ Notepad.Extensions.Logging/NativeMethods.cs | 4 +- Notepad.Extensions.Logging/NotepadLogger.cs | 17 ++-- .../NotepadLoggerProvider.cs | 4 +- .../WindowEnumerationState.cs | 72 ++++++++++++++++ .../WindowEnumerationStatePoolingPolicy.cs | 17 ++++ Notepad.Extensions.Logging/WindowFinder.cs | 84 ++++--------------- Notepad.Extensions.Logging/WindowInfo.cs | 16 ++++ Notepad.Extensions.Logging/WindowKind.cs | 2 +- 9 files changed, 143 insertions(+), 80 deletions(-) create mode 100644 Notepad.Extensions.Logging/IWindowFinder.cs create mode 100644 Notepad.Extensions.Logging/WindowEnumerationState.cs create mode 100644 Notepad.Extensions.Logging/WindowEnumerationStatePoolingPolicy.cs create mode 100644 Notepad.Extensions.Logging/WindowInfo.cs diff --git a/Notepad.Extensions.Logging/IWindowFinder.cs b/Notepad.Extensions.Logging/IWindowFinder.cs new file mode 100644 index 0000000..b8d446a --- /dev/null +++ b/Notepad.Extensions.Logging/IWindowFinder.cs @@ -0,0 +1,7 @@ +namespace Notepad.Extensions.Logging +{ + interface IWindowFinder + { + WindowInfo FindNotepadWindow(); + } +} \ No newline at end of file diff --git a/Notepad.Extensions.Logging/NativeMethods.cs b/Notepad.Extensions.Logging/NativeMethods.cs index 873b5de..af5abb8 100644 --- a/Notepad.Extensions.Logging/NativeMethods.cs +++ b/Notepad.Extensions.Logging/NativeMethods.cs @@ -22,10 +22,10 @@ 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, IntPtr lParam); + public delegate bool EnumWindowsDelegate(IntPtr hWnd, object lParam); [DllImport("user32.dll")] - public static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lParam); + public static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, object lParam); [DllImport("User32.dll")] public static extern int GetWindowText(IntPtr hWndParent, StringBuilder sb, int maxCount); diff --git a/Notepad.Extensions.Logging/NotepadLogger.cs b/Notepad.Extensions.Logging/NotepadLogger.cs index 7679e7b..3eab97f 100644 --- a/Notepad.Extensions.Logging/NotepadLogger.cs +++ b/Notepad.Extensions.Logging/NotepadLogger.cs @@ -1,6 +1,5 @@ using System; using System.Buffers; -using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; @@ -12,13 +11,15 @@ namespace Notepad.Extensions.Logging { class NotepadLogger : ILogger { - public NotepadLogger(ObjectPool stringBuilderPool, string categoryName) + public NotepadLogger(ObjectPool stringBuilderPool, IWindowFinder windowFinder, string categoryName) { - this.stringBuilderPool = stringBuilderPool; + this.stringBuilderPool = stringBuilderPool ?? throw new ArgumentNullException(nameof(stringBuilderPool)); + this.windowFinder = windowFinder ?? throw new ArgumentNullException(nameof(windowFinder)); this.categoryName = categoryName; } readonly ObjectPool stringBuilderPool; + readonly IWindowFinder windowFinder; readonly string categoryName; public IDisposable BeginScope(TState state) => NullDisposable.Instance; @@ -87,18 +88,18 @@ namespace Notepad.Extensions.Logging _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), }; - static void WriteToNotepad(string message) + void WriteToNotepad(string message) { - var (kind, hwnd) = WindowFinder.FindNotepadWindow(); - switch (kind) + var info = windowFinder.FindNotepadWindow(); + switch (info.Kind) { case WindowKind.Notepad: - SendMessage(hwnd, EM_REPLACESEL, (IntPtr)1, message); + SendMessage(info.Handle, EM_REPLACESEL, (IntPtr)1, message); break; case WindowKind.NotepadPlusPlus: { - WriteToNotepadPlusPlus(hwnd, message); + WriteToNotepadPlusPlus(info.Handle, message); break; } } diff --git a/Notepad.Extensions.Logging/NotepadLoggerProvider.cs b/Notepad.Extensions.Logging/NotepadLoggerProvider.cs index 601cb8a..5c479b8 100644 --- a/Notepad.Extensions.Logging/NotepadLoggerProvider.cs +++ b/Notepad.Extensions.Logging/NotepadLoggerProvider.cs @@ -10,13 +10,15 @@ namespace Notepad.Extensions.Logging { var poolProvider = new DefaultObjectPoolProvider(); stringBuilderPool = poolProvider.CreateStringBuilderPool(); + windowFinder = new WindowFinder(); } public static ILoggerProvider Instance { get; } = new NotepadLoggerProvider(); readonly ObjectPool stringBuilderPool; + readonly IWindowFinder windowFinder; - public ILogger CreateLogger(string categoryName) => new NotepadLogger(stringBuilderPool, categoryName); + public ILogger CreateLogger(string categoryName) => new NotepadLogger(stringBuilderPool, windowFinder, categoryName); public void Dispose() { diff --git a/Notepad.Extensions.Logging/WindowEnumerationState.cs b/Notepad.Extensions.Logging/WindowEnumerationState.cs new file mode 100644 index 0000000..2d13e75 --- /dev/null +++ b/Notepad.Extensions.Logging/WindowEnumerationState.cs @@ -0,0 +1,72 @@ +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 void Reset() + { + Handle = default; + WindowKind = 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 (IsKnownNotepadWindow(sb.ToString())) + { + return false; + } + return true; + } + + static Regex notepadPlusPlusRegex = new Regex(@"^new \d+ - Notepad\+\+$", RegexOptions.Compiled); + + 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/WindowEnumerationStatePoolingPolicy.cs b/Notepad.Extensions.Logging/WindowEnumerationStatePoolingPolicy.cs new file mode 100644 index 0000000..7056cab --- /dev/null +++ b/Notepad.Extensions.Logging/WindowEnumerationStatePoolingPolicy.cs @@ -0,0 +1,17 @@ +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 b716ec9..ce56966 100644 --- a/Notepad.Extensions.Logging/WindowFinder.cs +++ b/Notepad.Extensions.Logging/WindowFinder.cs @@ -1,89 +1,37 @@ using System; -using System.ComponentModel; -using System.Text; -using System.Text.RegularExpressions; +using Microsoft.Extensions.ObjectPool; namespace Notepad.Extensions.Logging { - static class WindowFinder + class WindowFinder : IWindowFinder { - public static (WindowKind kind, IntPtr hwnd) FindNotepadWindow() + public WindowFinder() { - sb ??= new StringBuilder(4096); + statePool = new DefaultObjectPool(WindowEnumerationStatePoolingPolicy.Instance); + } + readonly ObjectPool statePool; + + public WindowInfo FindNotepadWindow() + { + var stateObject = statePool.Get(); try { - FindMainWindow(); - return (windowKind, handle); + NativeMethods.EnumWindows(enumWindowsDelegate, stateObject); + return new WindowInfo(stateObject.WindowKind, stateObject.Handle); } finally { - handle = IntPtr.Zero; - sb.Clear(); - windowKind = WindowKind.Invalid; + statePool.Return(stateObject); } } - static IntPtr FindMainWindow() - { - NativeMethods.EnumWindows(enumWindowsDelegate, IntPtr.Zero); - return handle; - } - static NativeMethods.EnumWindowsDelegate enumWindowsDelegate = new NativeMethods.EnumWindowsDelegate(EnumWindowsCallback); - static bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam) + static bool EnumWindowsCallback(IntPtr hWnd, object lParam) { - 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; + var state = (WindowEnumerationState)lParam; + return state.ExamineWindow(hWnd); } } } diff --git a/Notepad.Extensions.Logging/WindowInfo.cs b/Notepad.Extensions.Logging/WindowInfo.cs new file mode 100644 index 0000000..6629774 --- /dev/null +++ b/Notepad.Extensions.Logging/WindowInfo.cs @@ -0,0 +1,16 @@ +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 d79e383..cd8c57d 100644 --- a/Notepad.Extensions.Logging/WindowKind.cs +++ b/Notepad.Extensions.Logging/WindowKind.cs @@ -1,6 +1,6 @@ namespace Notepad.Extensions.Logging { - public enum WindowKind + enum WindowKind { Invalid, Notepad,