Skip to content

Commit 6db95e1

Browse files
bstordrupaugustoproiete
authored andcommitted
Implementing an alternate way of finding editor handle for new Notepad
* Adding needed interop definitions to enumerate child windows * If first attempt using FindWindowEx with notepad process MainWindowHandle does not find editor handle, the search is done by enumerating child window handles for the notepad process MainWindowHandle.
1 parent bbfefbe commit 6db95e1

File tree

3 files changed

+92
-7
lines changed

3 files changed

+92
-7
lines changed

sample/ConsoleDemo/Program.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
//
1515
#endregion
1616

17+
using Serilog;
1718
using System;
1819
using System.Threading;
19-
using Serilog;
2020

2121
namespace ConsoleDemo
2222
{
@@ -33,15 +33,23 @@ private static void Main(string[] args)
3333

3434
try
3535
{
36-
Console.WriteLine("Open a `notepad.exe` instance and press <enter> to continue...");
37-
Console.ReadLine();
36+
//Console.WriteLine("Open a `notepad.exe` instance and press <enter> to continue...");
37+
//Console.ReadLine();
3838

3939
Console.WriteLine("Writing messages to the most recent Notepad you opened...");
4040

4141
Log.Debug("Getting started");
4242

43-
Log.Information("Hello {Name} from thread {ThreadId}", Environment.GetEnvironmentVariable("USERNAME"),
44-
Thread.CurrentThread.ManagedThreadId);
43+
var startTime = DateTime.Now;
44+
45+
while (DateTime.Now - startTime < TimeSpan.FromMinutes(1))
46+
{
47+
48+
Log.Information("Hello {Name} from thread {ThreadId}", Environment.GetEnvironmentVariable("USERNAME"),
49+
Thread.CurrentThread.ManagedThreadId);
50+
51+
Thread.Sleep(1000);
52+
}
4553

4654
Log.Warning("No coins remain at position {@Position}", new { Lat = 25, Long = 134 });
4755

src/Serilog.Sinks.Notepad/Sinks/Notepad/Interop/NotepadTextWriter.cs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
//
1515
#endregion
1616

17+
using Serilog.Debugging;
1718
using System;
19+
using System.Collections.Generic;
1820
using System.Diagnostics;
1921
using System.IO;
2022
using System.Linq;
23+
using System.Runtime.InteropServices;
2124
using System.Text;
22-
using Serilog.Debugging;
2325

2426
namespace Serilog.Sinks.Notepad.Interop
2527
{
@@ -55,7 +57,10 @@ public override void Flush()
5557
// No instances of Notepad found... Nothing to do
5658
return;
5759
}
60+
}
5861

62+
if (_currentNotepadEditorHandle == IntPtr.Zero)
63+
{
5964
var notepadWindowHandle = currentNotepadProcess.MainWindowHandle;
6065

6166
var notepadEditorHandle = FindNotepadEditorHandle(notepadWindowHandle);
@@ -80,7 +85,16 @@ public override void Flush()
8085
// Write the log message to Notepad
8186
User32.SendMessage(_currentNotepadEditorHandle, User32.EM_REPLACESEL, (IntPtr)1, message);
8287

83-
buffer.Clear();
88+
// Get how many characters are in the Notepad editor after putting in new text
89+
var textLengthAfter = User32.SendMessage(_currentNotepadEditorHandle, User32.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
90+
91+
// If no change in textLength, reset editor handle to try to find it again.
92+
if (textLengthAfter == textLength)
93+
_currentNotepadEditorHandle = IntPtr.Zero;
94+
95+
// Otherwise, we clear the buffer
96+
else
97+
buffer.Clear();
8498
}
8599

86100
protected override void Dispose(bool disposing)
@@ -120,6 +134,13 @@ private static IntPtr FindNotepadEditorHandle(IntPtr notepadWindowHandle)
120134
return richEditHandle;
121135
}
122136

137+
// Issue #59 - Alternate way of finding the RichEditD2DPT class:
138+
if (FindEditorHandleThroughChildWindows(notepadWindowHandle) is var richEditHandleFromChildren
139+
&& richEditHandleFromChildren != IntPtr.Zero)
140+
{
141+
return richEditHandleFromChildren;
142+
}
143+
123144
return User32.FindWindowEx(notepadWindowHandle, IntPtr.Zero, "Edit", null);
124145
}
125146

@@ -130,5 +151,49 @@ private void EnsureNotDisposed()
130151
throw new ObjectDisposedException(GetType().Name);
131152
}
132153
}
154+
155+
private static string GetClassNameFromWindow(IntPtr handle)
156+
{
157+
StringBuilder sb = new StringBuilder(256);
158+
var ret = User32.GetClassName(handle, sb, sb.Capacity);
159+
return ret != 0 ? sb.ToString() : string.Empty;
160+
}
161+
162+
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
163+
{
164+
GCHandle gch = GCHandle.FromIntPtr(pointer);
165+
List<IntPtr> list = gch.Target as List<IntPtr>;
166+
if (list == null)
167+
{
168+
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
169+
}
170+
171+
if (string.Equals(GetClassNameFromWindow(handle), "RichEditD2DPT", StringComparison.OrdinalIgnoreCase))
172+
{
173+
list.Add(handle);
174+
175+
// Stop enumerating - we found the one.
176+
return false;
177+
}
178+
179+
return true;
180+
}
181+
182+
private static IntPtr FindEditorHandleThroughChildWindows(IntPtr notepadWindowHandle)
183+
{
184+
List<IntPtr> result = new List<IntPtr>(1);
185+
GCHandle listHandle = GCHandle.Alloc(result);
186+
try
187+
{
188+
User32.Win32Callback childProc = new User32.Win32Callback(EnumWindow);
189+
User32.EnumChildWindows(notepadWindowHandle, childProc, GCHandle.ToIntPtr(listHandle));
190+
}
191+
finally
192+
{
193+
if (listHandle.IsAllocated)
194+
listHandle.Free();
195+
}
196+
return result.FirstOrDefault();
197+
}
133198
}
134199
}

src/Serilog.Sinks.Notepad/Sinks/Notepad/Interop/User32.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,17 @@ internal class User32
3535

3636
[DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
3737
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
38+
39+
40+
// Needed for EnumChildWindows for registering a call back function.
41+
public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);
42+
43+
[DllImport("user32.Dll", CharSet = CharSet.Unicode, SetLastError = true)]
44+
[return: MarshalAs(UnmanagedType.Bool)]
45+
public static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam);
46+
47+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
48+
public static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);
49+
3850
}
3951
}

0 commit comments

Comments
 (0)