在Visual C#中,我正在尝试从多个文本框中获取文本(一次一个)并将其粘贴到记事本中。我这样做是通过复制到剪贴板,alt-tabbing,然后粘贴到记事本...然后再为其他文本框。这段代码代表了这个想法:
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”三次。
知道为什么会这样吗?我现在已经坚持了大约一个小时,并且不知道发生了什么。
非常感谢!
答案 0 :(得分: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"));
答案 1 :(得分: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.
}
}
}
现在,我离开代码的地方正在循环中通过&#34;编辑&#34;当时运行的每个记事本进程的窗口。现在我们有了一个有效的窗口句柄,我们可以使用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和聚焦窗口等更可靠。你永远不需要失去自己的应用程序的焦点。
如果您有任何问题,请随时发表评论,我会尽力回答。
干杯,
Ĵ
编辑:一些参考文献:
SendMessage function (MSDN)
EnumChildWindows function (MSDN)
Appending text using SendMessage