LuaAppMaker で、Windows の日本語パス名で大苦戦した。以下、ざっくりと報告しておきます。
まず、日本語パス上にある dll ファイルを require
でロードできない。前に fopen()
で引っかかったやつと同じだな。調べてみると、lib_package.c
の中に、こういうコードがある。
static void *ll_load(lua_State *L, const char *path, int gl)
{
HINSTANCE lib = LoadLibraryExA(path, NULL, 0);
if (lib == NULL) pusherror(L);
UNUSED(gl);
return lib;
}
はい、LoadLibraryExA()
を呼んでますね。Lua の中では日本語文字はすべて UTF-8 だから、このシステムコールも UTF-8 エンコーディングのパス名で呼んでしまう。Windows のシステムコールは UTF-8 を受け付けないので(少なくとも古い Windows はそう)、当然失敗する。
これの修正はかなり手こずった。fopen()
の時は、「UTF-8 パスを wchar_t
に変換して、_wfopen()
を呼ぶ」関数を、同じ「fopen()
」という名前で定義して、静的にリンクすることで切り抜けた。今回は、同じやり方では修正できない。システムコールは WINBASEAPI
というキーワードをつけて宣言されている。これは __declspec(dllexport)
に読み替えられるので、dll からしか読み込めない。このため、差し替えるとしたら、LoadLibraryExA()
を含む dll を作成して、製品に同梱しないといけない。配布するファイルが増えるのは嬉しくない。そこで、#include <Windows.h>
の「後」に、#define LoadLibraryExA LoadLibraryExA_Patch
のようにマクロ定義を挿入する。こうすると、ヘッダ中の LoadLibraryExA
は元のままだが、ソースコード中の LoadLibraryExA
は LoadLibraryExA_Patch
に置き換わる。そこで、LoadLibraryExA_Patch()
関数を自前で書いてリンクすればよい。
実際には、インクルードパス中に、下の内容で Windows.h
というファイルを作成した。LoadLibraryEx
の他にも、「A」サフィックスのついたシステムコールをいくつか使っていたので、すべて差し替えるようにした。#include_next
は、インクルードサーチパス中の「次のディレクトリ」からヘッダファイルを探索する。この機能を使えば、ソースコードには一切の変更なしで、ヘッダだけを差し替えることができる。
#include_next "Windows.h"
#define LoadLibraryExA LoadLibraryExA_Patch
#define FormatMessageA FormatMessageA_Patch
#define GetModuleFileNameA GetModuleFileNameA_Patch
#define GetModuleHandleA GetModuleHandleA_Patch
#define GetModuleHandleExA GetModuleHandleExA_Patch
#ifdef __cplusplus
extern "C" {
#endif
extern HMODULE LoadLibraryExA_Patch(
LPCSTR lpLibFileName,
HANDLE hFile,
DWORD dwFlags
);
extern HMODULE GetModuleHandleA_Patch(
LPCSTR lpModuleName
);
extern DWORD GetModuleFileNameA_Patch(
HMODULE hModule,
LPSTR lpFilename,
DWORD nSize
);
extern DWORD FormatMessageA_Patch(
DWORD dwFlags,
LPCVOID lpSource,
DWORD dwMessageId,
DWORD dwLanguageId,
LPSTR lpBuffer,
DWORD nSize,
va_list *Arguments
);
extern BOOL GetModuleHandleExA_Patch(
DWORD dwFlags,
LPCSTR lpModuleName,
HMODULE *phModule
);
#ifdef __cplusplus
}
#endif
LoadLibraryExA_Patch()
は、次のように実装した。uf8towchar()
は、名前の通り UTF-8 から wchar_t に変換する関数。
HMODULE LoadLibraryExA_Patch(
LPCSTR lpLibFileName,
HANDLE hFile,
DWORD dwFlags
)
{
wchar_t *wpath, *wmode;
HMODULE retval;
if (utf8towchar(lpLibFileName, &wpath) != 0) {
return NULL;
}
retval = LoadLibraryExW(wpath, hFile, dwFlags);
free(wpath);
return retval;
}
なんとか切り抜けました。そしてもう一つ難所があったんだけど、長くなったので後日書きます。