如何暂停代码以使其可以完成其他代码然后恢复

时间:2019-11-05 10:55:54

标签: c# .net wpf dllimport

我想制作一个AI,并使它像玩家一样。当它不知道要做什么时,它会随机移动,我称之为rndPlay();,它将随机选择要单击的位置。它可以从9个位置中选择。 press1();press2();等。重复进行直到连续按下两个按钮。现在是问题了

在执行for循环时,我告诉它按850、400上的按钮,因此我们将其称为btn1并执行btnPressed1();,例如,我看到鼠标在那里移动并且它单击但不执行btnPressed1();。相反,它单击了下一个随机位置,并重复了10000次,因为它没有成功移动(必须按一定顺序按下两件事)。然后,当它退出for循环时,将执行btnPressed1();。我不希望它在停止后再执行此操作,因为每个按钮的顶部是另一个按钮,可以将其命名为btnMove1,并在其下方使用与之对应的数字。例如,它已经按下了按钮1、1和8,它应该在按下btn1之后显示btnMove1,但是由于它不会立即执行,因此它再次按下了btn1(不是按下btnMove1 ),然后btn8,然后在完成时执行btnPressed1();,然后再次执行btnPressed1();,最后执行btnPressed8();,如何使它执行在按下下一件事之前按下的代码? / p>

我尝试使用Thread.Sleep(100)使其停止,但是它只是等待而不执行刚刚按下的按钮。我还使用if语句创建了自己的for循环,因为我认为这可能是for循环阻止了输入的执行。

XAML代码:

<button x:Name="btn1" Click="btnPressed1"/>
<button x:Name="btnMove1" Click="btnMove1" Visibility="Hidden"/>
<button x:Name="btn2" Click="btnPressed2"/>
<button x:Name="btnMove2" Click="btnPressedMove2" Visibility="Hidden"/>
<button x:Name="btn3" Click="btnPressed3"/>
<button x:Name="btnMove3" Click="btnMove3" Visibility="Hidden"/>
//etc

C#代码:

        private void btnPressed1(object sender, RoutedEventArgs e)
        {
            btnMove1.Visibility = Visibility.Visible
        }

        private void btnPressed2(object sender, RoutedEventArgs e)
        {
            btnMove2.Visibility = Visibility.Visible
        }

        //etc

        private void btnPressedMove1(object sender, RoutedEventArgs e)
        {
          //some code
        }

        private void btnPressedMove2(object sender, RoutedEventArgs e)
        {
          //some code
        }

        //etc

        private void MoveTo(int x, int y)
        {
            mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, x, y, 0, 0);
        }

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
        //Mouse actions
        private const int MOUSEEVENTF_LEFTDOWN = 0x02;
        private const int MOUSEEVENTF_LEFTUP = 0x04;
        private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
        private const int MOUSEEVENTF_RIGHTUP = 0x10;

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private extern bool SetCursorPos(uint X, uint Y);
        private void LeftMouseClick(uint X, uint Y)
        {
            SetCursorPos(X, Y);
            //Call the imported function with the cursor's current 
            mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
            Thread.Sleep(1000);
            return;
        }

        private void press1()
        {
            LeftMouseClick(28000, 25000);
            last2Moves("1");
        }
        private void press2()
        {
            LeftMouseClick(33000, 25000);
            last2Moves("2");
        }
        //etc

        private void rndPlay()
        {
            for (int r = 0; moveSucces != true && r < 10000; r++)
            {

                Random rndP = new Random();
                int rndPress = rndP.Next(1, 10);
                if (rndPress == 1)
                {
                    press1();
                }
                if (rndPress == 2)
                {
                    press2();
                }
                    //etc
             }
         }

我希望它在按下下一个按钮之前先执行btnPressed1();,但是它会保存它所按的内容,直到完成代码为止。如何使其在完成下一个操作之前完成代码?

1 个答案:

答案 0 :(得分:1)

具有GUI的应用程序是事件驱动的。所有用户操作(和一些其他事件)都将发布到消息队列中,并由消息循环逐一处理。您可以阅读以下内容以获得一些背景信息: https://docs.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues

如果您编写的代码像for循环那样需要花费很长时间才能执行,那么您将阻止此消息循环处理任何事件。

可以避免阻塞消息循环的一种方法是将rndPlay重写为异步方法:

    private async Task rndPlay()
    {
        for (int r = 0; moveSucces != true && r < 10000; r++)
        {

            Random rndP = new Random();
            int rndPress = rndP.Next(1, 10);
            if (rndPress == 1)
            {
                press1();
            }
            if (rndPress == 2)
            {
                press2();
            }
            //etc

            await Task.Delay(1000);
         }
     }

这具有以下效果:在每个rndPlay暂停await的执行,并在1000ms延迟后将方法执行的继续发布到消息队列中。这样,消息队列中的其他事件就有机会被消息循环处理。

请注意,您的示例代码未显示rndPlay的调用方。您可能还必须在那里进行一些更改。