Question

I have a small bootstrapped extension that loads a dll file at startup() and holds it in a global variable. I don't know exactly how to use this properly maybe you will correct me on this, but I'm more interested in knowing if the functions I use from the dll file can be called asynchronously.

Right now what I'm doing is :

Components.utils.import("resource://gre/modules/ctypes.jsm");
log = function(str) { Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(str+'\n'); }

var lib;
var someFunc;

...

function startup(aData, aReason) {
   lib = ctypes.open(dllPath);
   if(!lib)
   {
      log("dll load error");
      lib = null;
      return;
   }
   else
   {
      var initDll = lib.declare("Init", ctypes.default_abi, ctypes.long);
      var status = initDll();
      log("Dll returned " + status);

      if(status == ctypes.long(0))
      {
         someFunc = lib.declare("SomeFunc", ctypes.default_abi, ctypes.long, ctypes.long);
      }
   }
}

Once I have someFunc I use it in the code quite often, but the call takes a long time. Is there a way to call it asynchronously? Or to call it in such a way that Firefox doesn't freeze for the duration of the call?

Was it helpful?

Solution

If the external (bound via ctypes) API isn't itself asyncrhonous, by definition you're not going to be able to call it asynchronously in JavaScript.

The easiest workaround is to use some kind of WebWorker to call the API in a background thread. Seeing as you need to do some privileged stuff, the ChromeWorker APIs fit the bill.

The mechanics of it are:

  1. Launch the ChromeWorker (a.k.a the 'worker')
  2. Send the 'worker' a message via postMessage to kick off the 'job'
  3. The message event handler in the 'worker' should load ctypes and make the synchronous call to the external code, and then...
  4. Have the 'worker' send a message via postMessage to the caller (your main code) to say (effectively, "I'm done").

If the external API requires call-specific arguments, you could organise to post those via the first postMessage. If the external API returns anything significant, you could organise to receive those via the second postMessage.

All of that will look a bit fiddly, so you'll want to package it into a JavaScript code module; that will allow you to separate the ctypes stuff from the rest of your code, and you can use createEvent and dispatchEvent in the calling thread (inside the module) to make the API seem asynchronous but hide the 'worker' part.

OTHER TIPS

This here shows how to use different functions with jsctypes. It may not be specific to your stuff above, if this doesn't help you out let me know and I'll take a look at your specific case.

const {Cc, Ci, Cu} = require('chrome');
Cu.import("resource://gre/modules/ctypes.jsm");

/*start getcursorpos*/
var lib = ctypes.open("user32.dll");

/*foreground window stuff*/
var FindWindowA = lib.declare('FindWindowA', ctypes.winapi_abi, ctypes.uint32_t, ctypes.jschar.ptr, ctypes.jschar.ptr)
var GetForegroundWindow = lib.declare('GetForegroundWindow', ctypes.winapi_abi, ctypes.uint32_t)
function doFindWindow() {
    var wm = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
    var title = wm.getMostRecentWindow('navigator:browser').gBrowser.contentDocument.title;
    Cu.reportError('title=' + title)
    var ret = FindWindowA('', title + ' - Mozilla Firefox');
    //var ret = GetForegroundWindow();

    Cu.reportError(ret);
}
/*end foreground window stuff*/

/* Declare the signature of the function we are going to call */
const struct_lpPoint = new ctypes.StructType("lpPoint",
                        [ { "x": ctypes.int },
                          { "y": ctypes.int } ]);
var GetCursorPos = lib.declare('GetCursorPos', ctypes.winapi_abi, ctypes.bool, struct_lpPoint.ptr);

function doGetCursorPos() {
        var point = new struct_lpPoint;
        var ret = GetCursorPos(point.address());
        Cu.reportError(ret);
        Cu.reportError(point);
}
/*end getcursorpos*/




/*start setcursorpos*/
//var lib = ctypes.open("user32.dll"); //already called on line 4
var SetCursorPos = lib.declare('SetCursorPos', ctypes.winapi_abi, ctypes.bool, ctypes.int, ctypes.int)

function doSetCursorPos() {
    var ret = SetCursorPos(10, 10);
}
/*end setcursorpos*/

/*start mouse_event*/
//used to click
//const DWORD = ctypes.uint32_t; //this just shows you that DWORD == ctypes.uint32_t
var mouse_event = lib.declare('mouse_event', ctypes.winapi_abi, ctypes.void_t, ctypes.uint32_t, ctypes.uint32_t, ctypes.uint32_t, ctypes.uint32_t, ctypes.uintptr_t);
const MOUSEEVENTF_LEFTDOWN = 2;
const MOUSEEVENTF_LEFTUP = 4;

function domouse_event() {
    var ret = mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
    var ret = mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
/*end mouse_event*/


/*start MessageBoxW*/
//var lib = ctypes.open("user32.dll"); //already called on line 4
var MessageBoxW = lib.declare('MessageBoxW', ctypes.winapi_abi, ctypes.int32_t, ctypes.int32_t, ctypes.jschar.ptr, ctypes.jschar.ptr, ctypes.int32_t);
var MB_OK = 0;

function doMessageBoxW() {
    var ret = MessageBoxW(0, "Hello world", "title", MB_OK);
}

/*end MessageBoxW*/

exports.main = function (options, callbacks) {



};


exports.onUnload = function (reason) {
    lib.close();
}

var { Hotkey } = require("hotkeys");

var showHotKey = Hotkey({
    combo: "alt-w",
    onPress: function() {
        /*setcursor stuff*/
        //doSetCursorPos();
        //domouse_event();
        /*setcursor stuff*/

        /*foreground stuff*/
        doFindWindow();
        /*foreground stuff*/
    }
});

Other jsctype topics on stackoverflow I found useful: * Javascript String to C++ char pointer -LPSTR buffer in JSCTypes * FF addon: How to declare C function fgets in javascript

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top