Swanman's Horizon

性欲をもてあましつつなんらかの話をするよ。

外部DLL関数をクラスメソッド化する。

これもAdvent Calendarネタにしようと思ってたけど、間に合わなかったので放出。

メンバーにしたい!

外部DLLから関数をインポートすると、どうしてもユニットの直下に宣言することになります。クラスのメンバーにすることはできません。
それでもしたいとなると、こういうコードを書くことになります。

interface

type
  TWin32API = class
  public
    class function MessageBox(hWnd: HWND; lpText, lpCaption: LPCWSTR; uType: UINT): Integer; static; inline;
  end;

implementation

function MessageBoxW(hWnd: HWND; lpText, lpCaption: LPCWSTR; uType: UINT): Integer; stdcall; external 'user32.dll' name 'MessageBoxW';

class function TWin32API.MessageBox;
begin
  Result := MessageBoxW(hWnd, lpText, lpCaption, uType);
end;

これでメンバーにできました。inlineでインライン展開されるので、メソッドという皮を被せたことによるオーバーヘッドもありません。

でもこれ、めんどくさいですよね?

そうなんです。面倒なんです。
じゃあどうするか。

合体!

interfaceは宣言を書くところ、implementationは実装を書くところですが、よく見たら実装が二つあるじゃないですか!
じゃあ合体しちゃいましょう。

interface

type
  TWin32API = class
  public
    class function MessageBox(hWnd: HWND; lpText, lpCaption: LPCWSTR; uType: UINT): Integer; stdcall; static;
  end;

implementation

class function TWin32API.MessageBox; external 'user32.dll' name 'MessageBoxW';

これで記述量を減らした上で、外部関数をメンバー化できました。

ポイント

外部DLLの関数をクラスメンバーにする上で必要なことはたったひとつです。静的クラスメソッドにする。これだけ。
以前もどこかで書きましたが、普通のクラスメソッドが暗黙の第一引数としてクラス参照を受け取るのに対し、静的クラスメソッドは普通の手続き/関数と何ら違いはありません。
何ら違いは無いので、普通にexternalを使って関数をインポートできてしまいます。*1

どこが便利なの?

Delphiは完全なオブジェクト指向ではないので、元から手続き/関数は溢れかえってますし、別にクラスに属さなくても使い勝手はぶっちゃけ大して変わりません。
じゃあメソッド化することで何が変わるのか。

Delphiのアイドル、RTTIちゃんだよー!

関数とメソッドでは大きな違いがひとつだけあります。RTTIが付くか付かないかです。
そう、上記のように外部関数をメソッドにすれば、RTTIが付くんです!
つまり外部関数をリストアップしたり、Invokeを使って実行時に動的にコールしたりということが超簡単にできるということ!テストにもめちゃくちゃ使える!やったー!

本日のまとめ

こういう裏技的な機能たのしいのでもっとください>エのつく会社

*1:静的クラスメソッド以外でもexternalを使って実装を外部に投げることはできます(が、パッケージ使った方が楽)