VB.NETとDLLのインターフェース

 それほど、Windows関連の仕事をするわけではないし、あったとしても、コンソールアプリが主流(?)なのですが、 たまには、Windowsなアプリを作る必要に迫られることがあります。 ちょこちょことしたWindowsアプリには、VisualBASICが開発も早く、楽だったのですが、世の中は既にVB.NETの時代になっています。うちの周りでもVB6からVB.NETに移行できない難民が発生していますが、Microsoftに文句いっても埒があかないし、VB.NETを使うための作業をするしかありません。


VB.NETからDLLを呼び出す

 VB.NETからDLLを呼び出すためには、VB時代とは違う宣言が必要なようです。 うちでは下のような宣言を使っています。新しいクラスを追加して、その中に下のようなコードを書きます。 後で作るDLLの名前は、native32.dllとしています。 で、このDLLはパスが通っているところに置かれているものとします。

    'DLLに対する汎用アクセスインターフェース
    <DllImport("native32.dll", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.Cdecl)> _
    Function n_cmd(ByVal cmd As Integer, ByVal prm As Integer) As Integer
    End Function

    'DLLへの文字列出力(DLLコール)
    <DllImport("native32.dll", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.Cdecl)> _
    Function n_setbuf(ByVal str As String) As Int32
    End Function

    'DLLから文字列入力(DLLコール)
    <DllImport("native32.dll", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.Cdecl)> _
    Function n_getbuf(ByVal str As System.Text.StringBuilder) As Int32
    End Function

    'DLLへの実数出力(DLLコール)
    <DllImport("native32.dll", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.Cdecl)> _
    Function n_setdbl(ByVal idx As Integer,ByVal str As Double) As Int32
    End Function

    'DLLから実数入力(DLLコール)
    <DllImport("native32.dll", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.Cdecl)> _
    Function n_getdbl(ByVal idx As Integer) As Double
    End Function

    'DLLから文字列を受け取るためのバッファ
    Public n_sbuf As New System.Text.StringBuilder(1024, 1024)

    'DLLに文字列を渡すためのVB関数
    Public Function n_putstr(ByVal s As String) As Int32
        setbuf(s)
    End Function
    'DLLから文字列を受け取るVB関数
    Public Function n_getstr() As String
        getstrbuf(n_sbuf)
        getstr = n_sbuf.ToString
    End Function

 VB.NETもそうですし、Javaもそうなんですが、どうも長ったらしい「呪文」を延々記述させられる言語って、好きになれません。それに覚えることがあまりに多すぎて、本質的なプログラミング部分に割ける時間が無くなるのも問題です。 そこで、この手の奴らには、ラッパーを被せたり、汎用的なインターフェースを考えて、「鼻をつまんで」そのトンネルを走るようにしています。今回の場合は、汎用的な5つの関数を通してWin32ネイティブのC言語までたどり着くように考えています。実際5つといっても、そのうち4つは、文字列の入出力と、実数の入出力に使いますから、プログラムの実行そのものは、1つの関数を通過させ、そこから処理します。

 VB.NETで唯一、うまくできているなと思ったのは、文字列の受け渡しをやると、ちゃんと文字コードがシフトJISから、ユニコードに変換されるところです。これを自前で行うと大きなテーブルを持って、結構面倒なことになりますが、この機能のおかげで、日本語文字列の受け渡しも簡単に行えます。

DLLの実装方針

 で、DLLなんですが、1キロバイト以上の文字列受け渡しはしない(もしするなら、上のStringBuilder()内の数値を適当にイジってください)。実数や文字列は専用受け渡し関数で1つづつ行う。という方針で実装します。32ビット整数は、メインの実行関数に常時1つパラメータが渡せるようにしてあります。32ビット整数は、メインの実行関数の戻り値で戻します。

 かなり強烈は制限のように見えますが、2つ3つ仕事をやってみたところ、特に問題になるような制限ではありませんでした。もっとも応用例によっては問題になるかもしれませんが。

//
//	native32.c STUB
//	copyright(c)2011 by 無作為研究所 http://www.faicha.com
//

#include "windows.h"
#define DLL_INT	__declspec(dllexport) int
#define DLL_DBL	__declspec(dllexport) double

char strbuf[1024];		// 文字列やり取り用
double dblbuf[16];		// 実数やりとり用

//	コマンドの実行
DLL_INT n_cmd(int cmd,int prm)
	{
	int rtn = 0;

	switch(cmd)
		{
		case 1000:	// テストコマンド
			rtn = prm + 1;
			break;
		}
	return rtn;
	}

//	文字列を返すための処理
DLL_INT n_getbuf(LPSTR str)
	{
	strcpy(str,strbuf);
	return 0;
	}

//	文字列を設定するための処理
DLL_INT n_setbuf(LPSTR str)
	{
	strcpy(strbuf,str);
	return 0;
	}

//	実数を返すための処理
DLL_DBL n_getdbl(int idx)
	{
	return dblbuf[idx];
	}

//	実数を設定するための処理
DLL_INT n_setdbl(int idx,double dat)
	{
	dblbuf[idx] = dat;
	return 0;
	}

//	DLLエントリー
BOOL WINAPI DllMain(HINSTANCE ins,DWORD rsn,LPVOID rsv)
	{
	switch(rsn)
		{
		case DLL_PROCESS_ATTACH:
			break;
		case DLL_THREAD_ATTACH:
			break;
		case DLL_PROCESS_DETACH:
			break;
		case DLL_THREAD_DETACH:
			break;
		}
	return TRUE;
	}

 このコードのn_cmd()関数内で、caseを増やしていって各処理を記述していけば、普通にC言語でプログラム可能です。VB.NETにPictureBoxを貼って、wndをこのインターフェースで渡し、win32APIでゴリゴリ描画することもできますし、起動時にスレッドを立てて、C言語側で、TCP/IPのサービスを行ったりすることも可能です。


無作為研究所トップページに戻る

copyright(c)2011 by MUSAKUI-LABO