#pragma twice

KAB-studio > プログラミング > #pragma twice > 316 Version 15.16 クラスをエクスポートする!

#pragma twice 316 Version 15.16 クラスをエクスポートする!

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

 Version 15.16
クラスをエクスポートする!

「前回は関数をエクスポートするときの方法と装飾名の関係について説明し
ました」
『 __declspec(dllexport) を使うとエクスポート名がへんてこになるけど
仕方ない、って話だったね』
「だから、プラグインみたいに〈関数名を指定する〉場合はモジュール定義
ファイルを使って、それ以外は __declspec(dllexport) を使う、というの
が基本かな」
『そうなっちゃうんだね。あの装飾名はなんかちょっとヤだけど……』
「さて、今回は関数ではなくクラスをエクスポートします」
『クラスを!? なんか難しそう……』
「実はそんなに難しくないんです。まずは実際に使ってみましょう。 
DLLTestEasy プロジェクトの DLLTestEasy.cpp を次のように修正してくだ
さい」

// DLLTestEasy.cpp

#include "stdafx.h"
#include "DLLTestEasy.h"

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;
}


// これはエクスポートされた変数の例です。
DLLTESTEASY_API int nDLLTestEasy=0;

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

// これはエクスポートされたクラスのコンストラクタです。
// クラスの定義については DLLTestEasy.h を参照してください。
CDLLTestEasy::CDLLTestEasy()
{ 
    OutputDebugString( "CDLLTestEasy::CDLLTestEasy()\n" );
}

「修正箇所は、 CDLLTestEasy::CDLLTestEasy() の中に」

    OutputDebugString( "CDLLTestEasy::CDLLTestEasy()\n" );

「を追加したこと。あと、前回の fnDLLTestEasy() の前の DLLTESTEASY_API 
を元に戻したりしています。同じく」

// DLLTestEasy.h

// 以下の(略)
#ifdef DLLTESTEASY_EXPORTS
#define DLLTESTEASY_API __declspec(dllexport)
#else
#define DLLTESTEASY_API __declspec(dllimport)
#endif

// このクラスは DLLTestEasy.dll からエクスポートされます
class DLLTESTEASY_API CDLLTestEasy {
public:
    CDLLTestEasy(void);
    // TODO: この位置にメソッドを追加してください。
};

extern DLLTESTEASY_API int nDLLTestEasy;

DLLTESTEASY_API int fnDLLTestEasy(void);

「こちらは fnDLLTestEasy() の前に DLLTESTEASY_API を点け直しただけ。
ようするに、前々回の状態に戻しておいてもらえればOK」
『定義ファイル使わないようにってことね』
「あ、でも、別に今回は fnDLLTestEasy() は使わないから、前回のままで
もいいんだけどね。コンパイルエラーさえ出なければ大丈夫」
『組み合わせて使ってもいいんだ』
「気になる人は、モジュール定義ファイルを無効にしておいてください。
【FileView】の【DLLTestEasy.def】で右クリックして【設定】を選んで、
右側の【一般】ページの【このファイルをビルドしない】にチェックを
入れればエクスポートに使われないから」
『あれ? これって前に見たような……』
「 Version 15.02 ( No.302 ) でやったね」
『そか、ソースファイルをコンパイルしない方法だ』
「モジュール定義ファイルはソースファイルじゃないけど、同じように無効
化できるっていうこと」
『ふーん』
「さて、 DLL 側はこれで準備ができました」
『え、もう!? だってエクスポートの指定とかしてなくない?』
「してあるんです。 fnDLLTestEasy() の時もそうだったけど、 AppWizard 
が CDLLTestEasy のエクスポート例をすでに作ってくれてるんです。まず」

// DLLTestEasy.h

// このクラスは DLLTestEasy.dll からエクスポートされます
class DLLTESTEASY_API CDLLTestEasy {
public:
    CDLLTestEasy(void);
    // TODO: この位置にメソッドを追加してください。
};

『あ! DLLTESTEASY_API って  __declspec(dllexport) に置き換える
マクロだ!』
「そう、クラスのエクスポートをする時にも __declspec(dllexport) を使
うんです。クラスの場合、 class の直後に置けばOK」
『関数と似てる……』
「でも、メンバ関数には付けなくていいんです」
『本当だ。メンバ関数ひとつひとつに付けてない』
「これは、メンバ関数の定義の方も同じ」

// DLLTestEasy.cpp

// これはエクスポートされたクラスのコンストラクタです。
// クラスの定義については DLLTestEasy.h を参照してください。
CDLLTestEasy::CDLLTestEasy()
{ 
    OutputDebugString( "CDLLTestEasy::CDLLTestEasy()\n" );
}

『ホントだ、メンバ関数の方についてない……これってすごく楽じゃな
い?』
「楽だよ。クラスのエクスポートは関数に比べて簡単なんです」
『へー』
「では、実際にこのクラスを使ってみます。ビルドして DLLTestEasy.dll 
ができたら BuildTest\Debug フォルダにコピーして」
『ほい』
「 BuildTest プロジェクトの方で、 Version 15.09 ( No.309 ) と 
Version 15.10 ( No.310 ) で fnDLLTestEasy() を使ったときと同じように
ヘッダーファイルとライブラリファイルを参照するようにして」
『前からなってなくない?』
「色々とごちゃごちゃ設定変えてるからね。それができたら Main.cpp をこ
う書き換えてください」

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

#include "DLLTestEasy.h"

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    CDLLTestEasy cDLLTestEasy;

    return 0;
}

『お、 CDLLTestEasy クラス使ってる! あれ、でもメンバ関数呼び出して
ないよ』
「うん、今回の例のこのメンバ関数」

// DLLTestEasy.cpp

// これはエクスポートされたクラスのコンストラクタです。
// クラスの定義については DLLTestEasy.h を参照してください。
CDLLTestEasy::CDLLTestEasy()
{ 
    OutputDebugString( "CDLLTestEasy::CDLLTestEasy()\n" );
}

「コンストラクタだから」
『あ、よく見たら……だから作るだけで呼び出されるわけね』
「そういうこと。では試してみて」
『ほい。ビルドして実行……お、〈CDLLTestEasy::CDLLTestEasy()〉って出
た!』
「こんな感じに、関数と同じようにクラスも使えるわけです」
『それも、 class の後ろに DLLTESTEASY_API を付ければいいだけ……あ、
質問!』
「はい火美ちゃん」
『エクスポートすると、やっぱり変な装飾名になっちゃうの?』
「なります」
『モジュール定義ファイル使えばならない?』
「ううん、クラスのメンバ関数は複雑だから、モジュール定義ファイルは使
えないんです」
『げ! それってもしかして、プラグインみたいにできないってこと!?』
「そういうこと。そもそも、メンバ関数単独では呼び出せないでしょ」
『そっか、クラス作って、その変数通して呼ばなきゃいけないから……』
「一応、クラスでもプラグインみたいに使うことができるんだけど、すごく
難しいからそれはずっと先に」
『はーい』

/*
    Preview Next Story!
*/
『これで関数もクラスもやったね』
「難しかった? 簡単?」
『んー、ひとつひとつは簡単だったけど、全体だと……』
「というわけで全部振り返ってみましょう」
『げ』
「というわけで次回」
< Version 15.17 一から 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のトップページをご覧ください。