Using js-ctypes

  • Revision slug: Mozilla/js-ctypes/Using_js-ctypes
  • Revision title: Using js-ctypes
  • Revision id: 59047
  • Created:
  • Creator: kmaglione
  • Is current revision? No
  • Comment 34 words added, 5 words removed

Revision Content

{{ gecko_minversion_header("2.0") }}

{{ draft() }}

Before you can use js-ctypes, you need to import the ctypes.jsm code module. This is as simple as including the following line of code in the desired JavaScript scope:

Components.utils.import("resource://gre/modules/ctypes.jsm")

Loading a native library

Once you've imported the code module, you can call the ctypes.open() method to load each native library you wish to use. On Windows, for example, you might load the system user32 library like this:

var lib = ctypes.open("C:\\WINDOWS\\system32\\user32.dll");

On Mac OS X, you can load the Core Foundation library from the Core Foundation framework like this:

var coreFoundation = ctypes.open("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");

The returned object is a Library object that you use to declare functions and data types for use with the loaded library.

Note: js-ctypes only works with C libraries; you can't use C++ methods directly. Instead, you'll need to create a shim library that uses C functions that then call into the C++ library for you.

Library search paths

If you specify a full path, that path is used to load the library. Otherwise, the system looks for the library in its standard locations.

Windows

On Windows, the following locations are searched for the library, in this order:

  1. The application's directory.
  2. The system directory.
  3. The 16-bit system directory.
  4. The Windows directory.
  5. The current working directory
  6. The directories listed in the PATH environment variable.
Note: This information comes from this article on MSDN.

After you're done

When you're finished using a library, you need to close it by calling the Library object's close() method:

lib.close();

Using the library

You may need to declare new types. These can be simple types or more complex types such as structures. See Declaring types for details. You will almost certainly need to declare one more more functions, so that you can call them.

Once you've declared the types and functions, you can write your code to make use of them. Instantiating C data objects and referencing them is covered in the article Working with data.

Memory management

If JS code creates a structure or array, that memory will be valid as long as the JS object stays alive. Pointers to that memory must be carefully managed to make sure the underlying memory is still referenced.

When binary code hands back a pointer/handle to allocated memory, the JS code must make sure to free that memory with the correct allocator. It is usually best to expose a freeing function from the binary.

Keeping objects alive

The following js-ctypes objects will hold references to objects, keeping them alive. This is not an exhaustive list, but will help you understand memory management and how it affects your use of js-ctypes:

  • A function or static data declared using the declare() method will hold that library alive.
  • A CType will hold referent CType objects alive.
  • A CData will hold referent CData objects alive, in specific circumstances. For example, a CData object produced by accessing a field of a structure or the internals of an array will hold the referent objects alive.

What won't keep objects alive

It's important to note that getting direct access to the contents of a CData object using address(), addressOfElement(), or contents, will result in a CData object that does not hold its referent alive. Be sure to hold an explicit reference to be sure the referent object doesn't get garbage collected before you're done using it.

Closures

You also need to be sure to retain references to any JavaScript code that native code may call back into. This should be obvious, but is important enough to be worth stating explicitly.

When in doubt, malloc()

When you absolutely, positively need to keep data around, you can use malloc() to allocate it directly. This bypasses JavaScript's memory management and lets you handle memory management yourself.

Examples

These examples offer a quick look at how js-ctypes is used. See js-ctypes examples for more intricate examples.

Calling Windows routines

This example demonstrates how to use ctypes to call a Win32 API.

Components.utils.import("resource://gre/modules/ctypes.jsm");

var lib = ctypes.open("C:\\WINDOWS\\system32\\user32.dll");

/* Declare the signature of the function we are going to call */
var msgBox = 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;

var ret = msgBox(0, "Hello world", "title", MB_OK);

lib.close();

In line 3, the user32.dll system library is loaded. Line 6 declares msgBox() to be a method that calls the Windows function MessageBoxW. Line 15 calls the msgBox() routine, which displays the alert.

The last thing we do is call lib.close() to close the library when we're done using it.

Instead of defining the whole path, you may also just give the file name.

var lib = ctypes.open("user32.dll");

Or even without the extension.

var lib = ctypes.open("user32");

Calling Carbon routines on Mac OS X

This example demonstrates how to use ctypes to call a Carbon function on Mac OS X.

/* build a Str255 ("Pascal style") string from the passed-in string */

function makeStr(str) {
  return String.fromCharCode(str.length) + str;
}

Components.utils.import("resource://gre/modules/ctypes.jsm");

