Я пытаюсь создать приложение Electron node.js, которое может выполнять определенные функции, используя глобальные привязки клавиш. К сожалению, глобальный API-интерфейс связывания клавиш в Electron не работает в играх, поэтому мне нужно создать собственный модуль узла, который будет прослушивать эти события клавиш низкого уровня.
Поэтому я использую node-gyp для компиляции проекта с Visual Studio 2015 и Nan, чтобы обеспечить связь между узлом и c ++. Мне удалось заставить оба аспекта проекта работать отдельно (низкоуровневые привязки клавиш и node.js).<-> Нан связи) но у меня возникли проблемы с их объединением. Я также признаю, что у меня очень мало опыта работы с c ++ (я не написал ни одной программы на c ++).
#include "node_modules/nan/nan.h"
using namespace std;
using namespace Nan;
HHOOK _hook;
KBDLLHOOKSTRUCT kbdStruct;
class KeyboardEventWorker : public AsyncProgressWorker {
public:
KeyboardEventWorker(Callback *callback, Callback *progress)
: AsyncProgressWorker(callback), progress(progress) {}
~KeyboardEventWorker() {}
LRESULT CALLBACK HookCallback(int nCode,WPARAM wParam,LPARAM lParam) {
executionProgress->Send(reinterpret_cast<const char*>(nCode), sizeof(nCode));
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
void Execute (const AsyncProgressWorker::ExecutionProgress& progress) {
executionProgress = &progress; //PROBLEM #1
_hook = SetWindowsHookEx(13, HookCallback, NULL, 0); //PROBLEM #2
SleepEx(INFINITE, true);
}void HandleProgressCallback(const char *data, size_t size) {
HandleScope scope;
v8::Local<v8::Value> argv[] = {
New<v8::Integer>(*reinterpret_cast<int*>(const_cast<char*>(data)))
};
progress->Call(1, argv);
}
private:
Callback *progress;
AsyncProgressWorker::ExecutionProgress *executionProgress;
};
NAN_METHOD(DoProgress) {
Callback *progress = new Callback(info[0].As<v8::Function>());
Callback *callback = new Callback(info[1].As<v8::Function>());
AsyncQueueWorker(new KeyboardEventWorker(callback, progress));
}
NAN_MODULE_INIT(Init) {
Set(target
, New<v8::String>("init").ToLocalChecked()
, New<v8::FunctionTemplate>(DoProgress)->GetFunction());
}
NODE_MODULE(asyncprogressworker, Init)
Проблема № 1: Чтобы иметь возможность отправлять обратно сообщения на node.js, мне нужно скопировать указатель AsyncProgressWorker :: ExecutionProgress и сделать его доступным для всего класса, чтобы при запуске HookCallback он мог отправить сообщение на node.js ,
Компилятору это не нравится
..\ binding.cc (21): ошибка C2440: ‘=’: невозможно преобразовать из ‘const
Nan :: AsyncProgressWorker :: ExecutionProgress * ‘to’ Nan:
: AsyncProgressWorker :: ExecutionProgress * ‘
[C: \ Users \ eksrow \ GDrive \ Проекты \ vscode \ узел-нативной привет-мир \ сборка \ bindin
g.vcxproj]...\ binding.cc (21): примечание: конверсия теряет квалификаторы
отформатирован:
‘const Nan :: AsyncProgressWorker :: ExecutionProgress *’
‘Nan :: AsyncProgressWorker :: ExecutionProgress *’
Эту проблему мне удалось решить, добавив ключевое слово const в приватный член * executeProgress ;. Но я не понимаю, почему это все исправит, переменные const не должны изменяться после их установки. Почему это компилируется?
Проблема № 2: Эта очень необычная:
..\ binding.cc (22): ошибка C3867: ‘KeyboardEventWorker :: HookCallback’:
нестандартный синтаксис; использовать&создать указатель на член
[C: \ Users \ eksrow \ GDrive \ Проекты \ vscode \ узел-нативной привет-мир \ сборка \ binding.vcxproj]
Я посмотрел много примеров в Интернете, и все они имеют одинаковый синтаксис относительно этого:
Я не вижу разницы между моим кодом и их в отношении этой строки.
Если я делаю то, что говорит компилятор, и добавляю амперсанд в эту строку, это дает совершенно другую ошибку:
..\ binding.cc (22): ошибка C2276: ‘&’: незаконная операция на связанном члене
выражение функции [C: \ Users \ eksrow \ gdrive \ proj
ЕКТС \ vscode \ узел-нативной привет-мир \ сборка \ binding.vcxproj] .. \ binding.cc (22): ошибка C2660: ‘SetWindowsHookExA’: функция не работает
принять 3 аргумента [C: \ Users \ eksrow \ gdrive \ project
s \ vscode \ узел-нативной привет-мир \ сборка \ binding.vcxproj]
Для задачи № 1, Вы правильно определили const
классификатор как проблема.
Причину, которую вы можете указать const
переменная-член после объявления этого из-за размещения const
в const AsyncProgressWorker::ExecutionProgress *executionProgress
, Это указатель на переменную AsyncProgressWorker :: ExecutionProgress. Это означает, что вы можете изменить значение указателя (например, переназначить его, как в примере выше), но вы не можете изменить данные, на которые он указывает. Лучший ответ на этот вопрос имеет очень хорошее объяснение этой концепции.
Для задачи № 2, ошибка вызвана попыткой передать функцию-член вашего класса как функцию обратного вызова. Это просто невозможно (ну, без обходного пути … см. Ниже) — метод-член — это не то же самое, что функция. SetWindowsHookEx
ожидает. Вы могли бы сделать вашу функцию обратного вызова static
, но тогда не сможет получить доступ к _hook
член.
Вот внешняя страница с (очень хакерским) обходным решением для этой точной проблемы. Это должно позволить вам использовать SetWindowsHookEx
так, как вы сейчас пытаетесь его использовать. Однако я бы рекомендовал переосмыслить способ подключения вашего приложения и вместо этого посмотреть, есть ли способ зарегистрировать одну глобальную функцию или статическую функцию-член в качестве обратного вызова для вашего приложения.
Не имеет отношения к вашему вопросу: если вы не пропустили код в своем примере, вы никогда не отцепите хук, установленный в SetWindowsHookEx
, Посмотрите на MSDN для UnhookWindowsHookEx
.
Других решений пока нет …