Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I'd use SendMessage for more accurate results. To use SendMessage, you first need a valid window handle to the text area of Notepad. This can be done in a variety of ways, but I prefer just using my simple child lookup function.</p> <p>You will need the following namespace imports and PInvoke declarations:</p> <pre><code>using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Text; //pinvoke [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] [return:MarshalAs(UnmanagedType.Bool)] private static extern bool GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll")] private static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll")] [return:MarshalAs(UnmanagedType.Bool)] private static extern bool EnumChildWindows(IntPtr hParent, delChildWndProc callback, IntPtr lpParam); //delegate callback for EnumChildWindows: [return:MarshalAs(UnmanagedType.Bool)] private delegate bool delChildWndProc(IntPtr hWnd, IntPtr lParam); </code></pre> <p>Now, onto the child window lookup. Basically works similar to FindWindowEx, but I wanted to write my own, and it retrieves multiple windows which can be nice. It uses the following wrapper class to describe information between calls:</p> <pre><code>private class WindowLookup { public string LookupName { get; private set; } public List&lt;IntPtr&gt; MatchedChildren { get; private set; } public int Depth { get; set; } public int MaxDepth { get; set; } public WindowLookup(string lookup, int maxdepth) { this.MatchedChildren = new List&lt;IntPtr&gt;(); this.LookupName = lookup; this.MaxDepth = maxdepth; if (this.MaxDepth &gt; 0) this.MaxDepth++; //account for the depth past the parent control. this.Depth = 0; } } </code></pre> <p>And then the following functions do all the work:</p> <pre><code>private static List&lt;IntPtr&gt; FindAllWindows(IntPtr hParent, string className, int maxdepth = 0) { var lookup = new WindowLookup(className, maxdepth); var gcAlloc = GCHandle.Alloc(lookup); try { LookupChildProc(hParent, GCHandle.ToIntPtr(gcAlloc)); } finally { if (gcAlloc.IsAllocated) gcAlloc.Free(); } return lookup.MatchedChildren; } private static bool LookupChildProc(IntPtr hChild, IntPtr lParam) { var handle = GCHandle.FromIntPtr(lParam); WindowLookup lookup = null; if (handle.IsAllocated &amp;&amp; (lookup = handle.Target as WindowLookup) != null) { if (lookup.Depth &lt; lookup.MaxDepth || lookup.MaxDepth == 0) { lookup.Depth++; var builder = new StringBuilder(256); if (GetClassName(hChild, builder, builder.Capacity) &amp;&amp; builder.ToString().ToLower() == lookup.LookupName.ToLower()) lookup.MatchedChildren.Add(hChild); EnumChildWindows(hChild, LookupChildProc, lParam); } } return true; } </code></pre> <p>You don't need to worry about the implementation of these functions too much, they'll work as-is. The key thing is that using these functions, you can find the handle to <code>notepad</code>'s <code>Edit</code> window (the text area you type in) very easily.</p> <pre><code>var notepads = Process.GetProcessesByName("notepad"); if (notepads.Length &gt; 0) { foreach(var notepad in notepads) //iterate through all the running notepad processes. Of course, you can filter this by processId or whatever. { foreach(var edit in FindAllWindows(notepad.MainWindowHandle, "Edit")) { //next part of the code will go here, read on. } } } </code></pre> <p>Now, where I left the code was in the middle of a loop through the "Edit" windows of each notepad process running at the time. Now that we have a valid window handle, we can use SendMessage to send stuff to it. In particular, appending text. I wrote the following function to handle appending text to a remote control:</p> <pre><code>private static void AppendWindowText(IntPtr hWnd, string text) { if (hWnd != IntPtr.Zero) { //for your reference, 0x0E (WM_GETTEXTLENGTH), 0xB1 (EM_SETSEL), 0xC2 (EM_REPLACESEL) int len = SendMessage(hWnd, 0x000E, IntPtr.Zero, IntPtr.Zero).ToInt32(); var unmanaged = Marshal.StringToHGlobalAuto(text); SendMessage(hWnd, 0x00B1, new IntPtr(len), new IntPtr(len)); SendMessage(hWnd, 0x00C2, IntPtr.Zero, unmanaged); Marshal.FreeHGlobal(unmanaged); } } </code></pre> <p>Now that we have our AppendWindowText function, you can add a function call to it in the nested loop above (where I put the comment):</p> <pre><code>AppendWindowText(edit, "Some text here"); </code></pre> <p>And there you have it. It's a bit of a wordy response, but in the end this method is far more reliable than using SendKeys and focusing the window etc. You never need to lose focus of your own application. </p> <p>If you have any questions, feel free to comment and I'll answer as best I can.</p> <p>Cheers,<br/> J</p> <p>EDIT: Some references:</p> <p><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms644950%28v=vs.85%29.aspx" rel="nofollow">SendMessage function (MSDN)</a><br/> <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633494%28v=vs.85%29.aspx" rel="nofollow">EnumChildWindows function (MSDN)</a><br/> <a href="http://support.microsoft.com/kb/109550" rel="nofollow">Appending text using SendMessage</a></p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload