0

Visual C#では、複数のテキストボックスから(一度に1つずつ)テキストを取得してメモ帳に貼り付けようとしています。これを行うには、クリップボードにコピーし、Altキーを押しながらタブを押してから、メモ帳に貼り付けます...その後、他のテキストボックスに貼り付けます。このコードはこのアイデアを表しています。

subBox1.SelectAll();
subBox1.Copy();
SendKeys.Send("%{TAB}");    // alt+tab
SendKeys.Send("^v");        // paste
SendKeys.Send("{TAB}");     // tab

subBox2.SelectAll();
subBox2.Copy();
SendKeys.Send("^v");
SendKeys.Send("{TAB}");

subBox3.SelectAll();
subBox3.Copy();
SendKeys.Send("^v");
SendKeys.Send("{TAB}");

ご覧のとおり、これは3つのテキストボックス(subBox1、2、および3という名前)からコピーして貼り付けます。ただし、何らかの理由で、最後のテキストボックスの内容のみがコピーされます。これは、3番目のボックスをコメントアウトした場合にも発生します...その場合、2番目のテキストボックスのコンテンツのみがコピーされます。ここに表示されているように、SelectAll()とCopy()、およびClipboardクラスを使用してみました。どちらも同じ問題を抱えています。

たとえば、テキストボックスの内容がそれぞれ「asdf」、「qwer」、「zxcv」の場合、表示されるのは「zxcv」の3回だけです。

なぜこれが起こっているのか考えていますか?私はこれに約1時間立ち往生していて、何が起こっているのかわかりません。

トンありがとう!

4

2 に答える 2

2

SendKeysは、他のアプリケーションが送信したキーを処理するのを待たないため、メモ帳がキー押下の処理に取り掛かるまでに、プログラムはすでにsubBox3他のテキストの上にのテキストをコピーしています。

代わりにSendWaitを使用する必要があります。

同様に、Alt + Tabを送信する代わりに、次のようなものを使用できます。

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);

// ... 

SetForegroundWindow(FindWindow(null, "Untitled - Notepad"));
于 2012-07-18T04:20:53.300 に答える
0

より正確な結果を得るには、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を使用してテキストを追加する

于 2012-07-18T06:20:56.637 に答える