Introduction to the
Electron uses Node-ffi to call the SendMessageW method in the Windows DLL library (user32.dll) to send a Windows message to a Windows window.
To prepare
What is a node – ffi
FFI (Foreign Function Interface) is a cross-language call scheme, in short, my program written in Java can directly call your C++ written Function. Notice that this is a direct call, rather than my Java process sending a message to a C++ process, which calls some function to process my message.
Node-ffi is the implementation of ffI standard under Node
What is a DLL
DLL (Dynamic Link Library) is a shared function Library in Windows. It can be interpreted as a collection of functions packed together in this file, so that the executable file can dynamically Link certain functions.
node-ffi
Let’s start with an official Node-ffi example
var ffi = require('ffi'); Var libm = ffi.library ()'libm', {
'ceil': [ 'double'['double']]}); / / call libm. Ceil (1.5); / / 2Copy the code
The core is the ffi.library () method: The first argument “libm” is the name of the DLL library to load. The second argument is {” function name “:[” return value type “,[” parameter 1 type “, “parameter 2 type “,” parameter n type “]]}, from left to right. The corresponding function declaration in C++ is double ceil(double x);
Once loaded, ceil is a method of the libm variable that can be called directly.
The most core point here is to describe C++ functions in JS, what type is the return value, what type is the input parameter, because there are many types in C++ and can be customized, it is impossible to correspond with JS type one by one, so we need to introduce a library (ref) to help us describe the type in C++.
ref
With the introduction of the ref library, basic types in C++ can be described in JS, and variables holding a type can be obtained directly from ref.types.XXX
void
int8
uint8
int16
uint16
int32
uint32
int64
uint64
float
double
Object
CString
bool
byte
char
uchar
short
int
uint
long
ulong
longlong
ulonglong
Copy the code
In addition, we can describe the pointer type in JS, and we can get the pointer of that type by refType, for example
const CHAR_P = ref.refType(ref.types.char);
Copy the code
This chestnut gets a pointer to type char, which is char *
OK, so now we have primitive types, now we have Pointers, now we can describe most types in C++, but notice that in C/C++ there’s also such a thing as structs, where you can combine multiple types to make new types, for example
typedef struct Sample{
int value;
char* name;
} Node;
Copy the code
For such types, we also need the help of a library
The attached:The document address
ref-struct
The library provides support for the types of structs. See the official examples for a good understanding of structs in C++
struct timeval {
time_t tv_sec; /* seconds since Jan. 1, 1970 */
suseconds_t tv_usec; /* and microseconds */
};
Copy the code
Declaration in node
var ref = require('ref'Var StructType = require(StructType = require(ref))'ref-struct-di'Var suseconds_t = ref.types. Long var suseconds_t = ref.types. Long var suseconds_t = ref.types. Tv_sec: time_t, tv_usec: suseconds_t}) var TV = new timevalCopy the code
Windows messaging
Windows messages can be used as a means of communication between multiple processes. When the user clicks the mouse or presses the keyboard, a specific message is generated, which is placed in the message queue of the application, and the application comes to consume the message and process it accordingly.
The actual operation
Install dependencies
node-ffi
Details: https://github.com/node-ffi-napi/node-ffi-napi
-
node-gyp
npm install -g node-gyp
-
Poke link to download and install Windows build tools check at https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools Visual C++ build tools Visual C++ build tools
-
node-ffi
npm config set msvs_version 2019
npm install ffi-napi
Copy the code
ref-napi
npm install ref-napi
ref-struct-di
npm install ref-struct-di
ref-wchar-napi
npm install ref-wchar-napi
Use SendMessageW and FindWindowW in JS
OK, now that we know how to call a dynamic library (DLL) function in JS, let’s see how to send a Windows message.
Here we can find a function called SendMessageW, which is defined as follows
LRESULT SendMessageW(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
Copy the code
The return value is of LRESULT type, and there are four input parameters: HWND,UINT,WPARAM, and LPARAM.
You can see many types of definitions here
The definition of LRESULT is typedef LONG LRESULT; So we can use LONG for LRESULT and HWND is defined as DECLARE_HANDLE(HWND); DECLARE_HANDLE is a macro
#ifdef STRICT
typedef void *HANDLE;
#define DECLARE_HANDLE() struct name##__ { int unused; }; typedef struct name##__ *name
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif
Copy the code
This concludes with a pointer of type void *. There’s nothing to say about UINT because ref has a definition of WPARAM and the definition of WPARAM is typedef UINT WPARAM; The definition of UINT LPARAM is typedef LONG LPARAM; , is of type LONG
At this point we can describe this function in JS.
const FFI = require('ffi-napi');
const ref = require('ref-napi');
const Struct = require('ref-struct-di')(ref);
const LRESULT = ref.types.long;
const POINTER = ref.refType(ref.types.void);
const UINT = ref.types.uint;
const WPARAM = ref.types.uint;
const LPARAM = ref.types.long;
let User32 = new FFI.Library('user32.dll', {
'SendMessageW': [LRESULT, [POINTER, UINT, WPARAM, LPARAM]]
});
Copy the code
Before sending, we also need to find the handle to the target window. Here we use another function, FindWindowW, to find the window method defined as follows
HWND FindWindowW(
LPCWSTR lpClassName,
LPCWSTR lpWindowName
);
Copy the code
The return value is of type HWND, and the arguments are of type LPCWSTR. The definition is typedef const wchar_t* LPCWSTR; The ref-wchar-nAPI we use provides this type.
Finally, there is an implicit type, COPYDATA, which is a Message structure for Windows
typedef struct tagCOPYDATASTRUCT {
ULONG_PTR dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
Copy the code
DwData of ULONG_PTR type is 32-bit custom data; lpData of PVOID type is a pointer to data; cbData of DWORD type is an integer representing the length of lpData. ULONG_PRT is the pointer type of ULONG,PVOID is the pointer type of void, and DWORD is typedef unsigned long DWORD, which is the type of ULONG
We can describe this data type in JS
const COPYDATA = new Struct({
dwData: ULONG_P,
cbData: ULONG,
lpData: VOID_P
});
Copy the code
At this point, our JS part is ready
const FFI = require('ffi-napi');
const ref = require('ref-napi');
const Struct = require('ref-struct-di')(ref);
const ULONG = ref.types.ulong;
const LRESULT = ref.types.long;
const POINTER = ref.refType(ref.types.void);
const UINT = ref.types.uint;
const UINT_P = ref.refType(UINT);
const ULONG_P = ref.refType(ULONG);
const WPARAM = ref.types.uint;
const LPARAM = ref.types.long;
const VOID_P = POINTER;
const CHAR_P = ref.refType(ref.types.char);
const WCHAR_T = require('ref-wchar-napi');
const WCHAR_T_P = ref.refType(WCHAR_T);
const COPYDATA = new Struct({
dwData: ULONG_P,
cbData: ULONG,
lpData: VOID_P
});
let User32 = new FFI.Library('user32.dll', {
'FindWindowW': [POINTER, [WCHAR_T_P, WCHAR_T_P]],
'SendMessageW': [LRESULT, [POINTER, UINT, WPARAM, ref.refType(LPARAM)]]
});
let hwnd = User32.FindWindowW(null, Buffer.from('win32gui\0'.'ucs2'));
let msg = JSON.stringify("Test message") + '\ 0';
let length = (new TextEncoder().encode(msg)).length;
data = COPYDATA();
data.dwData = ref.NULL;
data.cbData = length;
data.lpData = ref.allocCString(msg);
User32.SendMessageW(hwnd, 0x004a, null, data.ref())
Copy the code
We can directly use user32.findWindoww () to find window handles and user32.sendMessagew () to send messages
Create a window
Create a Win window directly in Python for testing
import win32con, win32api, win32gui, ctypes, ctypes.wintypes
from ctypes import *
class COPYDATASTRUCT(Structure):
_fields_ = [('dwData', POINTER(c_uint)),
('cbData', c_uint),
('lpData', c_char_p)]
PCOPYDATASTRUCT = POINTER(COPYDATASTRUCT)
class Listener:
def __init__(self):
message_map = {
win32con.WM_COPYDATA: self.OnCopyData
}
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = message_map
wc.lpszClassName = 'MyWindowClass'
hinst = wc.hInstance = win32api.GetModuleHandle(None)
classAtom = win32gui.RegisterClass(wc)
self.hwnd = win32gui.CreateWindow (
classAtom,
"win32gui",
0,
0,
0,
win32con.CW_USEDEFAULT,
win32con.CW_USEDEFAULT,
0,
0,
hinst,
None
)
print(self.hwnd)
def OnCopyData(self, hwnd, msg, wparam, lparam):
copydata = ctypes.cast(lparam, PCOPYDATASTRUCT).contents
print (copydata.cbData)
data = copydata.lpData[:copydata.cbData - 1]
print (data.decode('utf-8'))
return 1
l = Listener()
win32gui.PumpMessages()
Copy the code
test
Running our JS file directly sends a message to a Python window, which prints out the length and content of the message
1181360
15
"Test message"
15
"Test message"
15
"Test message"
31
"Chinese-english mixed message test"
Copy the code