dllの作成

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
dic
記事: 657
登録日時: 13年前
住所: 宮崎県
連絡を取る:

dllの作成

#1

投稿記事 by dic » 10年前

今、dllを作っているのですが、リンクがうまくいきません。
というのも、namespace を使っているせいか、関数の名前がどうにもリンクできません。

以下のようにして、namespace を使っているライブラリを、使用するコードでは
どのようにして呼び出せばいいのでしょうか?

PrintFromLib(); はわかるのですが、
PrintFromAAA(); の呼び出し方がわかりません。

AAA::PrintFromAAA(); にしてみてもうまくいきませんでした。

環境:Visual C++ 2010 Express


[lib,dllを作成しているコード]

コード:


   --- main.cpp ---

#include	<stdio.h>
#include	"main.h"

void	PrintFromLib()
{
	printf( "Hello from lib.\n" );
}

void	PrintFromAAA()
{
	printf( "Hello from AAA.\n" );
}

コード:

   --- main.h ---
#define EXPORT extern __declspec(dllexport) 


EXPORT	void	PrintFromLib();

namespace	AAA
{
	EXPORT	void	PrintFromAAA();
}


[lib, dll を呼び出すコード]

コード:

// test_for_lib.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"


#pragma	comment( lib, "test_lib.lib" )

using namespace AAA;

int _tmain(int argc, _TCHAR* argv[])
{
	PrintFromLib();
	PrintFromAAA();

	return 0;
}

コード:

   --- main.h ---
#define EXPORT __declspec(dllexport) 

EXPORT	void	PrintFromLib();

namespace	AAA
{
	EXPORT	void	PrintFromAAA();
}

コンパイルエラーは
1>------ ビルド開始: プロジェクト: test_for_lib, 構成: Debug Win32 ------
1> stdafx.cpp
1> test_for_lib.cpp
1>test_for_lib.obj : error LNK2019: 未解決の外部シンボル "void __cdecl AAA::PrintFromAAA(void)" (?PrintFromAAA@AAA@@YAXXZ) が関数 _wmain で参照されました。
1>c:\users\owner\documents\visual studio 2010\Projects\test_for_lib\Debug\test_for_lib.exe : fatal error LNK1120: 外部参照 1 が未解決です。
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========
となってます。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: dllの作成

#2

投稿記事 by YuO » 10年前

DLLがエクスポートしている関数のリストは確認しましたか。
AAA::PrintFromAAA (void)は実装されていないため,エクスポートされていないような気がします。

でもって,DLL作るときは,
  • Cの機能だけで呼び出せる形式にする
    • クラスは使わない
      コンストラクタ,コピーコンストラクタ,デストラクタのオーバーロードは当然厳禁
    • テンプレートは使わない
      std::stringやstd::vectorも使わない,というか使えない
    • 名前空間は使わない
    • 例外は外に出ないようにする
  • 呼び出し規約はWINAPI (__stdcall) を利用する
  • __declspec(dllexport)だけでなく,DEFファイルも書いておく
あたりは押さえておくべき事柄になります。
これらを守らないと,色々と相互運用性の面で面倒なことが起きやすいです。

dic
記事: 657
登録日時: 13年前
住所: 宮崎県
連絡を取る:

Re: dllの作成

#3

投稿記事 by dic » 10年前

>>YuOさん
提示していただいた、注意することを読みました。

しかし、今回 公開されているソースコードでは、私が最初に書いたソースコードのように
namespace AAA
{
EXPORT void PrintFromAAA();
}
と、dll,libを作っているソースコードは名前空間を使用しているのです。

この場合は、自分で namespace AAA の部分を削除し、
C関数で呼び出せるようにして、きちんとエクスポートするようにするしかないということでしょうか?


http://www.ni.com/support/ja/labview/pr ... /utils.htm
ここを参考にできた lib, dll をそれぞれダンプしてみたところ以下の出力がされました。