var carbon = ctypes.open("/System/Library/Frameworks/Carbon.framework/Carbon");

stdAlert = carbon.declare("StandardAlert",       /* function name */
                          ctypes.default_abi,    /* ABI type */
                          ctypes.int16_t,        /* return type */
                          ctypes.int16_t,        /* alert type */
                          ctypes.char.ptr,       /* primary text */
                          ctypes.char.ptr,       /* secondary text */
                          ctypes.uint32_t,       /* alert param */
                          ctypes.int16_t);       /* item hit */

var hit = 0;
var msgErr = makeStr("Carbon Says...");
var msgExp = makeStr("We just called the StandardAlert Carbon function from JavaScript!");

var err = stdAlert(1, msgErr, msgExp, 0, hit);

carbon.close();

The makeStr() function is a utility routine that takes as input a standard JavaScript string and returns a Carbon-style "Pascal" string, which is a length byte followed by the characters of the string itself. Note that this only works correctly if the string is in fact under 256 characters; if it's longer, this will fail spectacularly.

In line 9, the Carbon library is loaded from the system's Carbon framework.

Line 11 declares the stdAlert() function, which will call the Carbon StandardAlert routine. It uses the default ABI, returns a 16-bit integer (which is a Carbon OSErr value), and accepts an integer (the alert type), two strings, a pointer to a parameter block, which we aren't using, and another integer, which is used to return the hit item. See Apple's documentation for StandardAlert for details.

After that, we simply set up our parameters by using makeStr() to generate the two Str255 strings we need, then call stdAlert(), which produces the following alert window:

ctype-mac-dialog.png

The last thing we do is call carbon.close() to close the library when we're done using it.

Calling LibC routines on Linux/POSIX

This example demonstrates how to use ctypes to call a libc function on Linux.

/* import js-ctypes */
Components.utils.import("resource://gre/modules/ctypes.jsm");

/* Open the library */
try {
    /* Linux */
    var libc = ctypes.open("libc.so.6");
} catch (e) {
    /* Most other Unixes */
    libc = ctypes.open("libc.so");
}

/* Import a function */
var puts = libc.declare("puts",             /* function name */
                        ctypes.default_abi, /* call ABI */
                        ctypes.int_t,       /* return type */
                        ctypes.char.ptr);   /* argument type */

var ret = puts("Hello World from js-ctypes!");

Revision Source

