使用 node-nan 在 node.js 模块中设置 WindowsHookEx

SetWindowsHookEx in a node.js module using node-nan

我正在尝试创建一个 Electron node.js 应用程序,它可以使用全局键绑定执行某些功能。不幸的是,Electron 中的全局键绑定 API 在游戏中不起作用,所以我需要创建一个本地节点模块来监听这些低级键事件。

所以我正在使用 node-gyp 编译带有 visual studio 2015 和 nan 的项目,以提供节点和 c++ 之间的通信。我设法让项目的两个方面分开工作(低级键绑定和 node.js<-->nan 通信),但我无法将它们组合起来。我也承认我对 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 的指针并使其对整个 class 可用,所以当 HookCallback 触发时,它可以向 node.js.

发送消息

编译器不喜欢这样

..\binding.cc(21): error C2440: '=': cannot convert from 'const Nan::AsyncProgressWorker::ExecutionProgress *' to 'Nan: :AsyncProgressWorker::ExecutionProgress *' [C:\Users\eksrow\gdrive\projects\vscode\node-native-hello-world\build\bindin g.vcxproj].

..\binding.cc(21): note: Conversion loses qualifiers

格式化:

'const Nan::AsyncProgressWorker::ExecutionProgress *'

'Nan::AsyncProgressWorker::ExecutionProgress *'

我通过向私有成员 *executionProgress; 添加关键字 const 设法解决了这个问题。但我不明白为什么这会解决它,const 变量一旦设置就不应更改。为什么编译?

问题 #2:这个问题很奇怪:

..\binding.cc(22): error C3867: 'KeyboardEventWorker::HookCallback': non-standard syntax; use '&' to create a pointer t o member [C:\Users\eksrow\gdrive\projects\vscode\node-native-hello-world\build\binding.vcxproj]

我在网上查了很多例子,它们都有相同的语法:

  1. SetWindowsHookEx #1
  2. SetWindowsHookEx #2

关于那一行,我看不出我的代码和他们的代码有什么区别。

如果我按照编译器的指示进行操作并在该行添加一个符号,它会给出一个完全不同的错误:

..\binding.cc(22): error C2276: '&': illegal operation on bound member function expression [C:\Users\eksrow\gdrive\proj ects\vscode\node-native-hello-world\build\binding.vcxproj] ..\binding.cc(22): error C2660: 'SetWindowsHookExA': function does not take 3 arguments [C:\Users\eksrow\gdrive\project s\vscode\node-native-hello-world\build\binding.vcxproj]

对于问题 #1,您正确地将 const 限定词识别为问题所在。

之所以可以在声明后分配给const成员变量,是因为constconst AsyncProgressWorker::ExecutionProgress *executionProgress中的位置。这是一个指向常量 AsyncProgressWorker::ExecutionProgress 的变量指针。这意味着您可以更改指针的值(例如,按照上面的示例重新分配它),但您不能更改它指向的数据。 The top answer of this question对这个概念有很好的解释。

对于问题 #2,错误是由于试图将 class 的成员函数作为函数回调传递而引起的。这是根本不可能的(好吧,没有解决方法......见下文) - 成员方法与函数不同,这是 SetWindowsHookEx 所期望的。您可以创建回调函数 static,但那样就无法访问 _hook 成员。

Here is an external page 有一个(非常 hack-y)解决这个问题的方法。它应该允许您以您当前尝试使用它的方式使用 SetWindowsHookEx。但是,我建议重新考虑您的应用程序的挂钩方式,而不是查看是否有办法将单个全局函数或静态成员函数注册为您的应用程序的回调。

与您的问题无关:除非您在示例中省略了代码,否则您永远不会解开 SetWindowsHookEx 中设置的挂钩。看看 MSDN for UnhookWindowsHookEx.