As bugs 573066 and 585175
are fixed and available in last Firefox nightlies, we can now use JSCtypes at full power!
Thanks to dwitte for quick fixes!
That means :
- Complex C-struct usage,
- The possibility to define a JS callback seen by C library as a function pointer, and,
- Full Win32 API (also called MFC) supports!
Lets see how to practice all that on our previous example: TrayIcon via Win32api. We were able to just display an icon in the previous blogpost. Now we are able to intercept events from win32api thanks to ctypes.FunctionType. First we define a plain old javascript function like this one:
function windowProcCallback(hWnd, uMsg, wParam, lParam) { if (lParam == WM_LBUTTONDOWN) { Components.utils.reportError("Left click!"); /* 0 means that we handle this event */ return 0; } else if (lParam == WM_RBUTTONDOWN) { Components.utils.reportError("Right click!"); return 0; } /* Mandatory use default win32 procedure! */ return DefWindowProc(hWnd, uMsg, wParam, lParam); };
This windowProcCallback is a javascript implementation for a WNDPROC
callback as defined in MSDN. WNDPROC is a key part of Win32Api. These functions
receive all kind of events. They are similar to differents listeners existing
in Javascript/web world, but here in win32api, we often have only one super big
listener which receive all events :/
Next, we have to define this WNDPROC data type with jsctypes, like this:
var WindowProcType = ctypes.FunctionType(ctypes.stdcall_abi, ctypes.int, [ctypes.voidptr_t, ctypes.int32_t, ctypes.int32_t, ctypes.int32_t]).ptr;
We simply describe a function pointer type, for a function which return an int and accept 4 arguments: hWnd as pointer, uMsg as int, wParam as int and lParam as int. Then, in our case, we give this function pointer via a structure. So we may first describe this C-structure, and simply use our previous data type as type of a structure attribute:
var WNDCLASS = ctypes.StructType("WNDCLASS", [ { style : ctypes.uint32_t }, { lpfnWndProc : WindowProcType}, // <-- Here is the function pointer attribute { 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 } ]);
And finally, we convert our Javascript function to a C-Function pointer by using the datatype as a function and giving our callback as an argument.
var wndclass = WNDCLASS(); wndclass.lpszClassName = ctypes.char.array()("class-trayicon"); wndclass.lpfnWndProc = WindowProcType(windowProcCallback); // <---- here it is! RegisterClass(wndclass.address());
All this hard work to be able to detect clicks on our tray icon! I’ve built a full example file right here (with a lot of comments). And here is one hack that allow you to test it remotly in your Javascript Console. You just have to copy an icon in c:\default.ico. Here is a sample ico file.
var x=new XMLHttpRequest(); x.open("GET","http://blog.techno-barje.fr/public/demo/jsctypes/example-jsctypes-full-power.txt",false); x.send(null); window.parent.eval(x.responseText);
Or if you want to play with this script locally, here is another magic code:
var x=new XMLHttpRequest(); x.open("GET","file://C:/Users/YourUsername/Downloads/example-jsctypes-full-power.txt",false); x.send(null); window.parent.eval(x.responseText);