ロード出来るけど関数ポインタが取得できない(DLL)

解決


まろ  2004-04-28 02:47:45  No: 53539

使い方がよくわからなくて困っています。
(どこか良いサイトがあれば教えてください)

同プロジェクト内にワークスペースを2つ作成し
片方を「Win32 Console App」、もう片方を「Win32 Dynamic-Link..」
にしています。
色々なサイトを徘徊し、以下のようにコーディングしました。
DLLのロードは出来るのですが、関数ポインタが取得できません。
あと、何をしないといけないのでしょうか?

Appの方↓−−−−−−−−−−−−−−−
#include "windows.h"
#include "stdio.h"

typedef int (__stdcall *MYFUNC)(int, int);
MYFUNC pFunc;

void main(){
    HINSTANCE hDLL;

    hDLL = LoadLibrary("..\\DllTetst\\Debug\\DllTetst.dll");
    if (hDLL != NULL){
        pFunc = (MYFUNC)GetProcAddress(hDLL,"add");
        if (!pFunc){
            FreeLibrary(hDLL);    // 解放
            return;
        }
        printf("answer = %d\n",pFunc(1,3));
    }
    FreeLibrary(hDLL);
}

DLLの方↓−−−−−−−−−−−−−−
#include <windows.h>
#ifdef __cplusplus
extern "C"{
#endif
int __declspec(dllexport) __stdcall add(int a,int b);
#ifdef __cplusplus
}
#endif

BOOL WINAPI DllMain( HINSTANCE hDLL,DWORD dwReason,LPVOID lpReserved ){
    return TRUE;
}

int __declspec(dllexport) __stdcall add(int a,int b){
    return a+b;
}


まろ  2004-04-28 02:48:42  No: 53540

すみません、追記です。
両方とも「空のプロジェクト」で作成しています。


シャノン  2004-04-28 04:24:50  No: 53541

答えを教えるのは簡単ですが、ここでは敢えて自力解決のためのヒントを与えましょう。

まず、GetProcAddress が失敗しているのですから、GetProcAddress のヘルプを見ましょう。そこには、
>拡張エラー情報を取得するには、GetLastError 関数を使います。
と書かれているはずです。

GetLastError で、エラーコードを取得できました。でも意味がわかりません。
そこで今度は、GetLastError のヘルプを見ましょう。そこには
>エラーコードに対応するエラー文字列を取得するには、FormatMessage 関数を使います。
とかなんとか書いてあるでしょう。

今度は、FormatMessage のヘルプです。
さて、そこには親切なことに、GetLastError で得たエラーコードを文字列に変換するサンプルが載っているはずです。早速プログラムに組み込んで試してみてください。


まろ  2004-04-28 10:42:58  No: 53542

シャノンさんどうもありがとうございました。
言われてみると「確かにそうだ」と思えるのですが、
DLLの事しか頭になく、GetProcAddress のヘルプを見るなんて考えもしませんでした。
うわっつらの部分(DLL)にしか目が行っていませんでした。
どうもありがとうございます。
これ以上ない返信を頂いた感じです。

道が開けました。
いろいろとがんばってみます!


シャノン  2004-04-28 18:35:30  No: 53543

では、解答です。

FormatMessage ではどんな文字列が得られましたか?
おそらく「プロシージャが見つかりません」といった類のものでしょう。
つまり、GetProcAddress で取得しようとしている add という関数が DLL 内に見つからない、ということです。

DLL にどんな関数があるかを調べるには、VC++ に付属している Dependency Walker というソフトを使います。VC++ 6.0 なら、スタートメニューの「Microsoft Visual Studio 6.0 ツール」の中にあるでしょう。
これを起動して、ウィンドウに DLL ファイルをドラッグ&ドロップしてみましょう。
ウィンドウの右下に、DLL がエクスポートしている関数一覧が表示されます。さて、add はあるでしょうか?

おそらく、_add@8 とかいう関数があるんじゃないでしょうか。
つまり、GetProcAddress ではこの _add@8 という関数名を指定しなければならなかったのです。

