IUnknown

`'''IUnknown'''`インターフェイスは、Component Object Model (COM) において基盤となる抽象型である。COMの仕様では、すべてのCOMオブジェクトは`IUnknown`インターフェイスを実装していなければならないとされている。すなわち、`IUnknown`は、COMにおけるルートクラスである。また、すべてのCOMインターフェイスは`IUnknown`の派生型である。

## メソッド
`IUnknown`には、`QueryInterface`、`AddRef`、`Release`の3つのメソッドがある。`QueryInterface`は、ほかに実装しているインターフェイスを問い合わせ、取得するメソッドである。`AddRef`と`Release`は、それぞれ参照カウントの増減を行う。

* `QueryInterface`はインターフェイスを識別するGUID（通常インターフェイスIDと呼び、IIDとも略される）に基づいて、他のインターフェイスへのポインタを得るために用いる。COMインターフェイスは任意のインターフェイスを継承することができ、COMオブジェクトは1つ以上の任意のインターフェイスを実装することができるが、対象のCOMオブジェクトが実装していないインターフェイスを要求された場合には、エラー値として`E_NOINTERFACE`を返す。
** なお、すべてのCOMオブジェクトは`IUnknown`を実装しているという性質上、`IUnknown`のIIDを第1引数に指定した`QueryInterface`は必ず成功する。
* `AddRef`はクライアントがこのCOMオブジェクトの参照を始めることを伝えるために用いる。
* `Release`はクライアントがこのCOMオブジェクトの使用を終了することを伝えるために用いる。

```cpp
interface IUnknown
{
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) = 0;
    virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;
    virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
};
```


`AddRef`と`Release`の戻り値は現在の参照カウントの値であるが、あくまでテスト用を目的としている。

`QueryInterface`のようないくつかのメソッドの内部では、成功した場合に`AddRef`を呼び出して参照カウントを増加させる。参照カウントを正しく把握してオブジェクトのライフサイクルを管理することはCOMクライアント側の責任でもある。RAIIをサポートするC++では、COMインターフェイスをラップして解放処理を自動化するためのスマートポインタが、ATLを始めとするいくつかのテンプレートライブラリによって定義されている。

`QueryInterface`はC++のに相当する機能を言語非依存で実現するものである。つまり、安全なダウンキャストやクロスキャストに使うことができる。

なお、`IUnknown`のインターフェイスIDは、`{00000000-0000-0000-C000-000000000046}`と定義されている。

### C++
COMの仕様にはないが、Windows SDK（旧Platform SDK）に用意されているヘッダーファイルをC++で用いると、テンプレートによって型安全性を向上させた次のような`QueryInterface`のオーバーロードが定義される。

```cpp
template<class Q>
HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)
{
    return QueryInterface(__uuidof(Q), (void **)pp);
}
```

なお、`__uuidof(Q)`は、型`Q`に関連付けられたGUIDを取得するVisual C++の拡張機能である。こちらを用いると、第2引数で指定するインターフェイスへのポインタ（実際にはポインタ変数のアドレスを渡す）と異なるインターフェイスIDを第1引数に指定してしまうという誤りを避けることが可能である。

## 役割
前述のように、`IUnknown`は実装しているほかのインターフェイスの問い合わせ・取得（`QueryInterface`）と参照カウントの管理を行うメソッドを持っているのみである。そして、すべてのCOMインターフェイスは`IUnknown`から派生（直接に、または`IUnknown`から派生したインターフェイスから派生することによって間接的に）しているため、すべてのCOMインターフェイスで必ずこれらのメソッドが呼び出し可能であることが保証される。

ただし、`IUnknown`を直接扱う有用な例は少ない。

2つの異なるインターフェイスポインタが同じCOMオブジェクトを指すものであるか否かを調べるには、`IUnknown`のIIDを指定した`QueryInterface`の呼び出しを双方で行い、得られたインターフェイスへのポインタが一致するか否かを調べるとよい。これは、あるCOMオブジェクトに対し、`IUnknown`のIIDを指定した`QueryInterface`の呼び出しで得られるポインタは、かならず（どのインターフェイスで`QueryInterface`を呼んでも）同じポインタ値となる保証があるためである（逆に言えば、実装者はそのように実装する必要がある）。


```cpp
bool EqualObject(IUnknown* lhs, IUnknown* rhs)
{
    bool ret = false;
    IUnknown* lhsUnk;
    if (SUCCEEDED(lhs->QueryInterface(&lhsUnk)))
    {
        IUnknown* rhsUnk;
        if (SUCCEEDED(rhs->QueryInterface(&rhsUnk)))
        {
            ret = (lhsUnk == rhsUnk);
            rhsUnk->Release();
        }
        lhsUnk->Release();
    }
    return ret;
}
```


C++でCOMクライアントを実装する場合、たとえインターフェイス`IBase`とインターフェイス`IDerived`の間に継承関係があったとしても、`IUnknown::QueryInterface()`の代わりにを使ってダウンキャストを実行してはいけない。COMサーバーはC++で実装されているとは限らないからである。ATLには、引数付きコンストラクタや代入演算子オーバーロードにおいて`QueryInterface`を暗黙的に実行し、`IUnknown`または派生インターフェイスへのポインタから暗黙的に変換するスマートポインタ`CComQIPtr`も用意されている（オブジェクトがインターフェイスを実装しておらず、変換に失敗した場合、`CComQIPtr`はヌルポインタ相当の空オブジェクトとなる）。

なお、.NET Frameworkおよび.NETにはCOM相互運用機能が用意されている。`System.Runtime.InteropServices.Marshal`クラスを使うことで、ローレベルな相互運用コードを手動で記述することも可能だが、COMコンポーネントのタイプライブラリをインポートして.NET言語から利用できるようにするユーティリティツール（TlbImp.exe）を使うのが簡単である。Microsoft Visual Studioにも統合されており、タイプライブラリを参照設定に追加するだけで、C#やVisual Basic .NETといったマネージ言語で書かれたアプリケーションソフトウェアからCOMコンポーネントを利用することができるようになる。マネージ言語では`IUnknown::QueryInterface()`の代わりにキャスト構文を使用することができ、COMコクラスが指定のインターフェイスを実装していなかった場合は`System.InvalidCastException`がスローされる。

## 脚注

### 注釈

### 出典

## 関連項目
* Component Object Model