なでしこの新しいバージョンでは、Lua言語をサポートし、Luaでなでしこを拡張することができるようになりました。Luaとなでしこの連携について紹介する本稿の2回目では、Lua で外部ライブラリを呼び出す方法について紹介します。
なでしこから、Lua 言語の機能を利用することができますが、Lua 言語から、Windows の他のライブラリを呼ぶにはどうしたら良いでしょうか。前回、Windows の COM インターフェイスを利用する LuaCOM について少し紹介しましたが、COM をサポートしているライブラリなら、LuaCOM を利用し、Windows の DLL にあるライブラリを利用する場合には、alien というライブラリが利用できます。
今回は、この、LuaCOM と alien について紹介します。これらを使うことによって、Windowsの各種機能に自由にアクセスできるようになるでしょう。
なでしこで、LuaCOM を使うには、なでしこの実行ファイルと同じフォルダに、clibs\luacom.dll という DLLが必要になります。そして、Lua のプログラムで、「require "luacom"」と宣言します。
前回も紹介しましたが、以下は、Internet Explorer でなでしこのページを表示する例になります。
LUA(`---
require('luacom') -- luacom を使うことを宣言
ie = luacom.CreateObject("InternetExplorer.Application") -- オブジェクトを作る
ie:Navigate2("http://nadesi.com") -- IEのNavigate2メソッドを呼ぶ
ie.Visible = true
---`)
なでしこで比較的よく使われる Excel も次のように呼ぶことができます。以下は、Excelを起動して、適当な値を100個書き込む例です。
LUA(`---
require "luacom"
-- Excelの起動
excel = luacom.CreateObject("Excel.Application")
excel.Visible = true -- 可視状態に
-- ワークブックを追加
local book = excel.Workbooks:Add()
local sheet = book.Worksheets(1)
-- 適当な値を100個書き込む
for row=1,100 do
sheet.Cells(row, 1).Value2 = math.floor(math.random() * 100)
end
---`)
次に、もう少し、高度なことをしてみましょう。以下は、適当な値を30*30個書き込み、その値のなかで50以上のものを、黄色で色をつけるというものです。
LUA(`---
require "luacom"
excel = luacom.CreateObject("Excel.Application")
local book = excel.Workbooks:Add()
local sheet = book.Worksheets(1)
excel.Visible = true
-- 適当な値を書き込む
for row=1,30 do
for col=1,30 do
sheet.Cells(row, col).Value2 = math.floor(math.random() * 100)
end
end
-- 値を調べて50以上のものを黄色でマークする
local range = sheet:Range("A1")
for row=1,30 do
for col=1,30 do
local v = sheet.Cells(row, col).Value2
if v > 50 then
local cell = range:Offset(row-1, col-1)
cell:Select()
excel.Selection.Interior.Color = 65535
end
end
end
---`)
Excelを終了させるには、以下のように記述します。
LUA(`---
require "luacom"
excel = luacom.CreateObject("Excel.Application")
excel.Visible = true
---`)
「OKをクリックすると終了します」と言う。
LUA(`---
excel.DisplayAlerts = false -- 終了確認を出さないようにする
excel:Quit() -- 終了
excel = nil -- オブジェクトを解放する
---`)
どうでしょうか。Excel VBA が分かる人ならば、ちょっと工夫することで、なでしこの拡張ライブラリが作れそうな気がするのではないでしょうか。
見栄えの良い例として、チャートを作成してみます。これは、適当な値をExcelに書き込んで、それでExcelのチャートを作成します。
LUA(`---
require "luacom"
excel = luacom.CreateObject("Excel.Application")
local book = excel.Workbooks:Add()
local sheet = book.Worksheets(1)
excel.Visible = true
-- 適当な値を書き込む
for row=1,30 do
sheet.Cells(row, 1).Value2 = math.floor(math.random() * 100)
end
-- チャートを作る
local chart = excel.Charts:Add()
chart.ChartType = 4 -- xlLine
local range = sheet:Range("A1:A30")
chart:SetSourceData(range)
---`)
次に、Windows の DLL を呼び出すことが出来る、alien について紹介します。alien を使うことで、各種DLL にある関数を呼び出すことができます。
alien を使うためには、なでしこの実行ファイルと同じフォルダに clibs\alien フォルダと、lua\alien.lua ファイルが必要になります。
一番簡単な例として、メッセージボックスを表示させてみます。
LUA(`---
require "alien"
-- API を宣言
MessageBox = alien.User32.MessageBoxA
MessageBox:types{ret = 'long', abi = 'stdcall', 'long', 'string', 'string', 'long' }
-- 使ってみる
MessageBox(0, "こんにちは!", "LUAより", 0)
---`)
APIの宣言部分を見てみましょう。一行目では、User32.dll にある、MessageBoxA が利用できるように宣言しています。そして、DLLの関数を呼ぶ場合には、関数の戻り値や引数の型を自動的に取得することはできませんので、関数の宣言を 二行目で記述する必要があります。MessageBoxA の戻り値は、long 型、呼び出し規約は stdcall なので、テーブルの ret と abi に、その値を設定します。その後、引数の型を次々と指定します。
MessageBox = alien.User32.MessageBoxA
MessageBox:types{ret = 'long', abi = 'stdcall', 'long', 'string', 'string', 'long' }
C言語の宣言と見比べると分かりやすいでしょうか。32bit整数は、long と記述し、文字列のポインタを指定する箇所では、string を記述するという感じです。
int MessageBoxA(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
)
また、Windows API ではメモリに実行結果を書き込んで返すようなものが多いですが、これを利用するときは、alien.buffer() メソッドでメモリを確保しておいてから、関数を実行するという手順になります。
以下は、Kernel32.dll で定義されている関数 ExpandEnvironmentStringsA を呼び出す例です。
require( "alien" )
-- ExpandEnvironmentStrings の宣言
local ExpandEnvironmentStrings = alien.Kernel32.ExpandEnvironmentStringsA
ExpandEnvironmentStrings:types{ ret = "long", abi = 'stdcall', "string", "pointer", "long" }
-- バッファの確保
local buffer = alien.buffer(512)
-- 関数の呼び出し
ExpandEnvironmentStrings("%USERPROFILE%", buffer, 512)
print(tostring(buffer))
alien で利用できる型の種類は、以下のようなものがあります。そしてこれらの型に、"ref" を付けることで、参照型を指定できます。
"int", "byte", "short", "long", "float", "double", "pointer", "string"
他に、Callback 関数を設定する機能もあります。以下は、コールバック関数が必要な、EnumWindows を呼ぶ例です。EnumWindows の第一引数には、コール関数を指定する必要があります。この場合、alien.callback()メソッドを使って、コールバック関数を生成してあげる ことで対応できます。
以下は、EnumWindows で現在起動しているウィンドウの一覧を表示する例となっています。
require 'alien'
-- API宣言 (EnumWindows)
EnumWindows = alien.user32.EnumWindows
EnumWindows:types {"callback", "pointer", abi="stdcall"}
-- API宣言 (GetClassName)
GetClassName = alien.user32.GetClassNameA
GetClassName:types {"long", "pointer", "int", abi="stdcall" }
-- Callback 関数の生成
local buf = alien.buffer(512)
local function enum_func(hwnd, p)
GetClassName(hwnd, buf, 511)
print (hwnd..":"..tostring(buf))
return 1
end
local callback_func = alien.callback(
enum_func,
{"int", "pointer", abi="stdcall"})
-- 関数の呼び出し
EnumWindows(callback_func, nil)
なでしこ単体でも、DLLを呼び出すことはできますが、コールバック関数が利用できないなど、微妙に不便なことが多かったです。しかし、Lua の alien を使うことで、コールバック関数を利用できるようになります。
以上、今回は Lua で外部ライブラリを呼び出す方法について紹介しました。なでしこの外部連携機能の弱点を補う点で、Lua が強力な見方となることが分かったと思います。また、本稿は、なでしことの連携だけでなく、Lua を使う上でも有益な内容となったと思います。(現時点で、Luaの日本語資料が非常に少なかったので。)