Congratulations! You endured this boring documentation, and reached the fun part! (Or you skipped it all... a wise choice, I guess?)

Jwno can be extended via pure Janet code, but sometimes that's not enough. Maybe you want to call some native code. Maybe you found a bug in Jwno's core. Or maybe you just want to peek inside. We'll set your dev environment up here, so you can do whatever you want with it.

Compiling Jwno From Source

I recommend doing this even if you don't want to touch the core of Jwno, since it's the most straightforward way to ensure all the dependencies are in place.

First, you need these things:

  • Visurl Studio 2022 (The Community version will do.)
  • Janet (Preferably the latest release version. The binaries should be compiled with the MSVC toolchain.)
  • JPM
  • Jw32

Then, follow these steps:

  1. Set JANET_SOURCE_PATH environment variable to point to the Janet source tree. Make sure the version of Janet's source matches Janet's binaries.
  2. Start an x64 Native Tools Command Prompt for VS 2022.
  3. Run jpm -l build in Jw32 source directory.
  4. Run jpm --tree=path\to\jwno\jpm_tree install in Jw32 source directory, to install it as a dependency for Jwno.
  5. Run jpm -l deps in Jwno source directory.
  6. In Jwno source directory, run jpm -l run embed-manifest. This is important, the build won't fail if you skipped this, but the resulting exe file will not run properly.
  7. Check out the built artifact jwno.exe, jwno.exp and jwno.lib in build\.

If you encountered any problem or have any question when following these steps, please file an issue here.

Compiling Native Modules

When dealing with native modules (i.e. DLL files), Jwno is source-compatible with Janet. That means if a native module works with janet.exe, it can also work with jwno.exe, but you need to recompile it specifically for Jwno.

For example, if you have the Spork library installed for Janet, you can import its pure Janet modules in Jwno (e.g. spork/infix), but can not load the modules written in C directly (e.g. spork/zip). To use Spork native modules in Jwno, recompile Spork and tell jpm to link it with jwno.lib:

# Run in Spork source directory
jpm -l build --janet-importlib=C:\path\to\jwno.lib

You can get jwno.exp and jwno.lib for a specific version separately from Github releases, and they can be placed anywhere in your disks, not necessarily in the same place as jwno.exe.

About Jw32

Jw32 is the support library for Jwno, which wraps low-level Win32 APIs, so that we can use them in Janet code. Despite its name, Jw32 only works on x64 systems.

All names in Jw32 correnspond 1:1 to official Win32 names, e.g. UNDER_SCORES_IN_CONSTANT_NAMES and CapitalCaseInFunctionNames are preserved, so you can easily search in the official Win32 documentation with the names appearing in Jwno code.

In most cases, NULL pointers are mapped to nil in Janet code, Win32 handles are mapped to the pointer type, and numbers are mapped to Janet numbers or int/s64/int/u64 types. You can check types.h in Jw32 source to see how most of other Win32 types are mapped to Janet types.

All Win32 COM instances in Jw32 are represented as Janet tables, and the table prototypes contain their methods. You can check out the implemented methods of a COM object by inspecting its prototype:

(keys (table/proto-flatten com-obj))

Again, the method names map 1:1 to the names in Win32 documentation (sans the colon character : from Janet keywords, of course).

Note that Jw32 COM objects will not get garbage collected automatically, you need to call their :Release method to free them:

(:Release com-obj)

Or use the with-uia macro:

(import jwno/util)
(util/with-uia [com-obj (construct-com-obj ...)]
  # Do stuff with `com-obj` here. Its `:Release` method will
  # be called automatically when leaving this scope.
  )

And lastly, given the vastness of Win32 APIs, Jw32 will not and can not wrap up all of them. If something you need is missing, please file an issue in the Jw32 repo, and see whether I want to add it or not 🤪. As last resorts, you can always use the FFI APIs or build your own native modules.

See Also