このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
IColumnProvider のメソッドは IUnknown を除くと 3 つ。
Initialize, GetColumnInfo, GetItemData の順だ。
まず、MSDN ライブラリを参考にし、
メソッドのシグネチャを調べる。
基本的に 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);
}