mirror of
https://github.com/yaakov-h/Notepad.Extensions.Logging.git
synced 2024-11-22 11:14:50 +00:00
Merge branch 'dev/notepad++'
This commit is contained in:
commit
4259c47579
8 changed files with 170 additions and 36 deletions
|
@ -1,4 +1,6 @@
|
||||||
namespace Microsoft.Extensions.Logging
|
using Notepad.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Logging
|
||||||
{
|
{
|
||||||
public static class LoggingBuilderExtensions
|
public static class LoggingBuilderExtensions
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static IntPtr FindNotepadWindow()
|
|
||||||
{
|
{
|
||||||
IntPtr hwnd;
|
case WindowKind.Notepad:
|
||||||
|
SendMessage(hwnd, EM_REPLACESEL, (IntPtr)1, message);
|
||||||
|
break;
|
||||||
|
|
||||||
hwnd = NativeMethods.FindWindow(null, "Untitled - Notepad");
|
case WindowKind.NotepadPlusPlus:
|
||||||
if (hwnd != IntPtr.Zero)
|
|
||||||
{
|
{
|
||||||
return hwnd;
|
WriteToNotepadPlusPlus(hwnd, message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hwnd = NativeMethods.FindWindow(null, "*Untitled - Notepad");
|
unsafe static void WriteToNotepadPlusPlus(IntPtr hwnd, string message)
|
||||||
if (hwnd != IntPtr.Zero)
|
|
||||||
{
|
{
|
||||||
return hwnd;
|
var dataLength = Encoding.UTF8.GetByteCount(message);
|
||||||
}
|
|
||||||
|
|
||||||
return IntPtr.Zero;
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.Logging
|
namespace Notepad.Extensions.Logging
|
||||||
{
|
{
|
||||||
class NullDisposable : IDisposable
|
class NullDisposable : IDisposable
|
||||||
{
|
{
|
||||||
|
|
|
@ -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");
|
static NativeMethods.EnumWindowsDelegate enumWindowsDelegate = new NativeMethods.EnumWindowsDelegate(EnumWindowsCallback);
|
||||||
if (hwnd != IntPtr.Zero)
|
|
||||||
|
static bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
|
||||||
{
|
{
|
||||||
return hwnd;
|
var result = NativeMethods.GetWindowText(hWnd, sb, sb.Capacity);
|
||||||
}
|
if (result < 0)
|
||||||
|
|
||||||
hwnd = NativeMethods.FindWindow(null, "*Untitled - Notepad");
|
|
||||||
if (hwnd != IntPtr.Zero)
|
|
||||||
{
|
{
|
||||||
return hwnd;
|
throw new Win32Exception(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return IntPtr.Zero;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
9
Notepad.Extensions.Logging/WindowKind.cs
Normal file
9
Notepad.Extensions.Logging/WindowKind.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Notepad.Extensions.Logging
|
||||||
|
{
|
||||||
|
public enum WindowKind
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
Notepad,
|
||||||
|
NotepadPlusPlus
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue