RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1593137
Accepted
fan fani
fan fani
Asked:2024-09-08 00:05:19 +0000 UTC2024-09-08 00:05:19 +0000 UTC 2024-09-08 00:05:19 +0000 UTC

我怎样才能让这个程序在后台运行?

  • 772

我写了这段代码:

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我做得不太好。

c#
  • 1 1 个回答
  • 82 Views

1 个回答

  • Voted
  1. Best Answer
    EvgeniyZ
    2024-09-08T10:58:06Z2024-09-08T10:58:06Z

    如果我理解正确的话,您想在记事本中写入而不将其带到前台。如果是这种情况,那么要解决这个问题,你需要完全放弃对窗口进行聚焦。现在你的代码的工作方式如下:

    • 我在前屏幕上显示所需的窗口 ( SetForegroundWindow)。
    • 我按下必要的按钮。
    • 我希望窗口焦点清晰,没有被其他东西挡住。

    你应该大致拥有的逻辑是:

    • 我找到了正确的窗口。
    • 在此窗口中,我找到所需的元素(在我们的示例中为记事本文本字段)。
    • 我向该元素发送必要的命令。

    在这里,正如我所想,您已经注意到该方法是单独的,因为每个程序都有自己的内部组件,甚至记事本也不同。因此,我只会向您展示一个可以做什么的示例,然后根据您的要求进行调整。

    那么,让我们开始...

    1. 让我们准备一个项目。我不太喜欢手动编写所有 WinAPI 方法,这就是我将使用代码生成器的原因。您可以手动编写所有列出的方法,互联网上充满了它们的实现和应用程序,但如果您像我一样,那么:

      • 安装包Microsoft.Windows.CsWin32
      • 将文件添加到项目中NativeMethods.txt(暂时为空)
      • 在项目属性中,启用“不安全代码”(属性-程序集-勾选“不安全代码”)。
    2. 项目已经准备好了,太好了,让我们开始实施吧。首先,我们需要找到记事本窗口,这可以通过多种方式完成,最简单易懂的是:

      • 通过标题查找窗口(WinAPI 方法 - FindWindow)
      • 按名称找到进程.exe(如何操作)并Handle从那里获取窗口。我将使用这个方法,获取属性proc.MainWindowHandle。
    3. 我们找到了窗口,现在我们需要找到文本字段本身。如果我们打开Spy++(或类似的WinSpy++)并开始捕获记事本,我们将看到如下内容:

      间谍++

      正如你所看到的,窗口中负责输入文本的部分被称为RichEditD2DPT(在旧版本中它似乎被简单地称为Edit),这正是我们需要得到的,但是如何得到呢?有2种方法:

      1. 使用 WinAPI FindWindowEx,为此NativeMethods.txt我们写入文件FindWindowEx并重建项目。接下来我们写这样的东西:

        var editHandle = PInvoke.FindWindowEx((HWND)proc.MainWindowHandle, 默认, "编辑", null);

        让我们运行它并查看其值。如果它是坚固的0,那么我们就找不到它,但如果它有效,那就太好了。在旧版本的记事本中,该类Edit似乎是窗口本身的子对象,但在新版本中情况并非如此,因为它找不到...

      2. 使用EnumChildWindows所有窗口类,浏览所有类并通过名称找到您需要的类。这就是我要走的路。为此,我将在文件中NativeMethods.txt添加一个新行EnumChildWindows,GetClassName然后编写以下代码:

        private HWND FindHandle(HWND hWnd, params string[] names)
        {
            HWND result = default;
            PInvoke.EnumChildWindows(hWnd, (handle, lParam) =>
            {
                unsafe
                {
                    fixed (char* classNameChars = new char[256])
                    {
                        var length = PInvoke.GetClassName(handle, classNameChars, 256);
                        string className = new string(classNameChars);
                        if (names.Contains(className))
                        {
                            result = handle;
                            return false;
                        }
                    }
                }
        
                return true;
            }, 0);
        
            return result;
        }
        

        如果您知道 WinAPI 的工作原理,该方法相对简单)我们PInvoke.EnumChildWindows通过将窗口处理程序传递给它来进行调用。该方法需要我们有一个具有两个值的委托,即类处理程序及其参数,它将调用。在内部,当调用时,我们调用PInvoke.GetClassName,我们将类标识符传递给它,以及它的名称和该缓冲区的大小将被写入的位置(我使用了 256)。接下来,我们只需将其转换为字符串,如果该名称与搜索到的数组中的名称匹配,则保存结果并通过返回 delegate 来handle完成它。最后我们返回我们收到的东西。EnumChildWindowsfalsehandle

    4. 剩下要做的唯一一件事就是将必要的命令发送到找到的文本字段。在这里我们再次有选择:

      • SendMessage- 发送带有必要数据的“消息”并等待执行。
      • PostMessage- 与上一个类似,但只是“异步”,该函数不等待发送成功。
      • SendInput- 发送“imp”(键盘/鼠标)到活动窗口。在这种情况下,它不适合我们,我带来它只是为了让你了解什么是“引擎盖下” SendKeys.SendWait()。
      • 还有很多其他的品种,比如SendNotifyMessage,SendMessageTimeout等等,那就自己去这个动物园玩玩吧)

      对于这个例子,SendMessage事实上,我们将它添加到文件中NativeMethods.txt并使用必要的参数调用它。

    更多关于SendMessage

    该函数有 4 个参数:

    1. 我们发送它的窗口/类处理程序,一切都清楚了。

    2. 消息本身。这里已经有很多选择,我建议你研究一下。对我们来说有趣的是

      • WM_GETTEXT- 接收文本。
      • WM_SETTEXT- 将文本完全更改为指定文本。
      • WM_CHAR- 发送指定的字符。
      • WM_KEYDOWN- “保持”指定的键。
      • WM_KEYUP- “按”指定的键。

    3 和 4。这些是与特定消息相对应的参数。

    实际上,现在举几个例子:

    逐字符发送文本

    • 添加到NativeMethods.txt新行WM_CHAR。

    代码本身:

    var text = "Hello, world!";
    foreach (char chr in text)
    {
        PInvoke.SendMessage(editHandle, PInvoke.WM_CHAR, chr, 0);
    }
    

    替换窗口中的文本

    • 添加到NativeMethods.txt包含值的行WM_SETTEXT

    代码:

    unsafe
    {
        fixed (char* textPtr = "Новый текст".ToCharArray())
        {
            var res = PInvoke.SendMessage(editHandle, PInvoke.WM_SETTEXT, 0, (IntPtr)textPtr);
        }
    }
    

    “按”键

    • 添加到NativeMethods.txt行WM_KEYDOWN, WM_KEYUP,VIRTUAL_KEY

    例如,转到新行:

    PInvoke.SendMessage(editHandle, PInvoke.WM_KEYDOWN, (uint)VIRTUAL_KEY.VK_RETURN, 0);
    PInvoke.SendMessage(editHandle, PInvoke.WM_KEYUP, (uint)VIRTUAL_KEY.VK_RETURN, 0);
    

    请注意,这不是一个按键,它只是发送一条按键被按下的消息,这就是为什么每个程序都会有不同的行为,但为了以防万一,不要忘记发送“释放”事件。如果您需要直接模拟按键,请返回聚焦窗口并使用SendInput。

    获取窗口文本

    • 在NativeMethods.txt我们添加WM_GETTEXTLENGTH,WM_GETTEXT

    代码:

    var bufferSize = (int)PInvoke.SendMessage(editHandle, PInvoke.WM_GETTEXTLENGTH, 0, 0);
    char[] buffer = new char[bufferSize + 1];
    unsafe
    {
        fixed (char* bufferPtr = buffer)
        {
            PInvoke.SendMessage(editHandle, PInvoke.WM_GETTEXT, (nuint)buffer.Length, (IntPtr)bufferPtr);
            var text = new string(buffer);
        }
    }
    

    下面简要介绍一下 WinAPI 的世界。


    哦,是的,unsafe你可以尝试拒绝,文本替换代码将如下所示:

    var textPtr = Marshal.StringToHGlobalAuto("Текст");
    var res = PInvoke.SendMessage(editHandle, PInvoke.WM_SETTEXT, 0, textPtr);
    Marshal.FreeHGlobal(textPtr);
    

    其余的都是以此类推。好吧,如果您自己编写方法,请不要忘记安全性、优化等。例如,不应使用 StringBuilder(我经常看到使用它的上述函数的示例)。

    • 1

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5