C # keybd_event и mouse_event используют слишком много ЦП

#c# #visual-studio #performance #mouseevent #keyboard-events

#c# #visual-studio #Производительность #mouseevent #события клавиатуры

Вопрос:

Я пытаюсь самостоятельно запрограммировать запись макросов. У меня проблемы с выяснением, почему моя программа занимает так много ЦП. При записи программы требуется всего около 0,2% использования ЦП. При воспроизведении загрузка достигает примерно 25%.

Для имитации ввода с клавиатуры я вызываю этот код, который открывает новую задачу.

    var kTask = new Task(() => new KeyboardTask(keys_stop[index], keys[index]));
   kTask.Start();
  

Задача вызывает этот класс:

 class KeyboardTask
{

    [DllImport("user32.dll")] static extern short VkKeyScan(char ch);
    // Import the user32.dll
    [DllImport("user32.dll", SetLastError = true)]
    static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);

    // Declare some keyboard keys as constants with its respective code
    // See Virtual Code Keys: https://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
    public const int KEYEVENTF_EXTENDEDKEY = 0x0001; //Key down flag
    public const int KEYEVENTF_KEYUP = 0x0002; //Key up flag
    public const int VK_RCONTROL = 0xA3; //Right Control key code
    public const int VK_LCONTROL = 0xA2; //Left Control key code
    public const int VK_LSHIFT = 0xA0;
    public const int VK_RSHIFT = 0xA1;

    byte key;
    long cooldown;

    long cooldown_time = 500;
    long delay;
    long delay_now;
    bool first_press = false;

    DateTime lastCooldown;
    DateTime start;
    DateTime stop;
    TimeSpan ts;


    public KeyboardTask(long delay, Key run_key)
    {
        key = (byte)KeyInterop.VirtualKeyFromKey(run_key);
        lastCooldown = DateTime.Now;

        this.delay = delay;


        runOtherKeys();




        if (run_key == Key.LeftShift || run_key == Key.RightShift)
        {
            runShift();
        }
        else if (run_key == Key.LeftCtrl)
        {
            runCtrl();
        }



    }


    private void runShift()
    {
        start = DateTime.Now;

        while (delay >= delay_now)
        {
            stop = DateTime.Now;
            ts = (stop - start);
            delay_now = (long)ts.TotalMilliseconds;
            if (!first_press)
            {
                keybd_event(key, 0, KEYEVENTF_EXTENDEDKEY, 0);
                first_press = true;
            }
        }
        keybd_event(VK_LSHIFT, 0, KEYEVENTF_KEYUP, 0);
        keybd_event(VK_RSHIFT, 0x45, KEYEVENTF_KEYUP, 0);

    }

    private void runCtrl()
    {
        start = DateTime.Now;

        while (delay >= delay_now)
        {
            stop = DateTime.Now;
            ts = (stop - start);
            delay_now = (long)ts.TotalMilliseconds;
            if (!first_press)
            {
                keybd_event(VK_LCONTROL, 0x9d, 0, 0);
                first_press = true;
            }

        }
        keybd_event(VK_LCONTROL, 0x9d, KEYEVENTF_KEYUP, 0);



    }

    private void runOtherKeys()
    {
        start = DateTime.Now;

        while (delay >= delay_now)
        {
            stop = DateTime.Now;
            ts = (stop - start);
            delay_now = (long)ts.TotalMilliseconds;


            if (first_press)
            {
                cooldown = getCooldown();
                if (cooldown >= cooldown_time)
                {
                    lastCooldown = DateTime.Now;
                    cooldown_time = 33;


                    keybd_event(key, 0x45, 0, 0);
                    keybd_event(key, 0x45, KEYEVENTF_KEYUP, 0);


                }
            }
            else
            {
                lastCooldown = DateTime.Now;
                keybd_event(key, 0x45, 0, 0);
                keybd_event(key, 0x45, KEYEVENTF_KEYUP, 0);
                first_press = true;
            }



        }

        keybd_event(key, 0x45, KEYEVENTF_KEYUP, 0);

    }
    private long getCooldown()
    {
        DateTime cooldown_stop = DateTime.Now;
        TimeSpan cooldown_timespan = (cooldown_stop - lastCooldown);
        return (long)cooldown_timespan.TotalMilliseconds;

    }

}
  

И для имитации ввода с помощью мыши у меня есть этот код здесь:

 public void Start()
{
     Task.Run(() => runRecordMouse());      
} 


private void runRecordMouse()
    {

        DateTime start = DateTime.Now;
        DateTime stop;
        TimeSpan ts;
        long time;
        int count = 0;
        int max = x_arr.Length;
        long delay = delay_arr_mouse[count];

        while (count < max)
        {
            
         stop = DateTime.Now;
         ts = (stop - start);
         time = (long)ts.TotalMilliseconds;

         if (delay <= time)
          {

                    performMouseEvent(count);
                    count  ;
                    if (count != max) delay = delay_arr_mouse[count];
                    start = DateTime.Now;
                }
            
        }

    }


    private void performMouseEvent(int pos)
    {

        mouse_button = mouse_click_arr[pos];
        x = x_arr[pos];
        y = y_arr[pos];

        if (mouse_button_pre != mouse_button amp;amp; mouse_button_pre == 1)
        {
            mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0);
        }
        if (mouse_button_pre != mouse_button amp;amp; mouse_button_pre == 2) mouse_event(MOUSEEVENTF_RIGHTUP, x, y, 0, 0);

        switch (mouse_button)
        {
            case 0:
                SetCursorPos(x, y); //move
                break;

            case 1:

                if (mouse_button_pre == mouse_button)
                {
                    Drag(x_arr[pos - 1], y_arr[pos - 1], x, y); //left Click drag
                }
                else
                {
                    mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0); //left Click
                }

                break;

            case 2:
                mouse_event(MOUSEEVENTF_RIGHTDOWN, x, y, 0, 0); //right Click
                break;


        }


        mouse_button_pre = mouse_button;

    }

    private static void Drag(int xStart, int yStart, int x, int y)
    {
        
        x = x - xStart;
        y = y - yStart;
        SetCursorPos(xStart, yStart);
        mouse_event(MOUSEEVENTF_MOVE, x, y, 0, 0);
    }
  

Профилировщик производительности

Он написан на C # .NET Framework.

МОЙ КОМПЬЮТЕР: ПРОЦЕССОР: AMD Ryzen 5 3600 ОПЕРАТИВНАЯ память: 32 ГБ Графический процессор: AMD Radeon RX 5700

Комментарии:

1. Я думаю, потому что у вас есть цикл while без сна или интервала.

2. @Luckylazuli Вау, я действительно глуп. Я не думал, что это может оказать такое большое влияние на производительность. Спасибо

Ответ №1:

Я только что добавил

 Thread.Sleep(1);
  

для всех циклов while, и теперь программа использует только около 0,5% использования ЦП.

Комментарии:

1. ОК. Неплохо. Тогда я бы предложил переписать функцию, чтобы уйти от while.

2. Да, я забыл пару циклов while и мог сэкономить 400% мощности процессора. Я кое-чему научился там на всю жизнь.

3. Возможно, вы могли бы рассмотреть System. Многопоточность. Таймер или система. Таймеры . Я желаю вам больших успехов.