Merge branch 'dev/notepad++'

This commit is contained in:
Yaakov 2020-05-22 14:27:46 +10:00
commit 4259c47579
8 changed files with 170 additions and 36 deletions

View file

@ -1,4 +1,6 @@
namespace Microsoft.Extensions.Logging using Notepad.Extensions.Logging;
namespace Microsoft.Extensions.Logging
{ {
public static class LoggingBuilderExtensions public static class LoggingBuilderExtensions
{ {

View file

@ -1,7 +1,8 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.Extensions.Logging namespace Notepad.Extensions.Logging
{ {
static class NativeMethods static class NativeMethods
{ {
@ -15,5 +16,36 @@ namespace Microsoft.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, [MarshalAs(UnmanagedType.LPWStr)] string lParam); public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
public const int SCI_ADDTEXT = 2001;
[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);
[DllImport("user32.dll")]
public static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lParam);
[DllImport("User32.dll")]
public static extern int GetWindowText(IntPtr hWndParent, StringBuilder sb, int maxCount);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, int flAllocationType, int flProtect);
public const int MEM_COMMIT = 0x00001000;
public const int MEM_RESERVE = 0x00002000;
public const int MEM_RELEASE = 0x8000;
public const int PAGE_READWRITE = 0x04;
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, int dwFreeType);
[DllImport("User32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, IntPtr nSize, out IntPtr lpNumberOfBytesWritten);
} }
} }

View file

@ -10,6 +10,7 @@
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression> <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,9 +1,14 @@
using System; using System;
using System.Buffers;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
using static Notepad.Extensions.Logging.NativeMethods;
namespace Microsoft.Extensions.Logging namespace Notepad.Extensions.Logging
{ {
class NotepadLogger : ILogger class NotepadLogger : ILogger
{ {
@ -84,28 +89,55 @@ namespace Microsoft.Extensions.Logging
static void WriteToNotepad(string message) static void WriteToNotepad(string message)
{ {
IntPtr hwnd = FindNotepadWindow(); var (kind, hwnd) = WindowFinder.FindNotepadWindow();
IntPtr edit = NativeMethods.FindWindowEx(hwnd, IntPtr.Zero, "EDIT", null); switch (kind)
NativeMethods.SendMessage(edit, NativeMethods.EM_REPLACESEL, (IntPtr)1, message); {
case WindowKind.Notepad:
SendMessage(hwnd, EM_REPLACESEL, (IntPtr)1, message);
break;
case WindowKind.NotepadPlusPlus:
{
WriteToNotepadPlusPlus(hwnd, message);
break;
}
}
} }
static IntPtr FindNotepadWindow() unsafe static void WriteToNotepadPlusPlus(IntPtr hwnd, string message)
{ {
IntPtr hwnd; var dataLength = Encoding.UTF8.GetByteCount(message);
hwnd = NativeMethods.FindWindow(null, "Untitled - Notepad");
if (hwnd != IntPtr.Zero)
{
return hwnd;
}
hwnd = NativeMethods.FindWindow(null, "*Untitled - Notepad"); //
if (hwnd != IntPtr.Zero) // HERE BE DRAGONS
{ // We need to copy the message into Notepad++'s memory so that it can read it.
return hwnd; // Look away now, before its too late.
} //
var threadID = 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);
if (mem == IntPtr.Zero) throw new Win32Exception();
return IntPtr.Zero; try
{
var data = ArrayPool<byte>.Shared.Rent(dataLength);
try
{
var idx = Encoding.UTF8.GetBytes(message, 0, message.Length, data, 0);
WriteProcessMemory(remoteProcess.Handle, mem, data, (IntPtr)dataLength, out var bytesWritten);
SendMessage(hwnd, SCI_ADDTEXT, (IntPtr)dataLength, mem);
}
finally
{
ArrayPool<byte>.Shared.Return(data);
}
}
finally
{
VirtualFreeEx(remoteProcess.Handle, IntPtr.Zero, IntPtr.Zero, MEM_RELEASE);
}
} }
} }
} }

View file

@ -1,7 +1,8 @@
using System.Text; using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.ObjectPool;
namespace Microsoft.Extensions.Logging namespace Notepad.Extensions.Logging
{ {
class NotepadLoggerProvider : ILoggerProvider class NotepadLoggerProvider : ILoggerProvider
{ {

View file

@ -1,6 +1,6 @@
using System; using System;
namespace Microsoft.Extensions.Logging namespace Notepad.Extensions.Logging
{ {
class NullDisposable : IDisposable class NullDisposable : IDisposable
{ {

View file

@ -1,33 +1,90 @@
using System; using System;
using System.ComponentModel;
using System.Text;
using System.Text.RegularExpressions;
namespace Microsoft.Extensions.Logging namespace Notepad.Extensions.Logging
{ {
static class WindowFinder static class WindowFinder
{ {
public static IntPtr FindNotepadWindow()
public static (WindowKind kind, IntPtr hwnd) FindNotepadWindow()
{ {
var hwnd = FindMainWindow(); sb ??= new StringBuilder(4096);
IntPtr edit = NativeMethods.FindWindowEx(hwnd, IntPtr.Zero, "EDIT", null);
return edit; try
{
FindMainWindow();
return (windowKind, handle);
}
finally
{
handle = IntPtr.Zero;
sb.Clear();
windowKind = WindowKind.Invalid;
}
} }
static IntPtr FindMainWindow() static IntPtr FindMainWindow()
{ {
IntPtr hwnd; NativeMethods.EnumWindows(enumWindowsDelegate, IntPtr.Zero);
return handle;
hwnd = NativeMethods.FindWindow(null, "Untitled - Notepad"); }
if (hwnd != IntPtr.Zero)
static NativeMethods.EnumWindowsDelegate enumWindowsDelegate = new NativeMethods.EnumWindowsDelegate(EnumWindowsCallback);
static bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
{
var result = NativeMethods.GetWindowText(hWnd, sb, sb.Capacity);
if (result < 0)
{ {
return hwnd; throw new Win32Exception(result);
} }
hwnd = NativeMethods.FindWindow(null, "*Untitled - Notepad"); WindowFinder.handle = hWnd;
if (hwnd != IntPtr.Zero)
if (sb.Length > 0 && sb[0] == '*')
{ {
return hwnd; // Notepad and Notepad++ both mark dirty documents by adding a leading asterisk to the window name.
sb.Remove(0, 1);
} }
return IntPtr.Zero; 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,9 @@
namespace Notepad.Extensions.Logging
{
public enum WindowKind
{
Invalid,
Notepad,
NotepadPlusPlus
}
}