2006 年 11 月 17 日 23 時 40 分

基本ヘッダとソースファイルの作成


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


早速アイコンを提供するクラスを実装…と行きたい所だが、
先にプロジェクトに下準備をしておく必要がある。

ちょっと前に、DLL のプロジェクトを作っときながら、
いままで放ったらかしにしてたので、
今日はヘッダやソースをプロジェクトに追加していこう。

今後やたらと COM クラスが出てくるのであるが、
クラスを追加する都度、少し前に作った、
IUnknown の実装である Object のコードをコピーして、
クラスを量産していくことはできる。

しかし、それではあまりにも芸がない上に、
同じようなコードが何箇所にも増えてしまうことになる。
多重継承の問題点にも触れて行きたいと思っているので、
今後設計する COM クラスは Object を継承することにする。

ということで、まずはコアとなる Object クラスを、
プロジェクト内に用意してやる必要がある。

C++ プロジェクトに最初に作成するのは、
ビルド環境の設定やグローバル変数や関数を記述した、
ヘッダファイルであることが多い。

と言うことで、まずは、global.hpp を作る。
このファイルは、全ソースファイルが参照することになる。
拡張子が hpp なのは、C++ 専用として使う予定だからだ。

========== global.hpp ==========

#ifndef global_hpp_included
#define global_hpp_included

#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <ole2.h>

#endif // !global_hpp_included

========== end of global.hpp ==========

このファイルでは、基本となるシステムヘッダの取り込みと、
グローバル変数やグローバル関数などを宣言する。

もしこれが洗練されたプロジェクトなら、
環境設定とグローバル定義も分割するのだろうが、
今回は、あまりガチガチの設計にはしたくないので。

まあ、それでもここでの宣言は最小限にしておくこと。
今のところグローバル変数や関数はないので、
コンパイル環境に関する定義のみである。

STRICT は、型に厳密なコードを書くための宣言である。
WIN32_LEAN_AND_MEAN はコンパイル時間短縮のおまじないだ。
UNICODE は、Windows NT, 2000, XP, 2003 など、
Windows NT 系列用の環境を使うことを宣言している。
つまり、この時点で 9x 系 (95, 98, Me) は対象外になる。

NT 系 OS は、完全に Unicode が標準となっているが、
9x 系ではサポートは限られているため、
原則 Unicode しか使わない COM との相性が悪い。
現在の OS 普及率と、そもそも NTFS を扱うってことで、
今回は Windows 2000 以降専用ということにしておく。

あらゆるソースに共通で取り込むヘッダは、
Windows の基本となる windows.h と、
COM 開発の基本となる ole2.h としておこう。

では、次に、Object クラスを作る。

C++ でクラスを定義する場合、クラスの名前を持つ
ヘッダファイルとソースファイルを用意する。

まずは Object.hpp を作ろう。

========== Object.hpp ==========

#ifndef object_hpp_included
#define object_hpp_included

#include <unknwn.h>

class Object : public IUnknown {
    // 中身は 3 日前の日記を参照
};

#endif // !object_hpp_included

========== end of Object.hpp ==========

Object は IUnknown を継承しているので、
IUnknown を定義している <unknwn.h> のみ取り込んでいる。

ヘッダファイルは、プリプロセッサの #include 命令で
他のファイルに取り込んで利用される。
取り込むというのは、参照するということではなく、
ヘッダファイルの内容が丸ごと一部となることを意味する。

なので、取り込んだソースに悪影響を与えないように、
ヘッダファイルには、変数や関数ならその宣言を、
クラスの場合は実体を含まない定義のみを含むのが一般的だ。

Object.hpp は、Object というクラスの実体が
「どこかに存在する」から利用可能だということを定義する。
これにより、Object.hpp を取り込んだソースファイルは、
実際にどこに存在するかに関係なく、
「どこかに存在する」Object クラスを利用する事ができる。

Object のクラスの実体は 1 つしか存在できないので、
もし、Object.hpp に実体を含めてしまうと、
複数のソースファイルに取り込まれた場合、
クラスの実体が複数存在すると言うエラーになってしまう。

続いて、実装ファイルである、Object.cpp を作る。

========== Object.cpp ==========

// ビルド環境用のヘッダ
#include "global.hpp"

// クラスを定義したヘッダ
#include "Object.hpp"

// Object の実体

// IUnknown メソッド

HRESULT Object::QueryInterface(
        /* [in] */ REFIID riid,
        /* [out] */ void** ppvObject) {
    // 中身は 2 日前の日記を参照
}

ULONG Object::AddRef() {
    // 参照カウンタを 1 加算
    return ++referenceCount;
}

ULONG Object::Release() {
    // 参照カウンタを 1 減算
    if (--referenceCount == 0) {
        // カウンタが 0 なら自身を破棄
        delete this;
        return 0;
    }
    return referenceCount;
}

Object::Object(void) {
    // 初期状態ではカウンタは 0
    referenceCount = 0;
}

Object::~Object(void) {
    // 処理不要
}

========== end of Object.cpp ==========

Object.cpp は、Object というクラスの実体を定義している。
各ソースファイルでは、共通のヘッダファイルである
global.hpp を最初に取り込む必要がある。

では、この時点でビルドしてみよう。
コンパイルされるのは、ソースファイルだけなので、
現在は global.cpp だけである。

文法に問題がなければ、正常に終了するはずである。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.