これは、関数がエクスポートできていないということでしょうか?


================================================================================
① dumpbin.exe test_lib.lib >> hoge.txt の場合

Microsoft (R) COFF Binary File Dumper Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


Dump of file test_lib.lib

File Type: LIBRARY

Summary

C6 .debug$S
14 .idata$2
14 .idata$3
4 .idata$4
4 .idata$5
E .idata$6



================================================================================
② dumpbin.exe test_lib.dll >> hoge.txt の場合


Microsoft (R) COFF Binary File Dumper Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


Dump of file test_lib.dll

File Type: DLL

Summary

1000 .data
1000 .idata
2000 .rdata
1000 .reloc
1000 .rsrc
4000 .text
10000 .textbss

naohiro19
記事: 256
登録日時: 13年前
住所: 愛知県

Re: dllの作成

#4

投稿記事 by naohiro19 » 10年前


YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: dllの作成

#5

投稿記事 by YuO » 10年前

dic さんが書きました:しかし、今回 公開されているソースコードでは、私が最初に書いたソースコードのように
namespace AAA
{
EXPORT void PrintFromAAA();
}
と、dll,libを作っているソースコードは名前空間を使用しているのです。

この場合は、自分で namespace AAA の部分を削除し、
C関数で呼び出せるようにして、きちんとエクスポートするようにするしかないということでしょうか?
VC++の特定バージョンのみで使用可能,でよいのであれば,普通に__declspec(dllexport)で可能です。
クラスですら__declspec(dllexport)できます。
今回は,単純にAAA::PrintFromAAAの定義をDLLで行っていないのですから,当然それを呼び出すことはできません。
DLLの作成時に警告程度は出ているかと思いますが。
オフトピック
もしかして,::PrintFromAAAの関数定義をAAA::PrintFromAAAの関数定義と勘違いされていませんか
dic さんが書きました:これは、関数がエクスポートできていないということでしょうか?
/exportsオプションがないので,判断できません。
個人的にはdumpbinで見るのが面倒なので,Dependency Walkerへのショートカットを「送る」に入れて使っていますが。

dic
記事: 657
登録日時: 13年前
住所: 宮崎県
連絡を取る:

Re: dllの作成

#6

投稿記事 by dic » 10年前

>>naohiro19 さん
リンク先読ませてもらいました。

defファイルの必要が Visual Stdio 2012 以降では必要なのか、わかりませんが、
Visual C++ 2010 Express では、下に示すソースコードでうまく、名前空間を使ったdll,libの作成に成功しました。

今後も参考にさせていただきます。


>>YuO さん
YuOさん さんが書きました:/exportsオプションがないので,判断できません。
個人的にはdumpbinで見るのが面倒なので,Dependency Walkerへのショートカットを「送る」に入れて使っていますが。
ちょっと疲れていました。
/exports を指定していませんでした。
ソースコードの下に、/exports を指定したバージョンを載せます。
dumpbinの出力結果から、今回はエクスポートされていることが確認できたのですが、
これでいいでしょうか?

Dependency Walker は参考にさせていただきます。


まずは、ソースコードから

コード:

 // main.cpp

#include	<stdio.h>
#include	"main.h"





void	PrintFromLib()
{
	printf( "Hello from lib.\n" );
}

void	PrintFromAAA()
{
	printf( "Hello from AAA.\n" );
}

void	AAA::PrintFromAAA()
{
	printf( "Hello from AAA2.\n" );
}

namespace	BBB
{
	void	PrintFromBBB()
	{
		printf( "Hello from BBB.\n" );
	}
}

void	PrintFromC()
{
	printf( "Hello From C.\n" );
}

void	CCC::PrintFromCCC()
{
	printf( "Hello from CCC.\n" );
}

コード:

 // main.h
#define EXPORT extern __declspec(dllexport) 


EXPORT	void	PrintFromLib();