<p>{{ gecko_minversion_header("2.0") }}</p>
<p>{{ draft() }}</p>
<p>Before you can use js-ctypes, you need to import the ctypes.jsm code module. This is as simple as including the following line of code in the desired JavaScript scope:</p>
<pre class="eval"><span class="nowiki">Components.utils.import("resource://gre/modules/ctypes.jsm")</span></pre>
<h2>Loading a native library</h2>
<p>Once you've imported the code module, you can call the <a href="/en/js-ctypes/js-ctypes_reference/ctypes#open()" title="en/js-ctypes/js-ctypes reference/ctypes#open()"><code>ctypes.open()</code></a> method to load each native library you wish to use. On Windows, for example, you might load the system user32 library like this:</p>
<pre>var lib = ctypes.open("C:\\WINDOWS\\system32\\user32.dll");
</pre>
<p>On Mac OS X, you can load the Core Foundation library from the Core Foundation framework like this:</p>
<pre>var coreFoundation = ctypes.open("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
</pre>
<p>The returned object is a Library object that you use to declare functions and data types for use with the loaded library.</p>
<div class="note"><strong>Note:</strong> js-ctypes only works with C libraries; you can't use C++ methods directly. Instead, you'll need to create a shim library that uses C functions that then call into the C++ library for you.</div>
<h3>Library search paths</h3>
<p>If you specify a full path, that path is used to load the library. Otherwise, the system looks for the library in its standard locations.</p>
<h4>Windows</h4>
<p>On Windows, the following locations are searched for the library, in this order:</p>
<ol> <li>The application's directory.</li> <li>The system directory.</li> <li>The 16-bit system directory.</li> <li>The Windows directory.</li> <li>The current working directory</li> <li>The directories listed in the <code>PATH</code> environment variable.</li>
</ol>
<div class="note"><strong>Note:</strong> This information comes from <a class=" external" href="http://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx" title="http://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx">this article on MSDN</a>.</div>
<h2>After you're done</h2>
<p>When you're finished using a library, you need to close it by calling the <code>Library</code> object's <a href="/en/js-ctypes/js-ctypes_reference/Library#close()" title="en/js-ctypes/js-ctypes reference/Library#close()"><code>close()</code></a> method:</p>
<pre>lib.close();
</pre>
<h2>Using the library</h2>
<p>You may need to declare new types. These can be simple types or more complex types such as structures. See <a href="/en/js-ctypes/Using_js-ctypes/Declaring_types" title="en/js-ctypes/Using js-ctypes/Declaring types">Declaring types</a> for details. You will almost certainly need to <a href="/en/js-ctypes/Using_js-ctypes/Declaring_and_calling_functions" title="en/js-ctypes/Using js-ctypes/Declaring and calling functions">declare one more more functions</a>, so that you can call them.</p>
<p>Once you've declared the types and functions, you can write your code to make use of them. Instantiating C data objects and referencing them is covered in the article <a href="/en/js-ctypes/Using_js-ctypes/Working_with_data" title="en/js-ctypes/Using js-ctypes/Working with data">Working with data</a>.</p>
<h2>Memory management</h2>
<p>If JS code creates a structure or array, that memory will be valid as long as the JS object stays alive. Pointers to that memory must be carefully managed to make sure the underlying memory is still referenced.</p>
<p>When binary code hands back a pointer/handle to allocated memory, the JS code must make sure to free that memory with the correct allocator. It is usually best to expose a freeing function from the binary.</p>
<h3>Keeping objects alive</h3>
<p>The following js-ctypes objects will hold references to objects, keeping them alive. This is not an exhaustive list, but will help you understand memory management and how it affects your use of js-ctypes:</p>
<ul> <li>A function or static data declared using the <a href="/en/js-ctypes/js-ctypes_reference/Library#declare()" title="en/js-ctypes/js-ctypes reference/Library#declare()"><code>declare()</code></a> method will hold that library alive.</li> <li>A <a href="/en/js-ctypes/js-ctypes_reference/CType" title="en/js-ctypes/js-ctypes reference/CType"><code>CType</code></a> will hold referent <a href="/en/js-ctypes/js-ctypes_reference/CType" title="en/js-ctypes/js-ctypes reference/CType"><code>CType</code></a> objects alive.</li> <li>A <code><a href="/en/js-ctypes/js-ctypes_reference/CData" title="en/js-ctypes/js-ctypes reference/CData">CData</a></code> will hold referent <code><a href="/en/js-ctypes/js-ctypes_reference/CData" title="en/js-ctypes/js-ctypes reference/CData">CData</a></code> objects alive, in specific circumstances. For example, a <code><a href="/en/js-ctypes/js-ctypes_reference/CData" title="en/js-ctypes/js-ctypes reference/CData">CData</a></code> object produced by accessing a field of a structure or the internals of an array will hold the referent objects alive.</li>
</ul>
<h3>What won't keep objects alive</h3>
<p>It's important to note that getting direct access to the contents of a <code><a href="/en/js-ctypes/js-ctypes_reference/CData" title="en/js-ctypes/js-ctypes reference/CData">CData</a></code> object using <a href="/en/js-ctypes/js-ctypes_reference/CData#address()" title="en/js-ctypes/js-ctypes reference/CData#address()"><code>address()</code></a>, <a href="/en/js-ctypes/js-ctypes_reference/CData#addressOfElement()" title="en/js-ctypes/js-ctypes reference/CData#addressOfElement()"><code>addressOfElement()</code></a>, or <a href="/en/js-ctypes/js-ctypes_reference/CData#contents" title="en/js-ctypes/js-ctypes reference/CData#contents"><code>contents</code></a>, will result in a <code><a href="/en/js-ctypes/js-ctypes_reference/CData" title="en/js-ctypes/js-ctypes reference/CData">CData</a></code> object that <strong>does not</strong> hold its referent alive. Be sure to hold an explicit reference to be sure the referent object doesn't get garbage collected before you're done using it.</p>
<h3>Closures</h3>
<p>You also need to be sure to retain references to any JavaScript code that native code may call back into. This should be obvious, but is important enough to be worth stating explicitly.</p>
<h3>When in doubt, malloc()</h3>
<p>When you absolutely, positively need to keep data around, you can use <code>malloc()</code> to allocate it directly. This bypasses JavaScript's memory management and lets you handle memory management yourself.</p>
<h2>Examples</h2>
<p>These examples offer a quick look at how js-ctypes is used. See <a href="/en/js-ctypes/Examples" title="en/js-ctypes/Examples">js-ctypes examples</a> for more intricate examples.</p>
<h3>Calling Windows routines</h3>
<p>This example demonstrates how to use ctypes to call a Win32 API.</p>
<pre class="brush: js">Components.utils.import("resource://gre/modules/ctypes.jsm");

var lib = ctypes.open("C:\\WINDOWS\\system32\\user32.dll");

/* Declare the signature of the function we are going to call */
var msgBox = 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;

var ret = msgBox(0, "Hello world", "title", MB_OK);

lib.close();</pre>
<p>In line 3, the <code>user32.dll</code> system library is loaded. Line 6 declares <code>msgBox()</code> to be a method that calls the Windows function <a class=" external" href="http://msdn.microsoft.com/en-us/library/ms645505%28VS.85%29.aspx" title="http://msdn.microsoft.com/en-us/library/ms645505(VS.85).aspx"><code>MessageBoxW</code></a>. Line 15 calls the <code>msgBox()</code> routine, which displays the alert.</p>
<p>The last thing we do is call <code>lib.close()</code> to close the library when we're done using it.</p>
<p>Instead of defining the whole path, you may also just give the file name.</p>
<pre>var lib = ctypes.open("user32.dll");
</pre>
<p>Or even without the extension.</p>
<pre>var lib = ctypes.open("user32");
</pre>
<h3>Calling Carbon routines on Mac OS X</h3>
<p>This example demonstrates how to use ctypes to call a Carbon function on Mac OS X.</p>
<pre class="brush: js">/* build a Str255 ("Pascal style") string from the passed-in string */

function makeStr(str) {
  return String.fromCharCode(str.length) + str;
}

Components.utils.import("resource://gre/modules/ctypes.jsm");

var carbon = ctypes.open("/System/Library/Frameworks/Carbon.framework/Carbon");

stdAlert = carbon.declare("StandardAlert",       /* function name */
                          ctypes.default_abi,    /* ABI type */
                          ctypes.int16_t,        /* return type */
                          ctypes.int16_t,        /* alert type */
                          ctypes.char.ptr,       /* primary text */
                          ctypes.char.ptr,       /* secondary text */
                          ctypes.uint32_t,       /* alert param */
                          ctypes.int16_t);       /* item hit */

var hit = 0;
var msgErr = makeStr("Carbon Says...");
var msgExp = makeStr("We just called the StandardAlert Carbon function from JavaScript!");

var err = stdAlert(1, msgErr, msgExp, 0, hit);

carbon.close();</pre>
<p>The <code>makeStr()</code> function is a utility routine that takes as input a standard JavaScript string and returns a Carbon-style "Pascal" string, which is a length byte followed by the characters of the string itself. Note that this only works correctly if the string is in fact under 256 characters; if it's longer, this will fail spectacularly.</p>
<p>In line 9, the Carbon library is loaded from the system's Carbon framework.</p>
<p>Line 11 declares the <code>stdAlert()</code> function, which will call the Carbon <code>StandardAlert</code> routine. It uses the default ABI, returns a 16-bit integer (which is a Carbon <code>OSErr</code> value), and accepts an integer (the alert type), two strings, a pointer to a parameter block, which we aren't using, and another integer, which is used to return the hit item. See Apple's documentation for <a class="external" href="http://developer.apple.com/legacy/mac/library/documentation/Carbon/Reference/Dialog_Manager/Reference/reference.html#//apple_ref/c/func/StandardAlert" title="http://developer.apple.com/legacy/mac/library/documentation/Carbon/Reference/Dialog_Manager/Reference/reference.html#//apple_ref/c/func/StandardAlert"><code>StandardAlert</code></a> for details.</p>
<p>After that, we simply set up our parameters by using <code>makeStr()</code> to generate the two <code>Str255</code> strings we need, then call <code>stdAlert()</code>, which produces the following alert window:</p>
<p><img alt="ctype-mac-dialog.png" class="internal default" src="/@api/deki/files/4559/=ctype-mac-dialog.png"></p>
<p>The last thing we do is call <code>carbon.close()</code> to close the library when we're done using it.</p><h3>Calling LibC routines on Linux/POSIX</h3>
<p>This example demonstrates how to use ctypes to call a libc function on Linux.</p>
<pre class="brush: js">/* import js-ctypes */
Components.utils.import("resource://gre/modules/ctypes.jsm");

/* Open the library */
try {
    /* Linux */
    var libc = ctypes.open("libc.so.6");
} catch (e) {
    /* Most other Unixes */
    libc = ctypes.open("libc.so");
}

/* Import a function */
var puts = libc.declare("puts",             /* function name */
                        ctypes.default_abi, /* call ABI */
                        ctypes.int_t,       /* return type */
                        ctypes.char.ptr);   /* argument type */

var ret = puts("Hello World from js-ctypes!");</pre>
Revert to this revision