HaskellのDLLの作成については次の記事を参照.
13.6. Win32のDLLをビルド・利用する
文字列型のDLLの作成については次の記事を参照.
VB6, VBA の文字列 String を返す DLLを作成する - happynowの日記
DLL の作成
-- HsConcat.hs {-# LANGUAGE ForeignFunctionInterface #-} module HsConcat where import Foreign.C.String import Foreign.Marshal.Alloc import Foreign.Ptr (nullPtr) foreign import stdcall "windows.h SysAllocString" cSysAllocString :: CWString -> IO CWString foreign export stdcall hsConcat :: CWString -> CWString -> IO CWString hsConcat :: CWString -> CWString -> IO CWString hsConcat a b = do s1 <- toHsString a s2 <- toHsString b cstr <- newCWString (s1 ++ s2) ret <- cSysAllocString cstr free cstr return ret where toHsString s = if s == nullPtr -- peekCWString に NULLを与えるとクラッシュするため、NULLなら、空文字列に置換. then return "" else peekCWString s
// StartEnd.c #include <Rts.h> void HsStart() { int argc = 1; char* argv[] = {"ghcDll", NULL}; // argvはNULLで終わっていなければならない // Haskellのランタイムを初期化する char** args = argv; hs_init(&argc, &args); } void HsEnd() { hs_exit(); }
コンパイル、リンク
gcc -c HsConcat.hs
gcc -c StartEnd.c
gcc -shared -o HsConcat.dll HsConcat.o StartEnd.o -loleaut32
gcc -c StartEnd.c
gcc -shared -o HsConcat.dll HsConcat.o StartEnd.o -loleaut32
呼出し側 (Excel VBAでの使用例)
コンパイルされた HsConcat.dll と Book.xls を同一フォルダに置く。
Book.xlsには以下、ふたつのモジュールを記述する。
'同一フォルダにあるDLLファイルのロード、アンロード Option Explicit Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleW" (ByVal lpModuleName As Long) As Long Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryW" (ByVal lpLibFileName As Long) As Long Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long Public Declare Sub HsStart Lib "HsConcat.dll" () Public Declare Sub HsEnd Lib "HsConcat.dll" () ' Private Property Get LibraryPath() As String LibraryPath = ThisWorkbook.Path & "\HsConcat.dll" End Property Private Sub Auto_Open() LoadDLL HsStart End Sub Private Sub Auto_Close() HsEnd FreeDLL End Sub Private Sub LoadDLL() If 0 = GetModuleHandle(StrPtr(LibraryPath)) Then LoadLibrary StrPtr(LibraryPath) End If End Sub Private Sub FreeDLL() Dim hLib As Long hLib = GetModuleHandle(StrPtr(LibraryPath)) If hLib <> 0 Then FreeLibrary hLib End If End Sub
'DLL関数の呼出しコード Option Explicit Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long) Public Declare Function HsConcat Lib "HsConcat.dll" Alias "hsConcat@8" (ByVal x As Long, ByVal y As Long) As Long ' Sub TestHsConcat() Debug.Print Concat("かきくxけこ", "def") Debug.Print Concat(vbNullString, "ab あいうc") Debug.Print Concat("らりるれ", vbNullString) Debug.Print Concat(vbNullString, vbNullString) End Sub Function Concat(s1 As String, s2 As String) As String Concat = PtrToString(HsConcat(StrPtr(s1), StrPtr(s2))) End Function Private Function PtrToString(pBSTR As Long) As String CopyMemory VarPtr(PtrToString), VarPtr(pBSTR), Len(pBSTR) End Function
peekCStringにNULLポインタを渡すとエラー(セグメンテーション・フォールト)になることが、次の記事にレポートされている。しかし、これは期待すべき動作であるとしてクローズされている。
The module is described as "Utilities for primitive marshalling of C strings", so I think a segfault is the expected behaviour when passing a NULL pointer.
http://ghc.haskell.org/trac/ghc/ticket/4906