MCL [v1][v2]


MCL is a new kind of machine code library for AutoHotkey, which takes a no-nonsense approach to converting compiled code to AutoHotkey embedded machine code. It requires a GCC-based cross-compiler installed under PATH (named as gcc.exe/g++.exe or x86_64-w64-mingw32-gcc.exe/x86_64-w64-mingw32-g++.exe) to run the tests, examples, or to compile any code.

It was designed to work with mingw-w64 installed via Cygwin, and mingw-w64 installed via MYSYS2, but has been tested to work with TDM-GCC as well.

Once your C or C++ code has been compiled to a standalone function or class, that standalone class will have no dependency back to MCL. Scripts using your compiled code will need to include just the compiled code, and nothing else. You can check the releases of cJson.ahk [v1][v2] for an example of what compiled code would look like in a script.

MCL has a few extra features compared to other machine code tools, the big ones being:

  • Support for both C and C++
  • Includes some basic hand-written replacements for some of the most common standard headers
  • Support for both 32 and 64 bit
  • Allows very large constants (and Doubles, which GCC tends to promote to being a global constant, breaking code under other tools)
  • Allows exporting one or more functions to AutoHotkey
  • Allows exporting one or more global variables to AutoHotkey
  • Generates a wrapper with all the DllCalls/NumGet/NumPut calls pre-bound with offsets and types
  • Allows importing functions from external DLLs into the C/C++ code, such as from the Windows API or any other DLL you have laying around
  • Generates standalone AutoHotkey code with the compiled machine code and wrappers, so you can include compiled code without including the MCL library.

Altogether, MCL has just enough features that a solid subset of standard C code can be compiled under it, and run (more or less) fine.

The primary goal of MCL is to enable much more complex machine code functions/libraries without resorting to a dll. For example cJson.ahk [v1][v2] which is compiled under MCL, and makes use of global variables, "exporting" multiple functions, and floating point numbers.

MCL also supports a tiny subset of C++ (no STL, exceptions, or RTTI). For example, see the SpookyHash test, which uses a C++ implementation of the SpookyHash hashing algorithm.

MCL also adds the ability for code to "import" functions from DLLs in the same way you can with DllCall. This feature doesn't replace DLL files, and isn't the primary goal of the project. Just think of it as a cherry on top.

And a (low level) warning for this feature: There is a bug with mingw-w64 compilers which results in the stack being misaligned, which will cause a crash the next time a Windows API (or standard library) function is called from your C/C++. This bug can be tracked here.

For an example of "importing" functions, see cloakersmoker's PDFGen example library, which uses a regular C library with a few headers changed around along with an incredible number of imported standard library functions.

If you have any questions about the library, please feel free to join the Discord and ping Geek (GeekDude) or cloakersmoker in #mcl-mcode-lib.

If you run into any problems, please report them as GitHub issues.

The header mcl.h provides macros for "talking" to AHK from your code, mainly, this means exporting a function to AHK, or having AHK import a function from a .dll file before loading your code.

However, this header is not needed. By default, the __main function will be called if there are no exported functions, or if mcl.h is not used at all.

These macros are as follows:

  • MCL_EXPORT(Name, [Type1, Arg1, Type2, Arg2, ReturnType]) will export the function Name to AHK, allowing the function to be called as a library method.
  • MCL_EXPORT_INLINE(ReturnType, Name, Parameters) will export and define Name at the same time, in the same place.
  • MCL_EXPORT_GLOBAL(Name, Type) will export the global variable Name to AHK, allowing the variable to be get/set as a library property.
  • MCL_IMPORT(ReturnType, DllName, Name, ParameterTypes) will import the function Name from the DLL file DllName, and declare Name to be a function pointer to said function, with the given ReturnType/ParameterTypes.

Additionally, MCL provides a limited set of "fake" standard headers, which all either define implementations of common standard library functions, or use mcl.h to import them from the Windows API.

The following headers are provided, but none contain the full set of functions mandated by the C standard:

  • stddef.h
  • stdio.h (note: does not have a `printf` implementation, but most file based functions should work)
  • stdlib.h
  • string.h (note: only implements very common operations)
  • time.h

