#pragma twice

KAB-studio > プログラミング > #pragma twice > 310 Version 15.10 エクスポートとインポート

#pragma twice 310 Version 15.10 エクスポートとインポート

前のページへ 表紙・目次へ 次のページへ

 Version 15.10
エクスポートとインポート

前回はとりあえず DLL を作って使ってみました
あれだけ見ると結構簡単だったんだけど……
あれは全部下準備ができているからね。というわけで、今回はその必要な
下準備とかについて見ていきます
お〜
ではひとつずつ見ていきます。まず DllMain() 関数から

// DLLTestEasy.cpp

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

うん、こういう関数あるね
これは DLL が読み込まれたときに呼び出される関数。 WinMain() みたい
な感じかな
え、これが WinMain() の代わり!?
一応ね
一応?
だって、 DLL が読み込まれたときに呼び出される、って言っても、その
時に必要な処理ってほとんどないから。普通は Exe から関数が呼び出され
るだけだもの
そっか、 Exe とは違うんだものね……
だから、これは無視しちゃってください
無視ですか
次に関数について

// DLLTestEasy.cpp

// これはエクスポートされた関数の例です。
DLLTESTEASY_API int fnDLLTestEasy(void)
{
    OutputDebugString( "fnDLLTestEasy()" );
    return 42;
}

これを Exe から呼んでたんだよね
そうです。基本的にこの関数は普通の関数と同じ。ただひとつ違うのは
 DLLTESTEASY_API ってゆーのが付いてる
そう。これはヘッダーファイルの方で定義されています

// DLLTestEasy.h

#ifdef DLLTESTEASY_EXPORTS
#define DLLTESTEASY_API __declspec(dllexport)
#else
#define DLLTESTEASY_API __declspec(dllimport)
#endif

????
 #ifdef って憶えてる?
マクロだよね、 #define でこれ定義してたら、ってゆーの。 
Version 6.06 ( No.106 ) でやったし
そう、だからこれを分かりやすく書くと

#ifdef DLLTESTEASY_EXPORTS
    // もし DLLTESTEASY_EXPORTS が #define されていたらこっち。
    #define DLLTESTEASY_API __declspec(dllexport)
#else
    // それ以外はこっち。
    #define DLLTESTEASY_API __declspec(dllimport)
#endif

ということになります
ってことは……

// DLLTESTEASY_EXPORTS が #define されている場合
__declspec(dllexport) int fnDLLTestEasy(void)
{
    OutputDebugString( "fnDLLTestEasy()" );
    return 42;
}

か、

// DLLTESTEASY_EXPORTS が #define されていない場合
__declspec(dllimport) int fnDLLTestEasy(void)
{
    OutputDebugString( "fnDLLTestEasy()" );
    return 42;
}

ってことだよね。違うのは dllexport か dllimport か、ね
そうなります。それともうひとつ、ヘッダーファイルにも

// DLLTestEasy.h

DLLTESTEASY_API int fnDLLTestEasy(void);

あ、関数宣言の方だね。これも

// DLLTESTEASY_EXPORTS が #define されている場合
__declspec(dllexport) int fnDLLTestEasy(void);



// DLLTESTEASY_EXPORTS が #define されていない場合
__declspec(dllimport) int fnDLLTestEasy(void);

だね
実は、この違いは、関数宣言の方で大きな意味を持ちます
関数宣言の方?
そう。さて問題。 DLLTESTEASY_EXPORTS はどこで #define されているで
しょう
え……? ……あれ、ソースファイルやヘッダーファイル見てみたけどな
いよそんなの
そう、実は設定にあるんです
設定??
【プロジェクト】-【設定】ダイアログの【C/C++】-【一般】ページの
【プリプロセッサの定義】に
あ! DLLTESTEASY_EXPORTS がこんなとこに! しかも #define じゃな
いし!
この設定に書いてある文字列は、 #define と同じ意味を持つんです。普
通にプログラムに #define で書くよりは簡単だから
でも探すの大変……
さて、ここで DLLTESTEASY_EXPORTS が定義されているって事は?
んー、ってことは関数もその宣言も、

// DLLTESTEASY_EXPORTS が #define されている場合
__declspec(dllexport) int fnDLLTestEasy(void)
{
    OutputDebugString( "fnDLLTestEasy()" );
    return 42;
}

// DLLTESTEASY_EXPORTS が #define されている場合
__declspec(dllexport) int fnDLLTestEasy(void);

の方、ってそれじゃわざわざマクロ使う必要ないじゃん!
このプロジェクトではね
このプロジェクトでは……?
ヘッダーファイルの方は?
ヘッダーファイルは……あ!  BuildTest からも見てる!

// Main.cpp
#include <Windows.h>

#include "DLLTestEasy.h"

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    fnDLLTestEasy();
    return 0;
}

こっちで DLLTestEasy.h をインクルードするときには、さっきの
プロジェクトの設定の #define ってないわけだから

// DLLTESTEASY_EXPORTS が #define されていない場合
__declspec(dllimport) int fnDLLTestEasy(void);

になってる
つまり、 __declspec() の中が

・DLLTestEasy : dllexport
・BuildTest   : dllimport

と、切り替えられるってことです
どのプロジェクトから見るかで切り替わる……
この切り替わってるのが fnDLLTestEasy() の宣言ってことが重要。この
関数、プロジェクトによって立場が違うでしょ

・DLLTestEasy : fnDLLTestEasy() そのものがある。
・BuildTest   : fnDLLTestEasy() を呼び出す側。

それぞれのために、関数宣言の前に __declspec(dllexport) と
__declspec(dllimport) を付けるんです

・DLLTestEasy : __declspec(dllexport) : Exe から呼び出せるようにする
・BuildTest   : __declspec(dllimport) : DLL のを呼ぶようにする

呼び出せるように、と呼ぶように……
そう、専門用語で

・DLLTestEasy : __declspec(dllexport) : エクスポート
・BuildTest   : __declspec(dllimport) : インポート

あ、こっちの方が分かりやすいかも。 DLL では関数を〈外出し〉して、 
Exe では〈取り込む〉ってことなんだ
そういうこと。というわけで次回に続く!

/*
    Preview Next Story!
*/
プロジェクトによって変わるなんてわけわかんねー!
ここは難しいからもう一度説明します
っつーか全部説明されてもらってねー!
というかちょっと落ち着いて
いやーっ、私逃げるー!
というわけで次回
< Version 15.11 DLL の仕組み >
につづく!
次回は図を使って説明します
メルマガで図なんてありえねー!!
おちつけー!!
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。