namespace	AAA
{
	EXPORT	void	PrintFromAAA();
}

namespace	BBB
{
	EXPORT	void	PrintFromBBB();
}


extern "C" __declspec( dllexport )	void PrintFromC();

namespace	CCC
{
	extern "C" __declspec( dllexport )	void	PrintFromCCC();
}




そして、dumpbin の出力

[ コマンド ]
dumpbin.exe /exports test_lib.dll >> test.txt
dumpbin.exe /exports test_lib.lib >> test.txt

test.txt

コード:

Microsoft (R) COFF Binary File Dumper Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


Dump of file test_lib.dll

File Type: DLL

  Section contains the following exports for test_lib.dll

           0 characteristics
    5373493C time date stamp Wed May 14 19:45:16 2014
        0.00 version
           1 ordinal base
           5 number of functions
           5 number of names

    ordinal hint RVA      name

          1    0 00011078 ?PrintFromAAA@AAA@@YAXXZ
          2    1 000110AA ?PrintFromBBB@BBB@@YAXXZ
          3    2 0001100A ?PrintFromLib@@YAXXZ
          4    3 0001119F PrintFromC
          5    4 0001105A PrintFromCCC

  Summary

        1000 .data
        1000 .idata
        2000 .rdata
        1000 .reloc
        1000 .rsrc
        4000 .text
       10000 .textbss
Microsoft (R) COFF Binary File Dumper Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


Dump of file test_lib.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  ?PrintFromAAA@AAA@@YAXXZ (void __cdecl AAA::PrintFromAAA(void))
                  ?PrintFromBBB@BBB@@YAXXZ (void __cdecl BBB::PrintFromBBB(void))
                  ?PrintFromLib@@YAXXZ (void __cdecl PrintFromLib(void))
                  _PrintFromC
                  _PrintFromCCC

  Summary

          C6 .debug$S
          14 .idata$2
          14 .idata$3
           4 .idata$4
           4 .idata$5
           E .idata$6

となりました。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: dllの作成

#7

投稿記事 by YuO » 10年前

dic さんが書きました:defファイルの必要が Visual Stdio 2012 以降では必要なのか、わかりませんが、
Visual C++ 2010 Express では、下に示すソースコードでうまく、名前空間を使ったdll,libの作成に成功しました。
naohiro19さんがリンクされていたページに書かれていることも,私が最初に書いたことも,
あくまで別のコンパイラや言語から呼ばれることを考慮した場合に出てくる話です。
VC++の特定のバージョンのみで使えればよいと割り切るならば,モジュール定義ファイル (.def) は不要です。

逆に,例えばVBAからアクセスしたいとか,そういうことを必要とするのであれば,書き方に制約がかかります。
dic さんが書きました:ちょっと疲れていました。
/exports を指定していませんでした。
ソースコードの下に、/exports を指定したバージョンを載せます。
dumpbinの出力結果から、今回はエクスポートされていることが確認できたのですが、
これでいいでしょうか?
今度の物は,エクスポートされています。
マングルされた名前でエクスポートされているので,VC++ 2010のみで利用可能という制限を受けますが。
オフトピック
おそらくは,VC++の他のバージョンからも利用できるでしょう。
しかし,マングルされた名前がバージョンによって変化しないことをMSは保証していないはずです。

dic
記事: 657
登録日時: 13年前
住所: 宮崎県
連絡を取る:

Re: dllの作成

#8

投稿記事 by dic » 10年前

>>YuOさん

.defファイルを使うことによって、特定のコンパイラに縛られないんですね
わかりました。


疑問点が3つあります。

1つめは
http://msdn.microsoft.com/ja-jp/library/zkwh89ks.aspx
↑ このページでの.defファイルは
LIBRARY BTREE
EXPORTS
Insert @1
Delete @2
Member @3
Min @4

となってますが、
http://www.ni.com/white-paper/4877/ja/
↑ このページでは
LIBRARY OurDLL
EXPORTS
avg_num
add_num
num_Integers

