2006 年 6 月 20 日 20 時 13 分

COM メソッドのシグネチャ


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


IColumnProvider のメソッドは IUnknown を除くと 3 つ。
Initialize, GetColumnInfo, GetItemData の順だ。

まず、MSDN ライブラリを参考にし、
メソッドのシグネチャを調べる。

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/ifaces/icolumnprovider/icolumnprovider.asp

基本的に C++ 用の定義で書いてあるので、
C# 用に変換しなければならない。
これは、避けて通れない道だ。

注意したいのは、C++ のシグネチャだけでなく、
引数の「方向」も重要である。
COM では、引数の利用方向を明示するために、
[in] や [out] などの属性を持っている。
これもシグネチャと同時に調べる必要があるのだ。

[in] は、引数が呼び出し元から渡される事を示す。
つまり、引数には意味のある値が格納されている必要がある。

[out] は、引数がメソッドから返される事を示す。
メソッドは引数の値を参照してはいけない。
その代わりに引数の値に意味のある値を書き込む必要がある。
この特徴のため、一般的にポインタ型であることが多い。

[in] と [out] の両方が定義されている場合もある。
この場合、呼び出し元も値を設定し、
メソッドでも値の参照や設定等を行う事を意味する。

この属性は .NET にも引き継がれており、
InAttribute と OutAttribute として定義されている。
これらは COM の呼び出しの際に重要な役割を持つ。

シグネチャや方向属性は、昨日出てきた
IDL によって書かれたインタフェース定義があれば、
全て明確になるのだが、IColumnProvider 用の
IDL 定義が見つからないので、C++ シグネチャと、
MSDN ライブラリのヘルプによってこれを補うのだ。

では、順番に見ていこう。
まずは、Initialize メソッド。

    HRESULT Initialize(LPCSHCOLUMNINIT psci);

これを C# 用に変換するとこうなる。

    void Initialize([In] ref SHCOLUMNINIT psci);

HRESULT は COM のメソッドの戻り値の型であり、
エラーコードを格納する整数型である。
COM は、エラーが発生したときに、戻り値で示すのだ。

.NET では、HRESULT を自動的に例外に変換するため、
メソッドの戻り値は void で定義する。
COM のインタフェースを実装する際、
エラーを返す代わりに、例外を throw すればいいのだ。

次に LPCSHCOLUMNINIT だが、
これは Microsoft 流の C++ における型の別名だ。
これは、LP C SHCOLUMNINIT と分けて読む。
LP はポインタ、C は定数、SHCOLUMNINIT は構造体の名前だ。
つまり、「const SHCOLUMNINIT *」 ということだ。

この引数は、SHCOLUMNINIT 構造体へのポインタを受け取り、
メソッドでは構造体の値を変更せず、読み取るだけである。
これは、パラメータの説明を見ると、
psci が [in] であることからも分かる。

.NET では、SHCOLUMNINIT を構造体として宣言し、
その参照を渡すことになる。
C# で参照を渡す場合、前に ref キーワードを付ける。
ref は、暗黙的に [In][Out] を意味するが、
今回は値を受け取るだけなので、[In] を明示する。

SHCOLUMNINIT 構造体は後で考えよう。

次に、GetColumnInfo メソッドだ。

    HRESULT GetColumnInfo(
            DWORD dwIndex,
            SHCOLUMNINFO *psci);

dwIndex は [in], psci は [out] だ。

これはこうなる。

    void GetColumnInfo(
            uint dwIndex,
            out SHCOLUMNINFO psci);

DWORD は、C の unsigned long の別名だ。
Intel ベースの Windows では、
32 ビットの符号なし整数なので、C# では uint となる。

SHCOLUMNINFO も構造体であり、ポインタで渡されている。
Initialize のと異なり、SHCOLUMNINIT は [out] だ。
つまり、このメソッドが値を書き込む必要がある。

C# では参照を渡すもう 1 つのキーワード out がある。
out は、暗黙的に [Out] を意味するので丁度いいのだ。

また、out を指定しておくと、C# のコンパイラが、
このメソッド呼び出す際に引数を初期化しなくても
エラーとしないという特徴がある。ref ではエラーだ。
out の役割を考えれば理由は明確だ。

最後に、GetItemData メソッド。

    HRESULT GetItemData(
            LPCSHCOLUMNID pscid,
            LPCSHCOLUMNDATA pscd,
            VARIANT *pvarData);

pscid, pscd は [in], pvarData は [out] だ。

これはこうなる。

    void GetItemData(
            [In] ref SHCOLUMNID pscid,
            [In] ref SHCOLUMNDATA pscd,
            out object pvarData);

最初の 2 つの引数は上記と同じようなパターンだ。
VARIANT * は、VBA ではおなじみの Variant 型だ。
VARIANT は C# では、object として宣言することになる。
VARIANT へのポインタなら、object への参照となるのだ。

以上をまとめると、以下のようになる。

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

        // メソッドの順番は非常に重要
        // 必ず IDL 定義順に書くこと

        void Initialize(
                [In] ref SHCOLUMNINIT psci);

        void GetColumnInfo(
                uint dwIndex,
                out SHCOLUMNINFO psci);

        void GetItemData(
                [In] ref SHCOLUMNID pscid,
                [In] ref SHCOLUMNDATA pscd,
                out object pvarData);

    }



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