しかしこれでは不便ですね。なんとか add で取得できるようにしたいものです。
さて、ここでようやく解答です。
答えは「__declspec( dllexport ) を使わずに DEF ファイルを使う」です。
最初に
>どこか良いサイトがあれば教えてください
とありますので、お答えしましょう。
本来ならば、最も参考になるサイトはマイクロソフト、MSDN です。
しかしここの資料はわかりやすいとは言えないので、今回はこちら。

Area of VC++ Tips らららのお部屋
http://rararahp.cool.ne.jp/vc/

左上の「VC++ Tips」から、右上にある「DLL(Win32)」の中、「declspec(dllexport)を使ってエクスポートする」と「DEFファイルを使ってエクスポートする」をよーく読んでみてください。
偶然か必然か、まろさんが最初に提示したのとまったく同じソースコードが見つかります。
偶然でないとしたら、既に参考になるサイトを見つけていたことになりますね。

#いや、これに気づいたのはついさっきです。
#もっと早く言わなかったことを怒らないでくださいね…

なお、上記ページには
>エクスポートには、declspecを推奨します
という一文がありますが、これは必ずしも正しくはありません。
同じページ内に「暗黙的リンク」と「明示的リンク」についての解説があります。暗黙的リンクの場合は declspec の方が楽ですが、今回のように明示的リンクを行なう場合には DEF ファイルのほうがわかりやすいでしょう。
個人的には DEF ファイルを推奨します。

なお、関数ではなくクラスをエクスポートする場合は、暗黙的リンクしか選択肢がないと言っても過言ではありません。

あえて回りくどい解説をしてみましたが、お気に召さなかったらごめんなさいね。


まろ  2004-04-29 02:57:22  No: 53544

全然回りくどくないです。ヒントを教えて頂いた方が、次悩んだときとか参考になりますし。
一番最初に載せたプログラムはご察しの通りそちらのサイトからのパクリだったりします。(^^;
こんな感じで作ってみたい、というのをあちこち探していて、どうにかこうにかたどり着きました。
なのになぜこんな質問を?ですよね。DLLをよく理解していなく、DEFファイルを使わないでdllを作成し(declspecで作成して)、明示的にリンクする方法ってないのかな?と色々考えていました。

教えて頂いたようにエラーを取ったりしていた最中、GetProcAddressがnull以外の無効なアドレスを返すようになり、さらに調べていたら「lpProcName パラメータで指定された順序数に対応する関数が存在しないと、GetProcAddress が null 以外の無効なアドレスを返し、エラーが発生する可能性があります。」なんて事がmsdnに載っていて、→エクスポートしてない→どうする?→DEFファイルが必用
と、どうにか整理がつきました。

がしかし、declspec で明示的リンクをする方法がわかっていません。
Win32APIシステムプログラミングという本(解決した後に見つけたんですけどね)にDLLについて詳しく載っているのですが、ここにはdelspecでの明示的リンクの方法が載っていなくて・・。

とはいうものの、解決しました。
シャノンさん、ご丁寧にありがとうございました。


YuO  2004-04-29 03:44:24  No: 53545

解決となっていますが……。

> がしかし、declspec で明示的リンクをする方法がわかっていません。

やり方は基本的にDEFファイルでも一緒です。
つまり,DLLでエクスポートされた関数名をGetProcAddressに指定してやればよいのです。

シャノンさんが書かれているように,
extern "C" int __declspec(dllexport) __stdcall add(int a,int b);
と宣言されている関数は,VC++でエクスポートした場合,
おそらく_add@8という名前でエクスポートされます。
その為,
pFunc = (MYFUNC)GetProcAddress(hDLL,"_add@8");
の様に,addではなく_add@8という名前を使えばちゃんと利用できます。

_add@8という名前は,MAPファイルを調べるなりdumpbin.exeを使うなり,
Dependency Walkerを使うなりすれば知ることができます。
http://www.dependencywalker.com/


まろ  2004-04-29 23:31:38  No: 53546

あれ・・・。
改めてやってみたら何も問題なくできてしまいました。(言い訳です)
悩んでいるときに_add@8も試した気がしたんですけどね・・・。(言い訳です)
.defファイルを使う以外は殆どやってみたんですが・・・。
改めて最初に質問したプログラムを見てみると・・・(恥


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加