2006 年 6 月 19 日 18 時 24 分

IColumnProvider の枠組み


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


.NET では、API だけでなく、COM インタフェースも
ソースに定義することで利用することができる。

Visual Basic で API を利用する時は、
Declare 文で定義して利用していたが、
.NET では、これらの定義になるべく特殊な構文を使わず、
通常と同じようなソースの定義に対して、
「属性」を付けることによってコンパイラに指示をだす。

さて、IColumnProvider を .NET に移植しよう。
プロジェクトに IColumnProvider.cs を追加し、
IColumnProvider の基本的な記述を書く。

IColumnProvider は、COM のインタフェースなので、
IColumnProvider に属性を付けることでコンパイラに示す。

COM のインタフェース定義に必須の属性は、
System.Runtime.InteropServices に定義されている、
ComImportAttribute, InterfaceTypeAttribute,
GuidAttribute の 3 つである。

ComImportAttribute は、インタフェースやクラスが、
COM 由来で外部に定義されていることを示し、
コンパイラに特別な処理を行うように指示する。

InterfaceTypeAttribute は、
COM インタフェースの種類を指定する。
シェルのインターフェースは IUnknown ベースであり、
IColumnProvider も IUnknown から直接派生する。

複雑になるのでここでは詳細は書かないが、
あらゆる COM インタフェースは IUnknown から派生し、
ものによっては、さらに派生した IDispatch などの、
特殊なインタフェースから派生するものもあるのだ。

GuidAttribute は、一意となる ID を指定する。
COM のインタフェースは、必ずそれぞれに固有の
InterfaceID (IID) と呼ばれる GUID 値を持ち、
その値で個々のインタフェースを識別することになっている。

シェルのインタフェースは、C++ 言語向けに、
ヘッダファイルとして提供されており、
shlguid.h というファイルに IID の定義がある。

Visual C++ や Platform SDK をインストールしているなら、
Include フォルダに、shlguid.h があるはずだ。
開いて、「IID_IColumnProvider」を調べる。
なければ、Google で調べると見つかる場合もある。

調べると、IColumnProvider の IID は、
「E8025004-1C42-11d2-BE2C-00A0C9A83DA1」だった。

では、これらの情報を元に、基本的な枠組みを作る。

まず、プロジェクト内に Interop フォルダを作ろう。
自分のコードと、シェルの定義が混ざると煩雑になるので、
シェルの定義は Interop 名前空間に分類しておこう。

また、フォルダを作っておくと、IDE では
そのまま名前空間として既定で定義してくれるのだ。

では、Interop 内に新しく IColumnProvider.cs を追加する。
IDE ではなぜかインタフェースが直接作成できないので、
「クラス」の追加で枠組みを追加し、
class を interface に書き換えてコンストラクタを消す。
そして、上記の属性を付与する。

    using System;
    using System.Runtime.InteropServices;

    namespace LoaferShellEx.Interop {

        [ComImport]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        [Guid("E8025004-1C42-11d2-BE2C-00A0C9A83DA1")]
        public interface IColumnProvider {


        }

    }

C# では属性は、大括弧内に書く。
属性名を書き、パラメータがあればその後ろに並べて書く。
なお、属性クラスの Attribute は省略できる。

属性は、個別に大括弧に書くこともできるし、
1 つの大括弧内にカンマで区切って並べることもできるが、
属性は enum と違い最後の属性の後にカンマを付けられない。
なので、俺は個人的に個別の括弧で書く癖がある。

        [
            ComImport,
            InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
            Guid("E8025004-1C42-11d2-BE2C-00A0C9A83DA1")
        ]

次に、メソッドを定義する。

IColumnProvider の定義を探そう。
MSDN ライブラリもしくは、Microsoft の Web ページで、
IColumnProvider の項目を調べる。
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/ifaces/icolumnprovider/icolumnprovider.asp

IColumnProvider は、GetColumnInfo, GetItemData,
Initialize の 3 つのメソッドを持つ事が分かる。

COM のインタフェースを定義する場合、
メソッドの順番が「非常に重要」である。
というのも、COM でのネイティブメソッドの呼び出しは、
メソッドのインデックス(順番・位置)で行われるからだ。
そこで、メソッドの順番を調べる。

ページの Interface Information の項目を見ると、
Header に「shlobj.h」と書いてある。

shlobj.h を開き IColumnProvider を調べてみると、
とりあえず、Initialize, GetColumnInfo, GetItemData
の順に並んでいることは分かる。

ちなみに、QueryInterface, AddRef, Release もあるが、
これは、上で書いた IUnknown のメソッドだ。
.NET で記述する場合、これらは省略することができる。
InterfaceTypeAttribute が自動的に処理をしてくれるのだ。

では、各メソッドを見ていこう、っと言いたい所だが、
大分長くなったので明日にしよう。



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