となっており、@数字 が必要なのか必要でないのかがわかりません。


2つ目は、__stdcall と __cdecl についてですが、
http://msdn.microsoft.com/ja-jp/library/zxk0tw93.aspx
このページと
http://msdn.microsoft.com/ja-jp/library/zkwh89ks.aspx
を見ても、区別がわかりませんでした。

呼び出し規約を指定しないと、Visual C++ 2010 Express では、__cdecl が使われるようですが、
今回作るdllは、ウィンドウズ用の実行ファイルで呼び出したいので、__stdcall にする必要があるのでしょうか?

WinDef.h によると 以下のようになっており、ウィンドウズ用のdllにするには、__stdcall でないと
いけない気がするのです。
#elif (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED)
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
#else
...



3つ目は、DLL を使うコードは、.defファイルを用意するのか?ということです。



一応、DLLを作って、実行ファイルから呼び出すことに成功しました。
ソースコードを載せておきます。


====== DLL 作成コード =============

コード:

  // DLL   main.cpp

#include	<stdio.h>
#include	"main.h"

void	PrintFromLib()
{
	printf( "Hello from lib.\n" );
}

void	__stdcall PrintFromAAA()
{
	printf( "Hello from AAA.\n" );
}

void	__stdcall AAA::PrintFromAAA2()
{
	printf( "Hello from AAA2.\n" );
}

namespace	BBB
{
	void	PrintFromBBB()
	{
		printf( "Hello from BBB.\n" );
	}
}

void	PrintFromC()
{
	printf( "Hello From C.\n" );
}

void	CCC::PrintFromCCC()
{
	printf( "Hello from CCC.\n" );
}

コード:

 // DLL  main.h
#define EXPORT extern __declspec(dllexport) 


EXPORT	void	PrintFromLib();

EXPORT	void	__stdcall PrintFromAAA();

namespace	AAA
{
	EXPORT	void	__stdcall PrintFromAAA2();
}

namespace	BBB
{
	EXPORT	void	PrintFromBBB();
}


extern "C" __declspec( dllexport )	void PrintFromC();

namespace	CCC
{
	extern "C" __declspec( dllexport )	void	PrintFromCCC();
}

コード:

 // main.def
LIBRARY   BTREE
EXPORTS
	PrintFromLib
	PrintFromAAA
	AAA::PrintFromAAA2
	PrintFromBBB
	PrintFromC
	CCC::PrintFromCCC

====== DLL 呼び出しコード =============

コード:

// test_for_lib.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"


#pragma	comment( lib, "test_lib.lib" )

using namespace AAA;
using namespace BBB;

int _tmain(int argc, _TCHAR* argv[])
{
	PrintFromLib();
	PrintFromAAA();
	PrintFromAAA2();
	PrintFromBBB();
	PrintFromC();
	CCC::PrintFromCCC();

	return 0;
}


YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: dllの作成

#9

投稿記事 by YuO » 10年前

dic さんが書きました:1つめは
http://msdn.microsoft.com/ja-jp/library/zkwh89ks.aspx
↑ このページでの.defファイルは
LIBRARY BTREE
EXPORTS
Insert @1
Delete @2
Member @3
Min @4
となってますが、
http://www.ni.com/white-paper/4877/ja/
↑ このページでは
LIBRARY OurDLL
EXPORTS
avg_num
add_num
num_Integers
となっており、@数字 が必要なのか必要でないのかがわかりません。
オフトピック
リンク先が__cdeclのページなので,比較元がわかりませんが……。
@の後の数字は,序数を自分で制御する場合に利用します。
モジュール定義 (.def) ファイル - モジュール定義ステートメントに関する規則 - EXPORTS
に構文規則があります。
序数を使うと,「DLLの1番目の関数」のような呼び出し方ができます。
ほんの僅か,アドレス解決が早くなると聞いたことがありますが,実際に序数を使うことはまずないでしょう。

