Access 2000 VBA より VC++ 6.0 で作成したDLL内の関数を呼ぶと、
「実行時エラー'453':
エントリxxxxがDLLファイルyyyy内に見つかりません。」が表示されます。
ここで、xxxxは関数名でyyyyはDLLファイル名です。
xxxxの例は、GetNezumiです。
作成DLLの種類: MFCレギュラーDLL
このDLLファイル内では、VBAより呼び出す関数を定義しています。
また、この関数内で他のDLLに存在しMFCを使用している関数を呼んで
います。呼出関係は、こんな感じです。
VBA → MFCレギュラーDLL(VBAより呼び出す関数) → MFC拡張DLL
→は、呼出の意味です。
Dependency Walker でのエクスポート情報例:
VCで作成したDLLをDependency Walkerで開くと、下記のように
装飾された関数名と装飾なしの関数名が混在している。
Ordinal Function Entry Point
1 ??0aaaa@QAE@XZ 0x00001041
2 ??1aaaa@UAE@XZ 0x0000100A
3 ??4bbbb@QAEAAV0@ABV0@@Z 0x00001055
4 ??4cccc@QAEAAV0@ABV0@@Z 0x00001032
5 ??_7aaaa@@6B@ 0x00017048
6 GetNezumi 0x00001037
これをみると、VBAより呼び出すGetNezumi関数のエントリポイント
は在るので、正常に呼び出せると思ったのですが???
装飾された関数名と装飾なしの関数名が混在するとダメなの
でしょうか?
また、GetNezumi関数の序数は6なので、VBAより序数=6で呼び出してみると
「ハンドルされていない例外はMSACCESS.EXE(VB6E.DLL)にあります:
0xC0000005: Access Violation。」が表示されダメです。
ちなみに、このGetNezumi関数は、VCで作ったテストexeより
正常に呼び出せます。
誰か解る方、教えて下さい。
>これをみると、VBAより呼び出すGetNezumi関数のエントリポイント
>は在るので
defファイルを使っているのでしょうか?
>「ハンドルされていない例外はMSACCESS.EXE(VB6E.DLL)にあります:
>0xC0000005: Access Violation。」が表示されダメです。
関数の引数にVBで扱えない型たとえばCStirngとかはつかっていませんよね?
>ちなみに、このGetNezumi関数は、VCで作ったテストexeより
>正常に呼び出せます。
LoadLibrary+GetProcAddressを使って非MFCのexeでも使えましたか?
(XXX.libでリンクさせてはまったく意味ない)
参考:http://madia.world.coocan.jp/cgi-bin/VBBBS/wwwlng.cgi?print+200612/06120032.txt
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=35775&forum=36
マルチは極力控えてもらいたいのですが。。。
双方の掲示板をいちいち見るのが面倒です。
こういうのはマルチをした質問者が気を利かせて
あちらで○○というアドバイスをもらったとか逐一報告するようにしてもらいたいです。
(だから、マルチは嫌われるんですよ。)
Blueさん、返答ありがとうございます。
>defファイルを使っているのでしょうか?
はい、使っています。
>関数の引数にVBで扱えない型たとえばCStirngとかはつかっていませんよね?
はい、使っていません。
>LoadLibrary+GetProcAddressを使って非MFCのexeでも使えましたか?
>(XXX.libでリンクさせてはまったく意味ない)
むむ、そうですか。非MFCのexeを作ってやってみます。
現状は、XXX.libでリンクしています。
>マルチは極力控えてもらいたいのですが。。。
>
>双方の掲示板をいちいち見るのが面倒です。
>こういうのはマルチをした質問者が気を利かせて
>あちらで○○というアドバイスをもらったとか逐一報告するようにしてもらいたいです。
>(だから、マルチは嫌われるんですよ。)
分かりました。今後マルチは控えます。
気が利かなくてえらいすんません。
ちなみに、VC側の関数宣言文とVBA側のDeclare宣言文は載せることができますか?
VBAとVCでは多少型の指定に違いがあるので注意しなければなりません。
ちなみに適当なMFC DLLを作って実験してみました。
問題は特にないです。
// VC
// VBDLLMFC.cpp
(VCで勝手に吐き出されるコードなので省略)
int WINAPI GetNumber()
{
return 1;
}
// VBDLLMFC.def
; VBDLLMFC.def : DLL 用のモジュール パラメータ宣言
LIBRARY "VBDLLMFC"
DESCRIPTION 'VBDLLMFC Windows Dynamic Link Library'
EXPORTS
; 明示的なエクスポートはここへ記述できます
GetNumber
' VBA (但し Excel 2003)
Private Declare Function GetNumber Lib "VBDLLMFC.dll" () As Long
Sub test()
ChDrive ThisWorkbook.Path
ChDir ThisWorkbook.Path
MsgBox GetNumber
End Sub
ちなみに、
>LoadLibrary+GetProcAddress
をつかった確認用コード(VC)
#include <windows.h>
int main()
{
HMODULE hDLL = ::LoadLibrary( TEXT( "VBDLLMFC.dll" ) );
if ( hDLL )
{
int ( *pGetNumber )() = ( int ( * )() )::GetProcAddress( hDLL, "GetNumber" );
if ( pGetNumber )
{
int i = ( *pGetNumber )();
TCHAR buff[ 256 ];
wsprintf( buff, TEXT( "%d" ), i );
::MessageBox( NULL, buff, TEXT( "TEST" ), MB_OK );
}
::FreeLibrary( hDLL );
}
return 0;
}
訂正)
> int ( *pGetNumber )() = ( int ( * )() )::GetProcAddress( hDLL, "GetNumber" );
int ( WINAPI *pGetNumber )() = ( int ( WINAPI * )() )::GetProcAddress( hDLL, "GetNumber" );
To: Blueさん
確認用コードまで書いてくれてありがとうございます。感謝です。
しかし、先にここの頁を見て確認用コードを書いて実験してみました。
http://www.ne.jp/asahi/hishidama/home/tech/vcpp/dll.html#FUNC_test
結果は、GetProcAddress()の戻り値が0で、その時のGetLastError()の戻り値は、127です。「指定されたプロシージャが見つかりません。」という事らしいです。
ちなみに序数を指定してGetProcAddress()を呼ぶと0x10001005が返ってきますが、その後関数を呼ぶとAccessの時と同じ様に「ハンドルされていない例外は
test.exe(MSVCRTD.DLL)にあります:0xC0000005: Access Violation。」が
表示されダメです。
とりあえず、序数指定では位置をアドレスが取れるわけなので、
関数の型がちがうもしくは、そのDLLがバグっているのどちらかしか
考えれないかと。
>「指定されたプロシージャが見つかりません。」
はわかりません。
本当に def ファイルに指定して、Dependency Walkerで確認した
関数名と1字1句間違っていないのですよね?
ちなみに、リンク先の
>FUNC_test test = (FUNC_test)GetProcAddress(hDll, (LPCSTR)1); //序数での指定
ですが、(LPCSTR)のキャストよりも、MAKEINTRESOURCEマクロを使ったほうがよいかも。
>本当に def ファイルに指定して、Dependency Walkerで確認した
>関数名と1字1句間違っていないのですよね?
はい、間違っていません。
ちなみに、呼出関係を VBA → MFCレギュラーDLL(VBAより呼び出す関数)に
して、エクスポート情報を装飾なしの関数名のみにすると、VBAよりDLL内の
関数が問題無く呼び出せます。
Dependency Walkerでの表示例は、以下のとおりです。
Ordinal Function Entry Point
1 GetNezumi 0x00001019
よくわかんなくなってきた。
最初の
>VBA → MFCレギュラーDLL(VBAより呼び出す関数) → MFC拡張DLL
は具体的にどうなっているのでしょうか?
DLLが2つあって、両方VBA側から見えるようにはなっていますか?
もしかしたら、
http://msdn2.microsoft.com/ja-jp/library/hw85e4bb(VS.80).aspx
の
>標準 DLL から拡張 DLL を使用する場合には、この初期化関数をエクスポートする必要があります。
>この関数は、拡張 DLL のクラスやリソースを使用する前に標準 DLL から呼び出す必要があります。
にあたるのかも。
具体的なやり方が
http://support.microsoft.com/kb/154126/ja
にありました。
InitYourExtDLL関数がそれにあたります。
うまく説明できずにすいません。
MFCレギュラーDLL内のVBAより呼び出す関数の中で、MFC拡張DLL内のクラスを
使っています。
例えば、GetNezumi()関数の中で、aaaaという名前のクラスを使っています。
>標準 DLL から拡張 DLL を使用する場合には、この初期化関数をエクスポートする必要があります。
>この関数は、拡張 DLL のクラスやリソースを使用する前に標準 DLL から呼び出す必要があります。
これについては、今から調べます。
>具体的なやり方が
>http://support.microsoft.com/kb/154126/ja
>にありました。
まず
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// Remove this if you use lpReserved
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("YOUREXT.DLL Initializing!\n");
// Extension DLL one-time initialization
if (!AfxInitExtensionModule(extensionDLL, hInstance))
return 0;
new CDynLinkLibrary(extensionDLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("YOUREXT.DLL Terminating!\n");
// Terminate the library before destructors are called
AfxTermExtensionModule(extensionDLL);
}
return 1; // ok
}
↓
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
// Extension DLL one-time initialization.
if (!AfxInitExtensionModule(extensionDLL, hInstance))
return 0;
}
return 1; // ok
}
のように変更しろという事ですね?
次に、InitYourExtDLL()関数を定義して、通常 DLL 内のInitInstance() メンバ
関数内でInitYourExtDLL()関数の呼び出しを追加しろという事ですね?
DllMainはnew CDynLinkLibrary(extensionDLL);の行をコメントアウトすれば
いいとおもう。
あとはそのとおりです。
InitYourExtDLL関数宣言をヘッダファイルに書くのを忘れずに。
(通常 DLL のほうでこのヘッダファイルを#includeする)
この拡張DLLは、通常DLLから呼び出す事を想定していませんでした。
この拡張DLLは、標準ライブラリとしての位置付けで、通常は、他の拡張DLL
から呼び出して使っていおります。
今回、作成している通常DLLからは、複数の拡張DLLを静的リンクしていた
のですが、動作確認のため単一のDLLのみ静的リンクするように変更しました。もちろん、システム用DLLのMSVCRTD.DLL等はリンクされています。
拡張DLLのDllMain関数内で、new CDynLinkLibrary(extensionDLL);の行を
コメントアウトし、InitYourExtDLL()関数を定義しました。
次に、通常DLL 内のInitInstance() メンバ関数内にInitYourExtDLL()関数を
追加しましたが、現象は、全く変わりません。
LoadLibrary+GetProcAddressをつかった確認用コード(VC)で確認しました。
一つ気になった点があるのですが、LoadLibrary()でこの通常DLLを呼んだ
ときに、使っていないDLLもロードされている様です。DLLがロードされた時に
通過するTRACE0()関数によりデバックウインドウに「XXX.DLL Initializing!」
が表示されていると思うのですが、使っていないDLL名が表示されています。
例えば、HMODULE hDll = LoadLibrary("choro.dll"); を実行すると
デバックウィンドウに下記が表示されます。
AAA.DLL Initializing!
BBB.DLL Initializing!
CCC.DLL Initializing!
DDD.DLL Initializing!
choro.dll では、単一のDLLのみ静的リンクする様にしたので、AAA.DLLは
使っていますが、BBB.DLL〜DDD.DLLは使っていません。AAA.DLLでも
BBB.DLL〜DDD.DLLは使っていません。また、AAA.DLLでは、EEE.DLLとFFF.DLL
とGGG.DLLを静的リンクしているのですが、これが、表示されていません。
困ったもんだ。
とりあえず、もう内容がVBと全く関係ないようなので
レスをもらえるようにこちらは閉じてVCの掲示板に移ったほうがよいでしょう。
ちなみに、私も超簡単なコードを書いて、
>VBA → MFCレギュラーDLL(VBAより呼び出す関数) → MFC拡張DLL
ってのをやりましたがうまく動きました。
問題を切り分けるためにも、必要最低限のコードから
徐々に肉付けしてゆきどこが不味いのか絞り込むような作業をしたほうがよいかもしれません。
ところで
>通常DLL 内のInitInstance() メンバ関数
はどのようにメンバ関数を追加しましたか?
ClassViewの右クリックメニューの「仮想関数の追加」より追加していない?
(InitInstanceはvirtualな関数なので宣言にvirtualがついていないと全く持って意味無しです。)
>choro.dll では、単一のDLLのみ静的リンクする様にしたので、AAA.DLLは
>使っていますが、BBB.DLL〜DDD.DLLは使っていません。AAA.DLLでも
>BBB.DLL〜DDD.DLLは使っていません。また、AAA.DLLでは、EEE.DLLとFFF.DLL
>とGGG.DLLを静的リンクしているのですが、これが、表示されていません。
BBB.DLL〜DDD.DLLは自作のDLLでしょうか?
MFCを使ううえで必要なDLLではなくて。
>とりあえず、もう内容がVBと全く関係ないようなので
>レスをもらえるようにこちらは閉じてVCの掲示板に移ったほうがよい
>でしょう。
そうですね。私もそれを考えていました。では、次の発言からVCの掲示板に
移動します。
>ちなみに、私も超簡単なコードを書いて、
>>VBA → MFCレギュラーDLL(VBAより呼び出す関数) → MFC拡張DLL
>ってのをやりましたがうまく動きました。
おお! そうですか。では、どこかに問題があると言うことか。
ちょっと、私もやってみます。
>問題を切り分けるためにも、必要最低限のコードから
>徐々に肉付けしてゆきどこが不味いのか絞り込むような作業をしたほうが
>よいかもしれません。
そうですねぇ。やはり、いきなり本番は無理があるか...
>ところで
>>通常DLL 内のInitInstance() メンバ関数
>はどのようにメンバ関数を追加しましたか?
>ClassViewの右クリックメニューの「仮想関数の追加」より追加していない?
>(InitInstanceはvirtualな関数なので宣言にvirtualがついていないと
>全く持って意味無しです。)
ご指摘ありがとうございます。手動で追加していたため、AFX_VIRTUAL部分
に追加していませんでした。
移動するのであれば、どこに移動したのかリンクをはって、
こちらは「解決」ではないですけど閉じるという意味合いで
解決チェックを入れてください。
はい。度々すいません。
VCの掲示板に移動します。下記がリンクです。
http://madia.world.coocan.jp/cgi-bin/Vcbbs/wwwlng.cgi?print+200612/06120063.txt
ツイート | ![]() |