Миграция с AutoHotkey — DLL и нативный interop

План переноса AutoHotkey DllCall, callback-функций, буферов, структур и нативного interop на C# и API EyeAuras.

Миграция с AutoHotkey — DLL и native interop

Используйте эту страницу для миграции DllCall, CallbackCreate, Buffer, NumGet, NumPut, сырых указателей, native-структур и прямого Win32 interop.

Здесь разобран именно прямой перенос DllCall. Более высокоуровневые API EyeAuras или PoeShared тоже могут подойти, но это уже вариант переписывания кода, а не прямой эквивалент DllCall.

Краткая схема

Что нужно в AutoHotkeyAPI AHKПуть в EyeAurasПервый вариант на C#ОценкаПримечания
Вызов native DLLDllCall, Buffer, NumGet, NumPut, CallbackCreateТипизированные P/Invoke-обёртки на C#; при желании — переписывание на готовые API EyeAuras/PoeShared[DllImport("user32.dll")] static extern IntPtr GetForegroundWindow();Хорошо подходит для простых вызовов, но требует больше явного кода и шаблонной обвязки, чем AHK.Сначала показывайте прямую P/Invoke-сигнатуру. API EyeAuras/PoeShared упоминайте как более удобный путь переписывания, а не как единственный вариант.

Простой DllCall

AutoHotkey v2:

hwnd := DllCall("user32.dll\GetForegroundWindow", "ptr")

Скрипт EyeAuras на C#:

csharp
using System.Runtime.InteropServices;

var hwnd = NativeMethods.GetForegroundWindow();
Log.Info($"Foreground HWND: 0x{hwnd.ToInt64():X}");

static class NativeMethods
{
    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();
}

В версии на C# строковый вызов из AHK заменяется типизированной сигнатурой. Для небольшого скрипта такой helper-класс можно оставить в том же файле, а в более крупном проекте — вынести в отдельный .cs файл.

Необязательный вариант через EyeAuras, если вам нужен IWindowHandle, а не сырой HWND:

csharp
IWindowListProvider WindowsProvider { get; init; }

var foreground = WindowsProvider.GetForegroundWindow();

Рекомендация: хороший вариант для простых native-вызовов. Сначала показывайте прямую миграцию через P/Invoke, а затем уже упоминайте API EyeAuras/PoeShared, если для пользователя это более удобный способ переписать код.

DllCall с аргументами

AutoHotkey v2:

DllCall("user32.dll\MessageBoxW", "ptr", 0, "str", "Hello", "str", "EyeAuras", "uint", 0, "int")

Скрипт EyeAuras на C#:

csharp
using System.Runtime.InteropServices;

var result = NativeMethods.MessageBoxW(IntPtr.Zero, "Hello", "EyeAuras", 0);
Log.Info($"MessageBox result: {result}");

static class NativeMethods
{
    [DllImport("user32.dll", EntryPoint = "MessageBoxW", CharSet = CharSet.Unicode)]
    public static extern int MessageBoxW(
        IntPtr hWnd,
        string text,
        string caption,
        uint type);
}

Рекомендация: после того как сигнатура описана правильно, этот вариант надёжнее и понятнее, чем сырые строковые типы в AHK. Но риск просто смещается в другую сторону: теперь важно правильно выбрать типы C#, charset, calling convention и правила времени жизни объектов.

Базовое сопоставление типов

Тип AHKВариант в C#Примечания
ptrIntPtr или nintВ примерах лучше использовать IntPtr — он привычнее для большинства пользователей.
int, uintint, uintСледите за знаковостью; многие Win32-boolean значения описываются как bool с [return: MarshalAs(UnmanagedType.Bool)].
int64, uint64long, ulongЗначения размером с указатель лучше оставлять как IntPtr.
float, doublefloat, doubleТип должен точно совпадать с native ABI.
strstringДля современных Win32-вызовов используйте CharSet.Unicode или W-вариант entry point.
Буфер выходной строкиStringBuilder или выделенный буферОбычный string подходит только для входных строк.
Buffer, NumGet, NumPutbyte[], Span<byte>, MemoryMarshal, структуры с StructLayoutЕсли layout известен, обычно лучше использовать типизированные структуры.
CallbackCreatedelegate плюс Marshal.GetFunctionPointerForDelegate(...)Держите delegate живым всё время, пока native-код может его вызвать.
Опция CdeclCallingConvention = CallingConvention.CdeclИспользуйте только если native-функция действительно работает через cdecl.

Примеры выполняются в C# Action на Windows. Первый пишет в журнал числовой HWND, который может быть нулевым: он не создаёт и не владеет окном. Второй показывает модальное окно Hello, ждёт закрытия и пишет код результата. Токен остановки скрипта этим нативным вызовам не передаётся. Это не пример универсальной обёртки над callback или произвольным ABI.

Сигнатуры и результаты: GetForegroundWindow, MessageBoxW. При переносе другой функции проверяйте её собственный контракт.

Ссылки для проверки