実現したいこと
Haskellで作成したDLL(MyLib.dll)をExcel VBAから呼び出し、ワークシート関数として安定して利用したい。
環境】
Windows 10/11(バージョン明記)
Visual Studio 2022
GHC(Haskellコンパイラ)バージョン
Excel(32/64ビット、バージョン明記)
VBA
発生している問題・分からないこと
DLLのビルドは正常に完了し、VBAからDeclare文で呼び出しを試みている
依存DLL不足を疑い、Dependency Walkerで調査したが、「API-MS-」「EXT-MS-」系のみがnot foundで、libHS*やVCランタイム等の不足はなかった
DLLロード時の初期化(DllMain等)は推奨されないため、VBAから明示的に初期化関数を呼び出す方式も試したが、初期化直後にワークシートで関数を使うとExcelが再計算・ブック再オープンを経て正常動作するなど、安定しない。だから、DLLロード時の初期化を進めることにした。
しかし、
「ファイルが見つからない」エラー(エラー48等)が頻発し、依存DLL不足でないことが明らかになった今、解決策が見つからず手詰まり
【確認・試行済み事項】
依存DLL不足なし(Dependency Walkerで確認)
パス、権限、32/64ビット不一致、Excelのキャッシュ等も再確認
DLLの再ビルド・再配置も実施
VBAからの初期化呼び出し方式も試行
エラーメッセージ
error
1vbaでTestDllLoadWithErrorCheckを実行すると、エラーコード0が出る。 2TestTestFuncを実行すると、ファイルが見つかりません。(エラー 48)というエラーが出る。
該当のソースコード
MyLib.hs
1{-# LANGUAGE ForeignFunctionInterface #-} 2module MyLib where 3 4import Foreign 5import Foreign.C.Types 6import Foreign.C.String (CWString, peekCWString) 7import qualified Data.Map.Strict as Map 8import Data.Char (isSpace, ord) 9import System.IO (hPutStrLn, stderr) 10 11-- Mapの値をEither String Int型にする 12myDict :: Map.Map Int (Either String Int) 13myDict = Map.fromList [ 14 (32, Right 0), 15 (33, Right 1), 16 (38, Right 25), 17以下続く 18(12291, Right 1), 19 (65293, Right 1) 20 ] 21 22-- C言語から呼び出せるようにエクスポート 23{-# LANGUAGE ForeignFunctionInterface #-} 24 25-- 文字列のn番目(1始まり)のUnicode値を返す(空白は除去) 26foreign export ccall real_len :: CWString -> CInt -> CInt -> IO CInt 27 28real_len :: CWString -> CInt -> CInt -> IO CInt 29real_len cws elem1 elem2 = do 30 hPutStrLn stderr "real_len: 関数が呼ばれました" 31 str <- peekCWString cws -- ここでUTF-16(BSTR)を正しくHaskellのStringに変換 32 hPutStrLn stderr $ "real_len: 入力文字列=" ++ str 33 let strNoSpaces = filter (not . isSpace) str 34 idx = fromIntegral elem2 - 1 -- elem2は1始まり 35 if null strNoSpaces || idx < 0 || idx >= length strNoSpaces 36 then do 37 hPutStrLn stderr "real_len: 範囲外または空文字列" 38 return (-1) 39 else do 40 let ch = strNoSpaces !! idx 41 hPutStrLn stderr $ "real_len: 抽出文字=" ++ [ch] 42 return (fromIntegral . ord $ ch)
wrapper.c
1#include <windows.h> 2#include <oleauto.h> // BSTR用 3#include <HsFFI.h> 4#include <wchar.h> 5#include <stdio.h> // printf用 6 7#ifdef __cplusplus 8extern "C" { 9#endif 10 11 // Haskell関数の宣言 12 extern HsInt32 real_len(const wchar_t* s, HsInt32 elem1, HsInt32 elem2); 13 14 // DLLロード時にHaskellランタイムを初期化 15 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { 16 static int initialized = 0; 17 if (fdwReason == DLL_PROCESS_ATTACH && initialized == 0) { 18 int argc = 0; 19 char** argv = NULL; 20 hs_init(&argc, &argv); 21 initialized = 1; 22 } 23 return TRUE; 24 } 25 26 // VBAから呼び出すラッパー 27 __declspec(dllexport) HsInt32 __stdcall real_len_wrapper(BSTR s, HsInt32 elem1, HsInt32 elem2) { 28 MessageBoxW(NULL, L"real_len_wrapper called", L"Debug", MB_OK); // ←ここで呼ぶ 29 OutputDebugStringA("real_len_wrapper: 関数が呼ばれました\n"); 30 if (s == NULL) { 31 OutputDebugStringA("real_len_wrapper: BSTRがNULLです\n"); 32 return -1; 33 } 34 // BSTRはwchar_t*として使える。Haskell側もCWStringでOK 35 return real_len((const wchar_t*)s, elem1, elem2); 36 } 37 38 // ★テスト用の引数なし関数 39 __declspec(dllexport) int __stdcall test_func() { 40 MessageBoxW(NULL, L"test_func called", L"Debug", MB_OK); 41 OutputDebugStringA("test_func: called\n"); 42 return 42; 43 } 44 45#ifdef __cplusplus 46} 47#endif 48
StartEnd.c
1#include <HsFFI.h> 2#include <stddef.h> // ← これを追加 3 4// DllMainは絶対に定義しないでください 5 6// Haskellランタイムの手動初期化関数 7void HsStart() { 8 int argc = 1; 9 char* argv[] = { "ghcDll", NULL }; 10 char** args = argv; 11 hs_init(&argc, &args); 12} 13 14// Haskellランタイムの手動終了関数 15void HsEnd() { 16 hs_exit(); 17} 18
vba
1' 64ビットExcel専用のDLL/API宣言 2Declare PtrSafe Function real_Len_wrapper Lib "D:\visual_studio_2022\Git_repos_folderpath\MyLib.dll" Alias "real_len_wrapper" (ByVal s As String, ByVal elem1 As Long, ByVal elem2 As Long) As Long 3Declare PtrSafe Function test_func Lib "D:\visual_studio_2022\Git_repos_folderpath\MyLib.dll" () As Long 4Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr 5Declare PtrSafe Function FreeLibrary Lib "kernel32" (ByVal hLibModule As LongPtr) As Long 6Declare PtrSafe Function GetLastError Lib "kernel32" () As Long 7 8' DLLロードとエラー取得のテスト 9Sub TestDllLoadWithErrorCheck() 10 Dim hLib As LongPtr 11 Dim errCode As Long 12 Dim dllPath As String 13 dllPath = "D:\visual_studio_2022\Git_repos_folderpath\MyLib.dll" 14 15 If Dir(dllPath) = "" Then 16 MsgBox "DLLが見つかりません: " & dllPath, vbCritical 17 Exit Sub 18 End If 19 20 hLib = LoadLibrary(dllPath) 21 If hLib = 0 Then 22 errCode = GetLastError() 23 MsgBox "DLLのロード失敗 - エラーコード: " & errCode, vbCritical 24 Else 25 Debug.Print "DLLロード成功: " & hLib 26 FreeLibrary hLib 27 End If 28End Sub 29 30' セルの値をDLL関数に渡して長さを取得する関数 31Function Real_Len1(rng As Range, elem1 As Long, elem2 As Long) As Long 32 Dim s As String 33 Dim errCode As Long 34 35 On Error Resume Next 36 s = rng.Value 37 38 If Len(s) = 0 Then 39 Real_Len1 = -1 40 Exit Function 41 End If 42 43 ' DLL関数を直接呼び出す 44 Real_Len1 = real_Len_wrapper(s, elem1, elem2) 45 errCode = GetLastError() 46 If errCode <> 0 Then 47 Debug.Print "real_len_wrapper呼び出しエラー: " & errCode 48 End If 49End Function 50 51' DLLのtest_funcを呼び出して動作確認 52Sub TestTestFunc() 53 Dim ret As Long 54 ret = test_func() 55 MsgBox "test_funcの戻り値: " & ret 56End Sub 57 58
LIBRARY MyLib
EXPORTS
real_len_wrapper
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
AIに聞いてコードを書いた。
補足
特になし

回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2025/06/11 02:04