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;
}
なんとか切り抜けました。そしてもう一つ難所があったんだけど、長くなったので後日書きます。