JSCtypes! What a powerfull
tool, that allows to call native libraries with our simple Javascript.
Jetpack! What a powerfull tool,
that allows to build reliably javascript applications, with unittests, memory
profiling, web IDE, ...
And WinAPI ... a giant C
library still in production in 2010 that allows to do very various things on
Windows platform.
Mix all that and you get:
You may checkout this jetpack package directly from github.
Or if you want to learn jsctypes I suggest you to look at files in lib directory and to read my two previous posts on jsctypes.
- I explained on the first one how to start playing with jsctypes, how to create C-structures and call functions.
- Then I showed in the second post, how to create a JS callback passed to the native library as a function pointer.
That said, I wanted to highlight some underground hacks around win32api! In WinAPI, there is no addEventListener/setEventCallback/addActionListener/... In fact, there is the well known WndProc messages function, that receives absolutely all event of the application!! (Yes for real!) We define this function as a static function named WndProc. But in Jsctypes case, that's impossible to define a static function, we can only create function pointers. That's where comes the not so known hack which allow to register dynamically such event listener.
- First we have to define our listener function following the WinAPI datatypes
Components.utils.import("resource://gre/modules/ctypes.jsm"); var libs = {}; libs.user32 = ctypes.open("user32.dll");
// Define the function pointer type var WindowProcType = ctypes.FunctionType(ctypes.stdcall_abi, ctypes.int, [ctypes.voidptr_t, ctypes.int32_t, ctypes.int32_t, ctypes.int32_t]).ptr;
// Bind a usefull API function var DefWindowProc = libs.user32.declare("DefWindowProcA", ctypes.winapi_abi, ctypes.int, ctypes.voidptr_t, ctypes.int32_t, ctypes.int32_t, ctypes.int32_t);
// Set our javascript callback function windowProcJSCallback(hWnd, uMsg, wParam, lParam) {
// ... do something smart with this event!
// You HAVE TO call this api function when you don't known how to handle an event // or your apply is going to crash or do nothing return DefWindowProc(hWnd, uMsg, wParam, lParam); }
// Retrieve a C function pointer for our Javascript callback var WindowProcPointer = WindowProcType(windowProcJSCallback);
- Then we may fill a WNDCLASS structure with our fresh function pointer. This
structure is used to create a new window class that use it own WndProc
(not the default static function). See msdn doc
for more information.
var WNDCLASS = ctypes.StructType("WNDCLASS", [ { style : ctypes.uint32_t }, { lpfnWndProc : WindowProcType }, // here is our function pointer! { cbClsExtra : ctypes.int32_t }, { cbWndExtra : ctypes.int32_t }, { hInstance : ctypes.voidptr_t }, { hIcon : ctypes.voidptr_t }, { hCursor : ctypes.voidptr_t }, { hbrBackground : ctypes.voidptr_t }, { lpszMenuName : ctypes.char.ptr }, { lpszClassName : ctypes.char.ptr } ]); var wndclass = WNDCLASS(); wndclass.lpszClassName = ctypes.char.array()("class-custom-wndproc"); wndclass.lpfnWndProc = WindowProcType(windowProcCallback); // <---- here it is! RegisterClass(wndclass.address());
- After that we may create a hidden window that is created only to catch
events.
var CreateWindowEx = libs.user32.declare( "CreateWindowExA", ctypes.winapi_abi, ctypes.voidptr_t, ctypes.long, ctypes.char.ptr, ctypes.char.ptr, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.voidptr_t, ctypes.voidptr_t, ctypes.voidptr_t, ctypes.voidptr_t ); var HWND_MESSAGE = -3; // This is the code for message-only window // http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only var win = CreateWindowEx( 0, wndclass.lpszClassName, ctypes.char.array()("messages-only-window"), 0, 0, 0, 0, 0, ctypes.voidptr_t(HWND_MESSAGE), null, null, null);
- Finally, we only have to bind this window to any component which dispatch messages/events in order to receive them in our windowProcJSCallback callback. That's it!
Comments
You can use your Fediverse (i.e. Mastodon, among many others) account to reply to this post
(Note that comments from locked accounts won't be visible on the blog, but only to me)