windows .h INPUT не работает вне функций - PullRequest
0 голосов
/ 23 января 2020

Спецификации

  • ОС: Windows 10
  • Язык программирования: C ++ 14
  • Компилятор: MSV C 2019
  • IDE: CLion 2019.3.3

Код:

    #define WINVER 0x0500
    #include <windows.h>
    #include <string>

    void press_enter() {
        // This structure will be used to create the keyboard
        // input event.
        INPUT ip;

        // Set up a generic keyboard event.
        ip.type = INPUT_KEYBOARD;
        ip.ki.wScan = 0; // hardware scan code for key
        ip.ki.time = 0;
        ip.ki.dwExtraInfo = 0;


        // Press enter
        ip.ki.wVk = 0x0D;
        ip.ki.dwFlags = 0; // 0 for key press
        SendInput(1, &ip, sizeof(INPUT));

        // Release the key
        ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
        SendInput(1, &ip, sizeof(INPUT));
        Sleep(25);

    }

    void press_keys(std::string& text_to_write) {
        // This structure will be used to create the keyboard
        // input event.
        INPUT ip;

        // Load current window's keyboardLayout
        HKL kbl = GetKeyboardLayout(0);

        // Set up a generic keyboard event.
        ip.type = INPUT_KEYBOARD;
        ip.ki.wScan = 0; // hardware scan code for key
        ip.ki.time = 0;
        ip.ki.dwExtraInfo = 0;

        for (char& c : text_to_write) {
            // Press the corresponding 'c' key
            ip.ki.wVk = VkKeyScanEx(c, kbl);; // virtual-key code for the "a" key
            ip.ki.dwFlags = 0; // 0 for key press
            SendInput(1, &ip, sizeof(INPUT));

            // Release the key
            ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
            SendInput(1, &ip, sizeof(INPUT));
            Sleep(25);
        }
    }

    void give_100000(std::string& item) {
        for (int i = 0; i < 10; i++) {
            press_keys(item);
            Sleep(25);
            press_enter();
            Sleep(25);
            press_enter();
            Sleep(25);
        }
    }

    int main() {

        // Pause for 5 seconds.
        Sleep(5000);

        std::string lumber = "lumberjack";
        std::string food = "cheese steak jimmy's";
        std::string gold = "robin hood";
        std::string stone = "rock on";

        give_100000(lumber);
        give_100000(food);
        give_100000(gold);
        give_100000(stone);

        // Exit normally
        return 0;
    }

Что эта программа делает

Я все еще очень начинающий в C ++. Я написал эту программу как маленькую задачу и попрактиковался в C ++. Он имитирует нажатия клавиш, особенно для быстрого набора читов, так что я получаю кучу ресурсов в Age of Empires II.

Проблема

Этот код отлично работает как есть. Он делает то, что я хочу. Дело в том, что внутри функций press_enter() и press_keys() есть повторяющийся код, а именно:

INPUT ip;

// Set up a generic keyboard event.
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0; // hardware scan code for key
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;

Итак, я хотел это исправить.

Что я пробовал

