Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
guides:machine_code [2023-12-28 04:41] – Largely rewritten example section geek | guides:machine_code [2024-01-02 16:18] (current) – Add section 6, Importing functions geek | ||
---|---|---|---|
Line 206: | Line 206: | ||
</ | </ | ||
- | When compiled using godbolt, we see two problems arise: | + | When [[https:// |
- When the machine code is generated, only '' | - When the machine code is generated, only '' | ||
- If beta //were// to be called, //it doesn' | - If beta //were// to be called, //it doesn' | ||
- | Traditional MCode tools like MCode4GCC do nothing to address these problems. Instead, you would have to compile and load each function separately, managing and passing around | + | Traditional MCode tools like MCode4GCC do nothing to address these problems. Instead, you have to compile and load each function separately, managing and passing around |
+ | |||
+ | <runner ahk2> | ||
+ | alpha := MCode(' | ||
+ | beta := MCode(' | ||
+ | MsgBox DllCall(beta, | ||
+ | |||
+ | MCode(mcode) { | ||
+ | static e := Map(' | ||
+ | if (!regexmatch(mcode, | ||
+ | return | ||
+ | if (!DllCall(" | ||
+ | return | ||
+ | p := DllCall(" | ||
+ | if (c=" | ||
+ | DllCall(" | ||
+ | if (DllCall(" | ||
+ | return p | ||
+ | DllCall(" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This kind of workaround is not necessary when using MCL, which uses its built-in linker and loader to automatically identify function offsets and adjust the MCode at run-time so that references to other functions resolve correctly instead of remaining '' | ||
For code that defines multiple functions to be called from AHK, those functions must be // | For code that defines multiple functions to be called from AHK, those functions must be // | ||
Line 331: | Line 353: | ||
</ | </ | ||
+ | ==== 5. Callback to AHK ==== | ||
+ | |||
+ | Although there are many things you can do with MCode, sometimes it is helpful or even necessary to callback to AutoHotkey for some actions. For example, it can be extremely valuable to call back to AutoHotkey for OutputDebug, | ||
+ | |||
+ | AutoHotkey provides a tool to export its own functions to be consumed as a callback from C/%%C++%% APIs, [[https:// | ||
+ | |||
+ | <runner ahk2> | ||
+ | #Requires AutoHotkey v2.0 | ||
+ | #Include <MCL> | ||
+ | |||
+ | lib := MCL.FromC(" | ||
+ | ( | ||
+ | void alpha(void (*MsgBoxInt)(int), | ||
+ | MsgBoxInt(123); | ||
+ | MsgBoxStr(L" | ||
+ | MsgBoxInt(456); | ||
+ | } | ||
+ | )") | ||
+ | |||
+ | pMsgBoxInt := CallbackCreate((i) => MsgBox(i), " | ||
+ | pMsgBoxStr := CallbackCreate((p) => MsgBox(StrGet(p)), | ||
+ | DllCall( | ||
+ | lib, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ) | ||
+ | </ | ||
+ | |||
+ | When working with MCL, you can do a little better by passing the functions as global variables instead of parameters. This lets you more easily add and remove debug functions to be used all throughout the MCode without having to update all your function calls each time. | ||
+ | |||
+ | <runner ahk2> | ||
+ | #Requires AutoHotkey v2.0 | ||
+ | #Include <MCL> | ||
+ | |||
+ | lib := MCL.FromC(" | ||
+ | ( | ||
+ | #include < | ||
+ | |||
+ | MCL_EXPORT_GLOBAL(MsgBoxInt, | ||
+ | void (*MsgBoxInt)(int); | ||
+ | |||
+ | MCL_EXPORT_GLOBAL(MsgBoxStr, | ||
+ | void (*MsgBoxStr)(short*); | ||
+ | |||
+ | MCL_EXPORT(alpha, | ||
+ | void alpha() { | ||
+ | MsgBoxInt(123); | ||
+ | MsgBoxStr(L" | ||
+ | MsgBoxInt(456); | ||
+ | } | ||
+ | )") | ||
+ | |||
+ | lib.MsgBoxInt := CallbackCreate((i) => MsgBox(i), " | ||
+ | lib.MsgBoxStr := CallbackCreate((p) => MsgBox(StrGet(p)), | ||
+ | lib.alpha() | ||
+ | </ | ||
+ | |||
+ | It may be tempting to try to use CallbackCreate in order to wrap mathematical functions like Sqrt, however this should be avoided normally because it will absolutely destroy any performance gains that you were aiming to achieve by using MCode in the first place. Instead, native machine code implementations of sqrt and other mathematical functions should be embedded or imported. | ||
+ | |||
+ | ==== 6. Importing Functions ==== | ||
+ | |||
+ | Writing C or %%C++%% code in an MCode environment, | ||
+ | |||
+ | The basic strategy is this: | ||
+ | |||
+ | - Load the DLL using AutoHotkey | ||
+ | - Fetch the function pointer using AutoHotkey | ||
+ | - Pass that function pointer using the same strategy we did for CallbackCreate | ||
+ | - Call that function from your MCode | ||
+ | |||
+ | For example, we can import '' | ||
+ | |||
+ | <runner ahk2> | ||
+ | #Requires AutoHotkey v2.0 | ||
+ | |||
+ | if !(hDll := DllCall(" | ||
+ | throw OSError(,, " | ||
+ | if !(pFunction := DllCall(" | ||
+ | throw Error(,, " | ||
+ | |||
+ | ;double hypotenuse(double (*sqrt)(double), | ||
+ | ; return sqrt(a * a + b * b); | ||
+ | ;} | ||
+ | lib := MCode(" | ||
+ | |||
+ | MsgBox DllCall(lib, | ||
+ | |||
+ | MCode(mcode) { | ||
+ | static e := Map(' | ||
+ | if (!regexmatch(mcode, | ||
+ | return | ||
+ | if (!DllCall(" | ||
+ | return | ||
+ | p := DllCall(" | ||
+ | if (c=" | ||
+ | DllCall(" | ||
+ | if (DllCall(" | ||
+ | return p | ||
+ | DllCall(" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | When working with MCL, importing functions like this can be handled automatically by the '' | ||
+ | |||
+ | <runner ahk2> | ||
+ | #Requires AutoHotkey v2.0 | ||
+ | #include <MCL> | ||
+ | |||
+ | lib := MCL.FromC(" | ||
+ | ( | ||
+ | #include < | ||
+ | MCL_IMPORT(double, | ||
+ | |||
+ | double hypotenuse(double a, double b) { | ||
+ | return sqrt(a * a + b * b); | ||
+ | } | ||
+ | )") | ||
+ | |||
+ | MsgBox DllCall(lib, | ||
+ | </ | ||
+ | |||
+ | MCL can also be used to import functions from third-party DLLs, such as lua54.dll. For more information about that, please refer to the [[libraries: | ||
===== Compatibility Notes ===== | ===== Compatibility Notes ===== | ||