より正確な結果を得るには、SendMessageを使用します。SendMessageを使用するには、最初にメモ帳のテキスト領域への有効なウィンドウハンドルが必要です。これはさまざまな方法で実行できますが、私は単純な子ルックアップ関数を使用することを好みます。
次の名前空間のインポートとPInvoke宣言が必要になります。
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);
次に、子ウィンドウのルックアップに移ります。基本的にはFindWindowExと同様に機能しますが、自分で作成したかったので、複数のウィンドウを取得できます。次のラッパークラスを使用して、呼び出し間の情報を記述します。
private class WindowLookup
{
public string LookupName { get; private set; }
public List<IntPtr> MatchedChildren { get; private set; }
public int Depth { get; set; }
public int MaxDepth { get; set; }
public WindowLookup(string lookup, int maxdepth)
{
this.MatchedChildren = new List<IntPtr>();
this.LookupName = lookup;
this.MaxDepth = maxdepth;
if (this.MaxDepth > 0)
this.MaxDepth++; //account for the depth past the parent control.
this.Depth = 0;
}
}
そして、次の関数がすべての作業を行います。
private static List<IntPtr> 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 && (lookup = handle.Target as WindowLookup) != null)
{
if (lookup.Depth < lookup.MaxDepth || lookup.MaxDepth == 0)
{
lookup.Depth++;
var builder = new StringBuilder(256);
if (GetClassName(hChild, builder, builder.Capacity) && builder.ToString().ToLower() == lookup.LookupName.ToLower())
lookup.MatchedChildren.Add(hChild);
EnumChildWindows(hChild, LookupChildProc, lParam);
}
}
return true;
}
これらの関数の実装についてあまり心配する必要はありません。そのまま動作します。重要なのは、これらの関数を使用すると、ウィンドウ(入力したテキスト領域)へnotepad
のハンドルを非常に簡単に見つけることができるということです。Edit
var notepads = Process.GetProcessesByName("notepad");
if (notepads.Length > 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.
}
}
}
さて、私がコードを残したところは、その時点で実行されていた各メモ帳プロセスの「編集」ウィンドウを通るループの途中でした。有効なウィンドウハンドルができたので、SendMessageを使用してそれにデータを送信できます。特に、テキストの追加。リモコンへのテキストの追加を処理するために、次の関数を作成しました。
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);
}
}
AppendWindowText関数ができたので、上のネストされたループ(コメントを入れた場所)に関数呼び出しを追加できます。
AppendWindowText(edit, "Some text here");
そして、あなたはそれを持っています。少し言葉の多い応答ですが、最終的には、この方法は、SendKeysを使用してウィンドウのフォーカスなどを行うよりもはるかに信頼性が高くなります。独自のアプリケーションのフォーカスを失う必要はありません。
ご不明な点がございましたら、お気軽にコメントください。できる限りお答えいたします。
乾杯、
J
編集:いくつかの参照:
SendMessage関数(MSDN)
EnumChildWindows関数(MSDN)
SendMessageを使用してテキストを追加する