2012年4月20日 星期五

Calling Out with Alien

Alien 是 Lua 的擴充函式庫之一, 它提供了 FFI(Foreign Function Interface), 可以很輕易地在 Lua 中呼叫動態聯結函式庫中的函式.

以下為一些 prototype 較簡單的 Win32 API, 其 Alien 的 prototype 定義:
require "alien"

local SendMessage = alien.User32.SendMessageA
SendMessage:types({ret = "long", abi = "stdcall", "ulong", "uint", "ulong", "ulong"})

local PostMessage = alien.User32.PostMessageA
PostMessage:types({ret = "long", abi = "stdcall", "ulong", "uint", "ulong", "ulong"})

local Sleep = alien.Kernel32.Sleep
Sleep:types({ret = "void", abi = "stdcall", "uint"})

local GetLastError = alien.Kernel32.GetLastError
GetLastError:types({ret = "uint", abi = "stdcall"})

由於 Win32 API 的 calling convention 為 stdcall, 並非 cdecl, 因此我們需要指定 abi="stdcall", 否則在呼叫時可能會導致 AV(access violation).

牽扯到資料結構的函式, 我們就需要用到 alien.defstruct 或 alien.struct 來打包資料, 以下使用 alien.struct 來打包 SendInput 所使用的 INPUT 資料結構:
require "alien"
local struct = require "alien.struct"

-- prototypes of foreign functions
local SendInput = alien.User32.SendInput
SendInput:types({ret = "uint", abi = "stdcall", "uint", "pointer", "int"})

-- specify structure format of INPUT for SendInput
local fmtMI
local fmtKI
if os.getenv("PROCESSOR_ARCHITECTURE") == "x86" then
 fmtMI = "L".."LLLLLL"
 fmtKI = "L".."HHLLLxxxxxxxx"
else
 -- with alignment and paddings for 64-bit
 fmtMI = "Lxxxx".."LLLLLxxxxI8"
 fmtKI = "Lxxxx".."HHLLxxxxI8xxxxxxxx"
end

-- Send F1 key
local VK_F1 = 0x70
local input = struct.pack(fmtKI, 1, VK_F1, 0, 0, 0, 0)
local result = SendInput(1, input, struct.size(fmtKI))
從以上例子中可看出, 在資料結構中有用到 union 的, 我們可能會需要加 padding 以維持整個資料結購的大小, 另外, 在不同位元的作業系統上, 可能還會需要做 alignment.


更多資訊:
LuaForge - Alien
Library for Converting Data to and from C Structs
LUA调用Windows下的DLL
init.lua - guacamole - Simple platform for creating 2D games with Lua Source Code Reference


沒有留言:

張貼留言