COMのインターフェース

D言語を使っていると、C言語のライブラリとか、COM(Windows)とかを使いたくなることが有ります。だれかが提供してくれている場合はいいですが、どうしても書かざるをえない場合もあります。
そんな時にはバインディングを書いたりするのですが、今回は、COMインターフェースのバインディングの書き方(とサボる方法)を少し説明します。

例として。[ID2DFactory](https://msdn.microsoft.com/en-us/library/dd371246%28v=vs.85%29.aspx)

```d:d2d1.d
interface ID2D1Factory: IUnknown
{
	HRESULT ReloadSystemMetrics();
	void GetDesktopDpi(FLOAT *dpiX, FLOAT *dpiY);
	HRESULT CreateRectangleGeometry(const D2D1_RECT_F* rectangle, ID2D1RectangleGeometry* rectangleGeometry);
	void foo04(); //HRESULT CreateRoundedRectangleGeometry(const D2D1_ROUNDED_RECT *roundedRectangle, ID2D1RoundedRectangleGeometry **roundedRectangleGeometry);
	void foo05(); //HRESULT CreateEllipseGeometry(const D2D1_ELLIPSE *ellipse, ID2D1EllipseGeometry **ellipseGeometry);
	void foo06(); //HRESULT CreateGeometryGroup(D2D1_FILL_MODE fillMode, ID2D1Geometry **geometries, UINT geometriesCount, ID2D1GeometryGroup **geometryGroup);
	HRESULT CreateTransformedGeometry(ID2D1Geometry sourceGeometry, const D2D1_MATRIX_3X2_F* transform, ID2D1TransformedGeometry* transformedGeometry);
	HRESULT CreatePathGeometry(ID2D1PathGeometry* pathGeometry);
	void foo09(); //HRESULT CreateStrokeStyle(const D2D1_STROKE_STYLE_PROPERTIES *strokeStyleProperties, const FLOAT *dashes, UINT dashesCount, ID2D1StrokeStyle **strokeStyle);
	void foo10(); //HRESULT CreateDrawingStateBlock(const D2D1_DRAWING_STATE_DESCRIPTION *drawingStateDescription, IDWriteRenderingParams *textRenderingParams, ID2D1DrawingStateBlock **drawingStateBlock);
	HRESULT CreateWicBitmapRenderTarget(IWICBitmap target, const D2D1_RENDER_TARGET_PROPERTIES* renderTargetProperties, ID2D1RenderTarget* renderTarget);
	HRESULT CreateHwndRenderTarget(const D2D1_RENDER_TARGET_PROPERTIES* renderTargetProperties, const D2D1_HWND_RENDER_TARGET_PROPERTIES* hwndRenderTargetProperties, ID2D1HwndRenderTarget* hwndRenderTarget);
	void foo13(); //HRESULT CreateDxgiSurfaceRenderTarget(IDXGISurface *dxgiSurface, const D2D1_RENDER_TARGET_PROPERTIES *renderTargetProperties, ID2D1RenderTarget **renderTarget);
	HRESULT CreateDCRenderTarget(const D2D1_RENDER_TARGET_PROPERTIES* renderTargetProperties, ID2D1DCRenderTarget *dcRenderTarget);

final:
	HRESULT CreateRectangleGeometry(const ref D2D1_RECT_F rectangle, ref ID2D1RectangleGeometry rectangleGeometry) { return CreateRectangleGeometry(&rectangle, &rectangleGeometry); }

}
```

こんなかんじです。
ポイントは以下の点。

### 使わない関数は void fooXX(); とでもしておく(これ重要)。
重要なのは、仮想関数テーブルの枠を用意してあげることです。所定の場所に所定の関数ポインタを入れられる枠さえあれば、使わないなら別に名前はなんでもいいのです。
たとえば、サボらない場合、`CreateRectangleGeometry`関数を定義しようとするだけで、[`ID2D1RectangleGeometry`](https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd371561%28v=vs.85%29.aspx)インターフェースの定義が必要になります。それを定義するには[`ID2D1Geometry`](https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd316578%28v=vs.85%29.aspx)が必要で、それには[`ID2D1Resource`](https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd316908%28v=vs.85%29.aspx)とか[`ID2D1SimplifiedGeometrySink`](https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd316919%28v=vs.85%29.aspx)とかが必要で…と自分で定義しなければならないものが無限に発散していきます。人間の寿命は80年ほどしか無いので、時間は大切にするべきです。サボりましょう。

### I～から始まるのはinterface。ポインタをひとつ外す。
たとえば、`CreateRectangleGeometry`関数の第二引数には`ID2D1RectangleGeometry`インターフェースを保持する変数へのポインタを指定する必要があります。C++では仮引数の型が`ID2D1RectangleGeometry**`となっているところ、D言語では`ID2D1RectangleGeometry*`と、ポインタをひとつ外す必要があります。これは、C++のインターフェース(純粋仮想関数のみ含まれる構造体/クラス)は値型で、D言語のインターフェースは参照型であるためです。参照型の正体は、ただのポインタです。

```cpp:d2d1.h
HRESULT CreateRectangleGeometry(const D2D1_RECT_F* rectangle, ID2D1RectangleGeometry** rectangleGeometry);
```

```d:d2d1.d
HRESULT CreateRectangleGeometry(const D2D1_RECT_F* rectangle, ID2D1RectangleGeometry* rectangleGeometry);
```

### final:のあとは仮想関数にならない。
ので、ヘルパ関数を書くことができます。上記例はC++だと

```cpp:d2d1.h
HRESULT CreateRectangleGeometry(const D2D1_RECT_F &rectangle, ID2D1RectangleGeometry **rectangleGeometry)
{ return CreateRectangleGeometry(&rectangle, rectangleGeometry); }
```

となるところ、D言語で

```d:d2d1.d
HRESULT CreateRectangleGeometry(const ref D2D1_RECT_F rectangle, out ID2D1RectangleGeometry rectangleGeometry)
{ return CreateRectangleGeometry(&rectangle, &rectangleGeometry); }
```

という感じに書き換えたもの。


とにかくCOMはインターフェースの量が多い。こればかりはVisualStudioとかWindowsSDKとかインストールするだけで利用できるようになるC++が正直羨ましい。
一応D言語でも上記のようにバインディングを作ればCOMのあれそれを利用できるようになります。