0

在 Visual C# 中,我试图从多个文本框(一次一个)中获取文本并将它们粘贴到记事本中。我通过复制到剪贴板、alt-tab、然后粘贴到记事本来做到这一点……然后再粘贴到其他文本框。这段代码代表了这个想法:

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}");

如您所见,这从三个文本框(名为 subBox1、2 和 3)复制和粘贴。但是,由于某种原因,只有最后一个文本框的内容被复制了。如果我注释掉第三个框也会发生这种情况......在这种情况下,只有第二个文本框的内容被复制。正如您在此处看到的,我已经尝试使用 SelectAll() 和 Copy() 以及 Clipboard 类。两者都有相同的问题。

例如,如果文本框内容分别是“asdf”、“qwer”和“zxcv”,我看到的只是“zxcv”三遍。

知道为什么会这样吗?我已经坚持了大约一个小时,不知道发生了什么。

万分感谢!

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'sEdit窗口(您输入的文本区域)的句柄。

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 回答