If calling a standard library function gives you an error, then that function isn't implemented. Feel free to open an issue to request having the function added.

In C++, mcl.h uses malloc/free in order to implement the global new/delete operators, allowing you to use them in your C++. For an example of using new/delete, see Examples/C++_with_new_delete.ahk.

MCL also provides a limited set of "fake" Windows API headers, designed to make interoperability with the Windows API based on MS documentation examples easier. These do not and are not intended to match the Windows SDK originals, but follow along with similar naming at a much smaller coverage.

The following headers are provided:

In AHK, MCL provides all functionality through the MCL class.

Any method which is described as "returning compiled code" returns an object that wraps the exported functions and global variables as methods and properties respectively. The object is also callable, in which case it will invoke any export named "__main".

Now, for the API:

  • MCL.FromC(Code, Options := MCL.Options.OutputAHKBit) and MCL.FromCPP(Code, Options := MCL.Options.OutputAHKBit) compile Code as C/C++ (throwing an exception for any compile errors) and returns the compiled code.
  • MCL.FromString(Code) loads `Code`, which is pre-compiled code packed into a string (returned by one of the following methods) and returns the loaded compiled code.
  • MCL.AHKFromC(Code, Options := MCL.Options.OutputAHKBit) and MCL.AHKFromCPP(Code, Options := MCL.Options.OutputAHKBit) are like `MCL.FromC()`/`MCL.FromCPP()`, but pack the compiled code into a string which MCL.FromString(Code) can load.
  • MCL.StandaloneAHKFromC(Code, Options := MCL.Options.OutputAHKBit, Name := "MyC") and MCL.StandaloneAHKFromCPP(Code, Options := MCL.Options.OutputAHKBit, Name := "MyCPP") are like `MCL.AHKFromC(Code)`, but return code which does not require MCL at all, and can be used without including MCL.ahk. This code will be formatted as a single function or class, which returns the compiled code in the same format as all other functions.

For any method which takes an Options parameter, the following options can be provided to control the bitness/format of the generated code:

  • MCL.Options.OutputAHKBit generates code which will run in A_PtrSize * 8 AHK. So, on AHK U32, this flag tells MCL to generate 32 bit code. On AHK U64, this flag tells MCL to generate 64 bit code.
  • MCL.Options.Output32Bit generates 32 bit code, ignoring the bitness of the AHK executable.
  • MCL.Options.Output64Bit generates 64 bit code, ignoring the bitness of the AHK executable.
  • MCL.Options.OutputBothBit is only valid for methods which generate AHK code, such as MCL.AHKFromC or MCL.StandaloneAHKFromCPP. Generates code which is both 32 and 64 bit, and can run under either. This means compiling the code twice, once for each bitness. This also effectively doubles the size of the code.

However, if MCL is running under 32 bit AHK, the options MCL.Options.Output64Bit and MCL.Options.OutputBothBit will not work. This is because processing 64 bit code/data requires using 64 bit integers, which are not supported by 32 bit AHK.

Additionally, the following flag exists for the MCL.AHKFromC and MCL.AHKFromCPP methods:

  • MCL.Options.DoNotFormat Do not format the resulting code as an AHK string literal, instead simply return it as a string. This allows for chaining, like MCL.FromString(MCL.AHKFromC(Code, MCL.Options.DoNotFormat)).

And that's it.

Compared to tools like MCode4GCC or mcode-generator, MCL has a much shorter list of limitations.

With MCL, you can define multiple functions, use global variables, and use function pointers (ex: return &some_function_defined_in_c; will return a valid pointer).

Generally, any language feature supported by standard C should work. If you find something that doesn't, please report it as an issue.

AutoHotkey v1

AutoHotkey v2

AutoHotkey v1

AutoHotkey v2

AutoHotkey v1

AutoHotkey v2

AutoHotkey v1

AutoHotkey v2

AutoHotkey v1

AutoHotkey v2

AutoHotkey v1

AutoHotkey v2