プラグインを作る場合に序数を指定されている,などの特別な場合を除き,序数を明示で書く必要はないです。
オフトピック
序数を書かない場合,リンカが勝手に数値を振ります。
機械的に決まるとはいえ,exportする関数を増やしたりすると序数が変化します。
このため,通常DLL内の関数は名前で解決します。
dic さんが書きました:2つ目は、__stdcall と __cdecl についてですが、
http://msdn.microsoft.com/ja-jp/library/zxk0tw93.aspx
このページと
http://msdn.microsoft.com/ja-jp/library/zkwh89ks.aspx
を見ても、区別がわかりませんでした。
呼び出し規約は,C/C++と別の言語で書かれたコードをリンクする場合に,
  • 機械語レベルでどのようにして関数に引数を渡すか
  • 引数が使ったスタックを除去するのは呼び出し側・呼び出された側のどちらか
などを決める物です。
これが合っていないと,相互に呼び出しができません。
__stdcallと__cdeclの違いは,基本的に引数が使ったスタックの除去の責任の所在です。

・__stdcallは呼び出された関数が除去します。
これは,8086から続くx86系CPUにはサブルーチンから戻る命令RETに,WORD値をとるバージョンが存在し,
何バイトスタックをPOPするかを指定できる仕様があるため,これを使う発想によるものです。
同一関数を何度も呼び出した場合に,スタックを元に戻す記述が一箇所なので,全体としてサイズが小さくなります。

・__cdeclは呼び出した関数が除去します。
これは,Cにおける可変個をサポートするために必要な方法です。
呼び出された側は,いくつ引数を渡したかを知らなくても処理ができ,呼び出した側はいくつ引数を渡したかを必ず知っているので確実に処理できる方法になります。
WindowsのAPIでも,wsprintf関数のように可変個引数を受け取る物は__cdeclを使います。

はっきり言えば,詳細などは雑談ネタにしかなりませんが (上記もx64ではまた異なってくる),
呼び出し規約というものがあって,それが異なると呼び出せなかったり,呼び出した後におかしくなったりすることは知っておいた方がよいです。
dic さんが書きました:呼び出し規約を指定しないと、Visual C++ 2010 Express では、__cdecl が使われるようですが、
今回作るdllは、ウィンドウズ用の実行ファイルで呼び出したいので、__stdcall にする必要があるのでしょうか?
特別理由がないのであれば,「WINAPI」を指定しておくのが無難です。

VC++は当然Windows SDK等がサポートする呼び出し規約を全てサポートしますが,
VBAのように呼び出し規約は__stdcallしかサポートしていない言語もあります。
このため,__stdcall,というよりもWindows SDKのAPIの規約通りという意味でWINAPIにしておくのが無難となります。
dic さんが書きました:3つ目は、DLL を使うコードは、.defファイルを用意するのか?ということです。
呼び出す側は不要です。
というか,どのDLLのどの関数をインポートするのかは,
・呼び出された関数
・リンクしたインポートライブラリファイル
を元に,リンカが決定します。
先のMSDNにも,EXPORTSはあってもIMPORTSはなかったですよね。

もちろん,DLLを使うコードが関数をエクスポートするのであれば,.defファイルを用意するのがよいとは思いますが。
オフトピック
DLLがDLLを使うのは当たり前 (kernel32.dllとかuser32.dllとかのシステムDLLは当然のように使う) ですし,
COM関係で,.exeがCOMまわりの関数群をエクスポートすることもありえます。

dic
記事: 657
登録日時: 13年前
住所: 宮崎県
連絡を取る:

Re: dllの作成

#10

投稿記事 by dic » 10年前

>>YuOさん

回答が遅くなりました。すいません。

とても貴重な情報をありがとうございました。
無事、いろんなことが解決しました。

閉鎖

“C言語何でも質問掲示板” へ戻る