現在、VC++、VB、VBA(主にAccess)で利用できるDLLの作成を試みています。
開発環境はWindows XP Professional with SP2 + Visual Studio.NET 2005 Team Editionです。
DLL作成コードで
#include<stdio.h>
#include<Windows.h>
#include<comutil.h>
#pragma comment(lib, "comsuppw.lib")
//マクロ定義等
#define DllExport extern "C" __declspec(dllexport)
//DLLから利用できる関数
//DBの文字を元に戻す関数(戻り値はエラーコード)
DllExport void __stdcall MyFunc(BSTR Input,BSTR *Output, int *ReturnCode);
//DllMain関数
BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpRsv)
{
return TRUE;
}
//DBの文字を元に戻す関数(戻り値はエラーコード)
DllExport void __stdcall MyFunc(BSTR Input, BSTR *Output, int *ReturnCode)
{
・・・・・もろもろの文字列変換処理
}
という感じで関数を作成し(この関数はWin32アプリケーションで作成し、exe
内で動作を確認してから移行しています)、dllを作成(defファイルで関数名も
定義)。
VBAでテストしてみたところ
Declare Sub MyFunk Lib "MyDll.dll" (ByVal ConvertString As String, ByRef OutputString As String, ByRef ReturnCode As Integer)
で、普通にCallできたのですが、VC++でテストをしてみたところ
HMODULE hDll;
void (*f)(BSTR, BSTR*, int*);
int ReturnCode;
hDll = LoadLibrary(DLL_PATH);
if(hDll == NULL)
{
エラー処理
}
else
{
f = (void (*)(BSTR, BSTR*, int*))GetProcAddress(hDll, "MyFunk");
(*f)(bstrInputString, &bstrOutputString, &ReturnCode);
変換後に必要は処理・・・
FreeLibrary(hDll);
}
という感じで呼び出してみました。
すると・・・以下のようなエラーが出ます
Run-Time Check Failure #0 - The value of ESP was not properly saved
across a function call. This is usually a result of calling a
function declared with one calling convention with a function pointer
declared with a different calling convention.
中断してみると、
(*f)(bstrInputString, &bstrOutputString, &ReturnCode);
の直後の処理で止まっていました・・・。
以前こちらで質問させていただいたときに、「DLL内で作成したバッファの解
放はDLL内で行うとよい」とのアドバイスをいただいていたので、バッファ開
放関数を作成してみても状況は変わらず・・・
で、ネットでいろいろ調べてみたところ
http://www.ne.jp/asahi/hishidama/home/tech/vcpp/dllusage.html#暗黙的リンクと明示的リンク
こんなページがあり、「引数はアセンブラレベルではスタック上に確保される
が、この解放を__stdcallは呼ばれた関数側で行い、__cdeclは呼出元で行う。」とあり、試しに__stdcall→__cdeclに変更してみたところ、VC++側の
動作確認アプリの方はソースをいっさい変更せず問題なく動作したのですが、
今度はVBAの方で呼び出せなくなりました・・・
同ページに「他言語からもこの関数を呼び出したい場合は__stdcallにする必
要がある」とあるので、当然といえば当然なのだと思いますが・・・
dll内の関数で文字列を変換し、アプリ側で変換された文字列を利用したい場
合、何か制約などがあるのでしょうか?
関数の作り方の問題なのか、それともexeで検証時から見えないバグがあった
のか・・・
正直全く見当がつかない状態です。
どなたかご存じの方、または参考すべきURLなどがございましたらご教授いた
だけないでしょうか?
よろしくお願いいたします。
> 同ページに「他言語からもこの関数を呼び出したい場合は__stdcallにする必
> 要がある」とあるので、当然といえば当然なのだと思いますが・・・
ここまで分かっているのだから、後一歩。
VC++の呼び出し側の関数宣言に問題があります。
呼び出す関数の実体は、__stdcallであるのに対して、
> void (*f)(BSTR, BSTR*, int*);
と宣言されているため、この関数は__cdeclと解釈されます。
void (__stdcall *f)(BSTR, BSTR*, int*);
としてみましょう。
maruさん、ありがとうございます!!
void (*f)(BSTR, BSTR*, int*);
→void (__stdcall *f)(BSTR, BSTR*, int*);
f = (void (*)(BSTR, BSTR*, int*))GetProcAddress(hDll, "MyFunk");
→f = (void (__stdcall *)(BSTR, BSTR*, int*))GetProcAddress(hDll, "MyFunk");
と修正したらいけました!!
本当にありがとうございました!!
ツイート | ![]() |