いつもお世話になっております。
VS2013 標準Windowsライブラリを使用、Unicode文字セットを使用、共通言語ランタイム
サポートをしない の環境で、DLLを作成しています。
単純に、DLLでプロジェクトを生成してコンパイルし、testdll.dllが出来たのですが、使
う側ではdllのリンクだけでは動きませんでした。
【testdll.h】
#ifdef __cplusplus
#define EXPORT extern C __declspec(dllexport)
#else
#define EXPORT __declspec(dllexport)
#endif
#ifndef __TESTDLL_H
#define __TESTDLL_H
EXPORT int test(int a);//引数で渡された値を100かけて返す
#endif
調べてみると、defを作ればxxxx.DLLだけリンクすれば、DLL内部の関数を使えると書いて
あるのですが、defを使わないでやるようにと言われました。方法を探しているのですが
なかなか見つかりません。このサイトのログも見てみたのですが見つけられませんでした。
DLLの作り方、DLLを読み込む側のルールもいくつかあるのでしょうか。まとめサイトみた
いなのがありましたら教えていただけると助かります。
よろしくお願いします。
>単純に、DLLでプロジェクトを生成してコンパイルし、testdll.dllが出来たのですが、使
>う側ではdllのリンクだけでは動きませんでした。
もう少し、具体的にどうやったのか?
というのが必要かと。
>調べてみると、defを作ればxxxx.DLLだけリンクすれば、DLL内部の関数を使えると書いて
>あるのですが
DEFファイルで外部公開する時の関数名を設定できます。
DEFファイルで指定しない場合にはもろもろのルールにより外部公開する関数名が作成さ
れる為、利用側もそのもろもろのルールに合わせる必要があります。
# 戻り値のサイズとか引数の数や型などで実際に外部公開される関数名が変更される。
dllexportで指定している場合、dllimportにしたヘッダファイルを利用することで、
もろもろのルールを揃えることが可能になります。
# コンパイラ依存については不明ですが。
DLLを「Dependency Walker」で見てみると外部公開されている関数の名前が確認できるかと。
http://soundengine.jp/wordpress/tips/try/555/
http://blog.livedoor.jp/blackwingcat/archives/557640.html
>DLLの作り方、DLLを読み込む側のルールもいくつかあるのでしょうか。まとめサイトみた
>いなのがありましたら教えていただけると助かります。
サイトなどについては不明ですが…
暗黙的リンクなのかどうか…などもあるかと。
# DLL作った時のlibファイル利用だとたいていは暗黙リンクだったような?
# 公開されているDLLのみでヘッダやlibもない状態からの場合は動的リンク…かな。
>瀬戸っぷ様
ご返答ありがとうございます。
情報が少なかったようで申し訳ありません。
DLL側はEXPORT宣言以外は何も設定などませんでした。
使う側は同じくVC++です。(本番ではDLLから作成したDLLを呼びます)
自分で作った利用側テストプロジェクトでは、
プロパティ→リンカー→入力に DLLTEST.dllを記述
プロパティ→リンカー→すべてのオプションにDLLTEST.dllを記述
DLLTEST.dllを、「追加」の「既存項目」で読み込ませ、DLLのヘッダ(testdll.h)を
インクルードすることによって、使う側は普通にDLL内部の関数を使えていて、それで良
いかと思っていたのですが、DLLのみで使えないと駄目となりました。
//#include testdll.h //←ヘッダをコメントアウトするとtest()関数を認識しなくな
ります。
void CMFCApplication2Dlg::OnBnClickedButton1()
{
int res = 0;
res = test(4); //testdll.dllの中の関数を呼び出す
}
resには400が返ってきてました。
ヘッダを外すとDLLの関数を認識しなくなるので、DLLの作り方、呼ぶ側の使い方を改めて
調べ直しているところです。
多分、動的リンクとして扱いたいと思うのですが…はっきりしません。
とりあえず、ごく一般的なDLLの作成(def無し)、利用方法を教えていただきたいと思
い、書き込みさせていただきました。
教えてくださったDependency Walker については、後でDLしたいと思います。
>プロパティ→リンカー→入力に DLLTEST.dllを記述
>プロパティ→リンカー→すべてのオプションにDLLTEST.dllを記述
リンカの入力ファイルって、拡張子DLL指定するものでしたっけ?
DLL 系ヘッダファイルの書き方
DLL を実装する側(提供する側)と DLL を利用する側とで矛盾が発生しないよう
同一内容のヘッダファイル dlltest.h を提供して使い分けるようにするのが普通。
使う側は dllimport 提供する側は dllexport が必要なので下記のようになる。
---dlltest.h---
#if defined(IMPLEMENT_DLLTEST)
/* 提供する側はこちら */
#define DLLTEST_FUNC __declspec(dllexport)
#else
/* 利用する側はこちら */
#define DLLTEST_FUNC __declspec(dllimport)
#endif
DLLTEST_FUNC int dlltest1(int);
---使う側の user1.c---
#include dlltest.h // 単純に include すると IMPLEMENT_DLLTEST が未定義
void testfunc() {
if (dlltest1(0)==0) ...
}
---提供する側の dlltest.c---
#define IMPLEMENT_DLLTEST // include dlltest.h の前に定義する
#include dlltest.h
DLLTEST_FUNC int dlltest1(int value) {
...
}
提供側は dlltest.h dlltest.dll dlltest.lib の3点セットを提供。
dlltest.c は提供しない。
利用側は dlltest.h を追加ヘッダに追加 dlltest.lib を追加ライブラリに追加し
dlltest.dll を実行環境に配置
インストーラを作る際には dlltest.dll を依存部品に追加ってことで。
>みなさま
アドバイスありがとうございます。
テストプログラムを作り直したら、状況が悪化してしまいました。
単純に dllexportしてるんだから、dllimportすればいいんじゃない。って思い以下の
コードを書いてみたのです。
【DLL側 ヘッダファイル】
#ifdef __cplusplus
#define EXPORT extern C __declspec(dllexport)
#else
#define EXPORT __declspec(dllexport)
#endif
#ifndef __EXPORTTEST_H
#define __EXPORTTEST_H
EXPORT int kakezan(int a);//引数で渡された値を100かけて返す
#endif
【DLL側 ソースファイル】
#include EXPORTTEST.h
EXPORT int kakezan(int a)//引数で渡された値を100かけて返す
{
return a * 100;
}
コンパイル→作成したEXPORTTEST.dll をIMPORTTESTのDeubgフォルダにコピー
※tetrapod 様の書き込みを拝見すると、dllだけでは駄目という感じですが。
【使う側 ヘッダファイル】
●EXPORTTEST.dllをプロジェクトに追加
●プロジェクトのプロパティ 「追加の依存ファイル」に$(TargetDir)/EXPORTTEST.dll記載
#ifdef __cplusplus
#define IMPORT extern C __declspec(dllimport)
#else
#define EXPORT __declspec(dllimport)
#endif
#ifndef __IMPORTTEST_H
#define __IMPORTTEST_H
IMPORT int kakezan(int a);//引数で渡された値を100かけて返す
#endif
【使う側 ソースファイル】
int _tmain(int argc, _TCHAR* argv[])
{
int res = kakezan(20);
printf(KAKEZAN KEKKA %d\n, res);
return 0;
}
という感じでコンパイルしてみると、
error LNK1107: ファイルが無効であるか、または壊れています: 0x300 を読み取れ
ません。 EXPORTTEST.dll 1 1 IMPORTTEST
なんてエラーが…。
dllが壊れるほど複雑な作り方はしてないと思うのですが、一体どこが悪いのでしょうか。
EXPORTTEST.dllを S2013 Toolsコマンドプロンプトでdumpbin /exportsしたところ、
ordinal hint RVA name
1 0 000110D7 kakezan = @ILT+210(_kakezan)
という結果がでました。
これは、EXPORT関数としてちゃんと定義できてると判断して良いという事ですよね。
…あれ。dllimportの宣言(?)もDLL側で書くのかな…。間違えている気がしてきました。
LoadLibraryは、サンプルを見たのですが、お恥ずかしい事に、使い方が理解できません
でした。
すでに指摘が入っていますが,「追加の依存ファイル」に書くのは
・インポートライブラリ (拡張子が .lib )
であって,
・DLL本体 (拡張子が .dll )
ではありません。
ref) https://msdn.microsoft.com/ja-jp/library/ba1z7822.aspx
EXPORTTESTのビルド時に生成されているであろうEXPORTTEST.libを依存ファイルとして追加する
ことで,
IMPORTTESTのビルドができるはずです。
# ビルドにDLLは不要,実行時にLIBは不要。
>YuO 様
ありがとうございます。
使う側のプロパティの「追加の依存ファイル」にEXPORTTEST.libを記載することによって
動きました。
なぜ、前回作っていたテストプログラムでは追加の依存ファイルに EXPORTTEST.dllで動
いていたのかの謎が残りますが、正しい設定方法が分かって良かったです。
【まとめ】
●DLL側の作成方法は問題なし
●呼び側の設定
呼び側は、DLLのlib,dll,hが必要
プロパティで依存ファイルとして設定するのはlib
DLLのヘッダのインクルードが必要
※ソース中にIMPORT宣言は不要
コンパイルが済んだexeを動かす際にはdllのみが必要
という理解で間違いないでしょうか。
しかし、今回は 呼び側に与えられるのはdllのみという設定(今、dllのスタブを作って
いるところです)なので、libやhを使わない方法は無いでしょうか。
DLLだけでも、一応インポートライブラリは作成できます。
1. dumpbin等でエクスポートされた名前の一覧を取得する
2. 取得した名前を加工してDEFファイルを作る
3. libコマンドに作成したDEFファイルを渡してインポートライブラリを作成する
ただし、DLL等のバイナリからヘッダファイルを作成するのは、基本的に無理です。
型等の情報がないため、まともなヘッダファイルを作れません。
# C++のマングルされた名前を読み解けば、一応ある程度までヘッダファイルを作れます
が……。
>YuO様
早急な回答ありがとうございます。
defを用意しないでできないかという前提条件があるのですが。
今、defを用意しない EXPORTTEST2を作って普通にコンパイルして作成したdllを利用側
にコピーしました。
【EXPORTTEST2】
#ifdef __cplusplus
#define EXPORT extern C __declspec(dllexport)
#else
#define EXPORT __declspec(dllexport)
#endif
#ifndef __EXPORTTEST2_H
#define __EXPORTTEST2_H
EXPORT int warizan(int a);//引数で渡された値を2で割って返す
#endif
利用側は、dllをプロジェクトに追加するだけで、依存ファイル(lib)の設定なども無し
ソース内に、以下のように記述
typedef int(*FUNC2)(int);
int _tmain(int argc, _TCHAR* argv[])
{
HMODULE hModule2 = LoadLibrary(_T(EXPORTTEST2.dll));
FUNC lpFunc2 = (FUNC2)GetProcAddress(hModule2, warizan);
int res = (*lpFunc2)(100);
printf(WARIZAN KEKKA %d\n, res);
return 0;
}
正しく動きました。
defを用意しないで作ったDLLを上記のように使った場合では使っているうちに何かしら不
具合が出るでしょうか。
利用するDLL内の関数分typdefとか関数のアドレス取得とか面倒な気もするのですが、DLL
を使う上のルールという事で納得するしかないのでしょうか。
また、DEFを作ればこのような手間はなく、直接関数名を呼べるというのであればDEFを作
ることも考えたいと思いますが、私がネットで見たDEFを作った方法を紹介しているサイ
トでは上記と同様のコードで書かれていました。
利用側に dlltest.h と dlltest.lib を提供するってことは
例示 typedef/キャスト をコンパイラがやってくれるということだ。
コンパイル時点で各種バグが指摘できて良いことばかり。
利用側に dlltest.h と dlltest.lib を提供しないって事は
利用側で適切な typedef とキャストをする必要があるってこと。
コンパイラによるバグ指摘もなくなってしまう。
どうなれば適切なのかは提供側にしか情報が無いわけで、
提供側はその辺を日本語で記述したマニュアルを作成して提供する義務がある。
その日本語マニュアルがきっちり読み取れる利用者なら、
ヘッダファイルをマニュアルから書き下すことができるわけで
「ヘッダファイルを提供しない」というのは選択肢として限りなく愚策と思う。
政治的理由で「 DLL のみ提供」と(上司が)考えているなら、再考慮を推奨する。
DEF なしで DLL を作ることは何の問題も無いよ。
その DLL を同一名称のまま機能拡張したいとか考えない範囲であれば、だけど。
DLL Hell で検索。
利用側の最後にFreeLibraryを入れて下さい。