如何在 windows 表单应用程序中同时 运行 两项任务?

How can I run two tasks at once in windows forms app?

我想寻求有关一些小型 C# 项目的帮助。我有一个 windows 表单应用程序,其目的是编写用户输入的文本。它基本上由一个文本框和一个按钮组成。当您按下按钮时,程序获取文本并跳入循环,在循环中它总是只获取一个符号并使用 SendKeys.Send() 发送它。这没有任何问题,但最近我想添加一个功能,它允许我通过按键停止程序。我被困在这一点上,因为程序在“写入”循环中 运行。我唯一的想法是检查循环内的按键,但除了 KeyPress 事件,我找不到任何其他注册按键的方法。有人知道吗??

我的代码:

public partial class Form1 : Form
{
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
    String textToWrite;
    int delay;
    bool continueWriting = true;

    enum KeyModifier
    {
        None = 0,
        Alt = 1,
        Control = 2,
        Shift = 4,
        WinKey = 8
    }

    public Form1()
    {
        InitializeComponent();
        this.KeyPreview = true;
        int id = 0;
        RegisterHotKey(this.Handle, id, (int)KeyModifier.Shift, Keys.A.GetHashCode());
    }

    private void pisBut_Click(object sender, EventArgs e)
    {
        textToWrite= textToWriteTextBox.Text;
        Console.WriteLine(zadaniText);
        continueWriting = true;
        Thread.Sleep(5000);

        try
        {
            delay = Convert.ToInt32(delayBetweenSymbolsTextBox.Text);
        }
        catch
        {
            Console.WriteLine("conversion failed!");
        }

        for (int i = 0; i < textToWrite.Length; i++) 
        {
            // loop intended to take one char and send it 
            Random nahoda = new Random();
            SendKeys.Send(zadaniText[i].ToString());
            int finalDelay = nahoda.Next(delay - 40, delay);
            Console.WriteLine(finalDelay);
            Thread.Sleep(finalDelay);
            if (continueWriting == false) // stop after setting this bool false 
            {
                break;
            }
        }
    }

    private void zadani_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
        if (e.KeyCode == Keys.Enter)
        {
            // interrupt writing
        }
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == 0x0312)
        {
            Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
            KeyModifier modifier = (KeyModifier)((int)m.LParam & 0xFFFF);
            int id = m.WParam.ToInt32();                                        .
            MessageBox.Show("Hotkey has been pressed!");
            continueWriting = false;
        }
    }

    private void ExampleForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        UnregisterHotKey(this.Handle, 0);
    }
}

谢谢!!

您的问题可能与 UI 线程被重复调用 Thread.Sleep 阻塞有关,使得 UI non-responsive。解决此问题的一种简单方法是使 Click 事件的处理程序异步(添加 async 修饰符),并将 Thread.Sleep 替换为 await Task.Delay。这样 UI 将在发送密钥的整个操作期间保持响应。

private async void Button_Click(object sender, EventArgs e)
{
    //...
    await Task.Delay(5000); // Instead of Thread.Sleep(5000);
    //...
}

这可能会产生另一个问题,即当 send-keys 操作正在进行时用户将能够再次单击该按钮,但我相信您会找到解决此问题的方法。

我会考虑使用 Microsoft 的 Reactive Framework(又名 Rx)- NuGet System.Reactive.Windows.Forms 并添加 using System.Reactive.Linq; - 然后您可以这样做:

private IDisposable _subscription = null;
private void pisBut_Click(object sender, EventArgs e)
{
    if (int.TryParse(delayBetweenSymbolsTextBox.Text, out delay))
    {
        Random random = new Random();
        _subscription =
            Observable
                .Generate(
                    0, x => x < zadaniText.Length, x => x + 1, x => x,
                    x => TimeSpan.FromMilliseconds(x == 0 ? 5000.0 : random.Next(delay - 40, delay)))
                .ObserveOn(this)
                .Subscribe(i => SendKeys.Send(zadaniText[i].ToString()));
    }
}

现在要中途停止发送密钥,您可以这样做:_subscription?.Dispose();