我写了这段代码:
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace AutoPoster
{
public partial class Form1 : Form
{
private bool isWriting { get; set; }
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F9)
{
MessageBox.Show("staring");
WriteThis("Test");
}
else if (e.KeyCode == Keys.F4)
{
ShowWindow(this.Handle, 1);
MessageBox.Show("stopping");
isWriting = false;
}
}
private void button1_Click(object sender, EventArgs e)
{
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr parentHandle, int childAfter, string className, string windowTitle);
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
const int WM_CHAR = 0x0102;
[DllImport("user32.dll")]
public static extern int SetForegroundWindow(IntPtr hWnd);
[STAThread]
private void WriteThis(string letter)
{
isWriting = true;
while (isWriting)
{
Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process proc in processes)
{
SetForegroundWindow(proc.MainWindowHandle);
Thread.Sleep(1000);
SendKeys.SendWait("T");
SendKeys.SendWait($"{letter}");
SendKeys.SendWait("{ENTER}");
Thread.Sleep(10000);
}
}
}
}
}
它有一个 WriteThis 方法,可以模拟特定窗口中的击键。当您按 f9 时,它开始工作,当您按 f4 时,它停止。我需要确保在执行此方法期间我看不到它如何打开窗口并执行操作。也就是说,程序必须在后台运行。我如何更改代码或添加一些内容来实现这一点?我知道isWriting我做得不太好。
如果我理解正确的话,您想在记事本中写入而不将其带到前台。如果是这种情况,那么要解决这个问题,你需要完全放弃对窗口进行聚焦。现在你的代码的工作方式如下:
SetForegroundWindow)。你应该大致拥有的逻辑是:
在这里,正如我所想,您已经注意到该方法是单独的,因为每个程序都有自己的内部组件,甚至记事本也不同。因此,我只会向您展示一个可以做什么的示例,然后根据您的要求进行调整。
那么,让我们开始...
让我们准备一个项目。我不太喜欢手动编写所有 WinAPI 方法,这就是我将使用代码生成器的原因。您可以手动编写所有列出的方法,互联网上充满了它们的实现和应用程序,但如果您像我一样,那么:
Microsoft.Windows.CsWin32NativeMethods.txt(暂时为空)项目已经准备好了,太好了,让我们开始实施吧。首先,我们需要找到记事本窗口,这可以通过多种方式完成,最简单易懂的是:
FindWindow).exe(如何操作)并Handle从那里获取窗口。我将使用这个方法,获取属性proc.MainWindowHandle。我们找到了窗口,现在我们需要找到文本字段本身。如果我们打开
Spy++(或类似的WinSpy++)并开始捕获记事本,我们将看到如下内容:正如你所看到的,窗口中负责输入文本的部分被称为
RichEditD2DPT(在旧版本中它似乎被简单地称为Edit),这正是我们需要得到的,但是如何得到呢?有2种方法:使用 WinAPI
FindWindowEx,为此NativeMethods.txt我们写入文件FindWindowEx并重建项目。接下来我们写这样的东西:var editHandle = PInvoke.FindWindowEx((HWND)proc.MainWindowHandle, 默认, "编辑", null);
让我们运行它并查看其值。如果它是坚固的
0,那么我们就找不到它,但如果它有效,那就太好了。在旧版本的记事本中,该类Edit似乎是窗口本身的子对象,但在新版本中情况并非如此,因为它找不到...使用
EnumChildWindows所有窗口类,浏览所有类并通过名称找到您需要的类。这就是我要走的路。为此,我将在文件中NativeMethods.txt添加一个新行EnumChildWindows,GetClassName然后编写以下代码:如果您知道 WinAPI 的工作原理,该方法相对简单)我们
PInvoke.EnumChildWindows通过将窗口处理程序传递给它来进行调用。该方法需要我们有一个具有两个值的委托,即类处理程序及其参数,它将调用。在内部,当调用时,我们调用PInvoke.GetClassName,我们将类标识符传递给它,以及它的名称和该缓冲区的大小将被写入的位置(我使用了 256)。接下来,我们只需将其转换为字符串,如果该名称与搜索到的数组中的名称匹配,则保存结果并通过返回 delegate 来handle完成它。最后我们返回我们收到的东西。EnumChildWindowsfalsehandle剩下要做的唯一一件事就是将必要的命令发送到找到的文本字段。在这里我们再次有选择:
SendMessage- 发送带有必要数据的“消息”并等待执行。PostMessage- 与上一个类似,但只是“异步”,该函数不等待发送成功。SendInput- 发送“imp”(键盘/鼠标)到活动窗口。在这种情况下,它不适合我们,我带来它只是为了让你了解什么是“引擎盖下”SendKeys.SendWait()。SendNotifyMessage,SendMessageTimeout等等,那就自己去这个动物园玩玩吧)对于这个例子,
SendMessage事实上,我们将它添加到文件中NativeMethods.txt并使用必要的参数调用它。更多关于
SendMessage该函数有 4 个参数:
我们发送它的窗口/类处理程序,一切都清楚了。
消息本身。这里已经有很多选择,我建议你研究一下。对我们来说有趣的是
WM_GETTEXT- 接收文本。WM_SETTEXT- 将文本完全更改为指定文本。WM_CHAR- 发送指定的字符。WM_KEYDOWN- “保持”指定的键。WM_KEYUP- “按”指定的键。3 和 4。这些是与特定消息相对应的参数。
实际上,现在举几个例子:
逐字符发送文本
NativeMethods.txt新行WM_CHAR。代码本身:
替换窗口中的文本
NativeMethods.txt包含值的行WM_SETTEXT代码:
“按”键
NativeMethods.txt行WM_KEYDOWN,WM_KEYUP,VIRTUAL_KEY例如,转到新行:
请注意,这不是一个按键,它只是发送一条按键被按下的消息,这就是为什么每个程序都会有不同的行为,但为了以防万一,不要忘记发送“释放”事件。如果您需要直接模拟按键,请返回聚焦窗口并使用
SendInput。获取窗口文本
NativeMethods.txt我们添加WM_GETTEXTLENGTH,WM_GETTEXT代码:
下面简要介绍一下 WinAPI 的世界。
哦,是的,
unsafe你可以尝试拒绝,文本替换代码将如下所示:其余的都是以此类推。好吧,如果您自己编写方法,请不要忘记安全性、优化等。例如,不应使用 StringBuilder(我经常看到使用它的上述函数的示例)。