Remove statics and threadstatic so that we can pass options around

This commit is contained in:
Yaakov 2020-05-23 19:05:09 +10:00
parent b817c75720
commit c658b437b5
9 changed files with 143 additions and 80 deletions

View file

@ -0,0 +1,7 @@
namespace Notepad.Extensions.Logging
{
interface IWindowFinder
{
WindowInfo FindNotepadWindow();
}
}

View file

@ -22,10 +22,10 @@ namespace Notepad.Extensions.Logging
[DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); 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")] [DllImport("user32.dll")]
public static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lParam); public static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, object lParam);
[DllImport("User32.dll")] [DllImport("User32.dll")]
public static extern int GetWindowText(IntPtr hWndParent, StringBuilder sb, int maxCount); public static extern int GetWindowText(IntPtr hWndParent, StringBuilder sb, int maxCount);

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -12,13 +11,15 @@ namespace Notepad.Extensions.Logging
{ {
class NotepadLogger : ILogger class NotepadLogger : ILogger
{ {
public NotepadLogger(ObjectPool<StringBuilder> stringBuilderPool, string categoryName) public NotepadLogger(ObjectPool<StringBuilder> 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; this.categoryName = categoryName;
} }
readonly ObjectPool<StringBuilder> stringBuilderPool; readonly ObjectPool<StringBuilder> stringBuilderPool;
readonly IWindowFinder windowFinder;
readonly string categoryName; readonly string categoryName;
public IDisposable BeginScope<TState>(TState state) => NullDisposable.Instance; public IDisposable BeginScope<TState>(TState state) => NullDisposable.Instance;
@ -87,18 +88,18 @@ namespace Notepad.Extensions.Logging
_ => throw new ArgumentOutOfRangeException(nameof(logLevel)), _ => throw new ArgumentOutOfRangeException(nameof(logLevel)),
}; };
static void WriteToNotepad(string message) void WriteToNotepad(string message)
{ {
var (kind, hwnd) = WindowFinder.FindNotepadWindow(); var info = windowFinder.FindNotepadWindow();
switch (kind) switch (info.Kind)
{ {
case WindowKind.Notepad: case WindowKind.Notepad:
SendMessage(hwnd, EM_REPLACESEL, (IntPtr)1, message); SendMessage(info.Handle, EM_REPLACESEL, (IntPtr)1, message);
break; break;
case WindowKind.NotepadPlusPlus: case WindowKind.NotepadPlusPlus:
{ {
WriteToNotepadPlusPlus(hwnd, message); WriteToNotepadPlusPlus(info.Handle, message);
break; break;
} }
} }

View file

@ -10,13 +10,15 @@ namespace Notepad.Extensions.Logging
{ {
var poolProvider = new DefaultObjectPoolProvider(); var poolProvider = new DefaultObjectPoolProvider();
stringBuilderPool = poolProvider.CreateStringBuilderPool(); stringBuilderPool = poolProvider.CreateStringBuilderPool();
windowFinder = new WindowFinder();
} }
public static ILoggerProvider Instance { get; } = new NotepadLoggerProvider(); public static ILoggerProvider Instance { get; } = new NotepadLoggerProvider();
readonly ObjectPool<StringBuilder> stringBuilderPool; readonly ObjectPool<StringBuilder> 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() public void Dispose()
{ {

View file

@ -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;
}
}
}

View file

@ -0,0 +1,17 @@
using Microsoft.Extensions.ObjectPool;
namespace Notepad.Extensions.Logging
{
class WindowEnumerationStatePoolingPolicy : IPooledObjectPolicy<WindowEnumerationState>
{
public static IPooledObjectPolicy<WindowEnumerationState> Instance { get; } = new WindowEnumerationStatePoolingPolicy();
public WindowEnumerationState Create() => new WindowEnumerationState();
public bool Return(WindowEnumerationState obj)
{
obj.Reset();
return true;
}
}
}

View file

@ -1,89 +1,37 @@
using System; using System;
using System.ComponentModel; using Microsoft.Extensions.ObjectPool;
using System.Text;
using System.Text.RegularExpressions;
namespace Notepad.Extensions.Logging 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<WindowEnumerationState>(WindowEnumerationStatePoolingPolicy.Instance);
}
readonly ObjectPool<WindowEnumerationState> statePool;
public WindowInfo FindNotepadWindow()
{
var stateObject = statePool.Get();
try try
{ {
FindMainWindow(); NativeMethods.EnumWindows(enumWindowsDelegate, stateObject);
return (windowKind, handle); return new WindowInfo(stateObject.WindowKind, stateObject.Handle);
} }
finally finally
{ {
handle = IntPtr.Zero; statePool.Return(stateObject);
sb.Clear();
windowKind = WindowKind.Invalid;
} }
} }
static IntPtr FindMainWindow()
{
NativeMethods.EnumWindows(enumWindowsDelegate, IntPtr.Zero);
return handle;
}
static NativeMethods.EnumWindowsDelegate enumWindowsDelegate = new NativeMethods.EnumWindowsDelegate(EnumWindowsCallback); 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); var state = (WindowEnumerationState)lParam;
if (result < 0) return state.ExamineWindow(hWnd);
{
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;
} }
} }
} }

View file

@ -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; }
}
}

View file

@ -1,6 +1,6 @@
namespace Notepad.Extensions.Logging namespace Notepad.Extensions.Logging
{ {
public enum WindowKind enum WindowKind
{ {
Invalid, Invalid,
Notepad, Notepad,