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:
A jetpack API for adding Tray icons on windows via jsctypes and on linux with a
binary xpcom component (I didn’t had time to work on a jsctypes version).
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!