Я подумал, что могу просто вывести этот фрагмент кода за пределы всех функций (прямо под #include) и заставить их действовать как глобальные переменные, чтобы ip был доступен для всех функций. Но это вызвало недовольство CLion, а компиляция дала мне гигантский список c непонятных ошибок (я могу при необходимости опубликовать). Когда я наведите курсор на любую из 4 строк, начинающихся с ip., CLion говорит: «Неизвестное имя типа« ip »». Я не понимаю этого, поскольку ip было объявлено буквально на 2 строки выше.

Что я ищу

Как я уже сказал, я все еще новичок в C ++, поэтому я очень хотел бы понять, что это значит, если мне не хватает какой-то фундаментальной концепции и способа заставить его работать без ненужного повторения кода.

Ответы [ 3 ]

1 голос
/ 23 января 2020

А почему вы получили ошибку «Неизвестный тип», это потому, что вы пытаетесь запустить код там, где он может быть только объявлен.

Внутри функции вы запускаете код и место, где вы можете изменить поведение инициализированного типа.

0 голосов
/ 23 января 2020

Итак, из того, что я собрал здесь и на других сайтах, я совершил ошибку, пытаясь запустить оператор вне функции. Здесь есть и другие замечательные предложения, которые я рекомендую всем, у кого есть вопросы, аналогичные тем, которые я читал, но с помощью человека ( Теда Бёрка ), чей код я основал на своем, я Я считаю, что я достиг того, чего изначально хотел. Тед порекомендовал мне объявить ip глобально и одновременно инициализировать его следующим образом:

INPUT ip = {.type = INPUT_KEYBOARD, .ki.wScan = 0, .ki.time = 0, .ki.dwExtraInfo = 0};

Я попытался, но компилятор выдал мне ошибки, поэтому я переписал это так:

(ошибки компилятора меня смущали, поэтому бонус указывает на того, кто может объяснить, почему следующий код работал, а Тед не работал)

TL; DR Solution

INPUT ip = {ip.type = INPUT_KEYBOARD,
            ip.ki.wScan = 0,
            static_cast<LONG>(ip.ki.time = 0),
            ip.ki.dwExtraInfo = 0};

Причина этого static_cast<LONG> заключается в том, что CLion обнаружил следующее: «Невыраженное выражение не может быть сужено от типа« DWORD »(он же« unsigned long ») до« LONG »(он же« long ») в списке инициализатора. ".

Мне нравится это решение, потому что таким образом нам не нужно создавать экземпляр объекта INPUT для каждого нажатия клавиши. Я знаю, что в этом случае прирост производительности как во времени, так и в памяти, вероятно, будет незначительным (возможно, даже благодаря тому, что компилятор оптимизировал мой первый код, это даже не было необходимо?), Но с логической точки зрения, мне было ненужно Повторите создание экземпляра INPUT сотни раз, когда его можно создать только один раз.

Я вижу, что я могу использовать SendInput не в лучшем образце, и что я могу просто использовать press_keys("\r") и избавиться от press_enter(). Я посмотрю на эти изменения, чтобы улучшить свой код, но сейчас моя основная задача была выполнена. Спасибо всем!

Это финальная версия моего кода:

#define WINVER 0x0500
#include <windows.h>
#include <string>

INPUT ip = {ip.type = INPUT_KEYBOARD,
            ip.ki.wScan = 0,
            static_cast<LONG>(ip.ki.time = 0),
            ip.ki.dwExtraInfo = 0};
HKL kbl = GetKeyboardLayout(0);

void press_enter() {
    // Press enter
    ip.ki.wVk = 0x0D;
    ip.ki.dwFlags = 0; // 0 for key press
    SendInput(1, &ip, sizeof(INPUT));

    // Release the key
    ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
    SendInput(1, &ip, sizeof(INPUT));
    Sleep(25);
}

void press_keys(std::string& text_to_write) {
    for (char& c : text_to_write) {
        // Press the corresponding 'c' key
        ip.ki.wVk = VkKeyScanEx(c, kbl);; // virtual-key code for the "a" key
        ip.ki.dwFlags = 0; // 0 for key press
        SendInput(1, &ip, sizeof(INPUT));

        // Release the key
        ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
        SendInput(1, &ip, sizeof(INPUT));
        Sleep(25);
    }
}

void give_100000(std::string& item) {
    for (int i = 0; i < 10; i++) {
        press_keys(item);
        Sleep(25);
        press_enter();
        Sleep(25);
        press_enter();
        Sleep(25);
    }
}

int main() {
    // Pause for 5 seconds.
    Sleep(5000);

    std::string lumber = "lumberjack";
    std::string food = "cheese steak jimmy's";
    std::string gold = "robin hood";
    std::string stone = "rock on";

    give_100000(lumber);
    give_100000(food);
    give_100000(gold);
    give_100000(stone);

    // Exit normally
    return 0;
}
0 голосов
/ 23 января 2020

Дело в том, что внутри функций press_enter() и press_keys() есть повторяющийся код

Если вы хотите повторно использовать один и тот же экземпляр INPUT, передайте его по ссылке функции, которые в этом нуждаются - старайтесь держаться подальше от глобальных переменных.

Также обратите внимание, что отправка одиночных событий клавиатуры таким способом не рекомендуется . Вы можете создать вспомогательный класс для хранения информации, которую вам нужно инициализировать только один раз, и добавить функцию для отправки полных последовательностей событий ( клавиша вниз / вверх ). Затем его легко расширить другими функциями для специального сценария ios.

Пример:

#define WINVER 0x0500
#include <windows.h>
#include <string>
#include <vector>

struct KeyboardInput {
    KeyboardInput() :
        kbl{ GetKeyboardLayout(0) }
    {}

    // the basic function to send a number of keys at once
    void press_keys(const std::string& text_to_write) {
        // create a vector of INPUT structs, double the size of the string
        // to be able to send both key down and key up events
        std::vector<INPUT> INPUTs(text_to_write.size() * 2);
        SHORT wVk;

        // loop though all characters in the string and fill the INPUT structs
        size_t idx = 0;
        for(auto ch : text_to_write) {
            wVk = KeyScan(ch);

            // fill the INPUT struct with data
            fill_input(INPUTs[idx], wVk, 0); // KEYDOWN
            fill_input(INPUTs[idx + 1], wVk, KEYEVENTF_KEYUP);

            idx += 2;
        }

        // Call SendInput once for the complete string
        SendInput(static_cast<UINT>(INPUTs.size()), INPUTs.data(), sizeof(INPUT));
    }

    // a press_keys overload with a delay between keystrokes
    void press_keys(const std::string& text_to_write, DWORD delay_ms) {
        for (auto ch : text_to_write) {
            press_keys(std::string(1, ch));
            Sleep(delay_ms);
        }
    }

    // functions using the above
    void give_100000(const std::string& item) {
        press_keys(item + "\n\n", 250); // sleep 250 ms between each keystroke
    }
    void press_enter() {
        press_keys("\n");
    }
private:
    SHORT KeyScan(TCHAR ch) {
        return VkKeyScanEx(ch, kbl);
    }
    void fill_input(INPUT& i, SHORT wVk, DWORD dwFlags) {
        i.type = INPUT_KEYBOARD;
        i.ki.wVk = wVk;
        i.ki.dwFlags = dwFlags;
        i.ki.time = 0;
        i.ki.dwExtraInfo = 0;
    }

    HKL kbl;
};

int main() {
    Sleep(4000);

    std::vector<std::string> messages{
        "lumberjack",
        "cheese steak jimmy's",
        "robin hood",
        "rock on"
    };

    KeyboardInput ip;

    for (const std::string& msg : messages) {
        ip.give_100000(msg);
    }
} // Exit normally (return 0 is default)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...