#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のトップページをご覧ください。