Dll作りは忘れました。

 はまりました。

 ある他社製品をCOMで利用するプログラムを何本か出しているのですが、それらに頬とフォルダー機能を設け、字度処理できるようにしてあります。

 実は、もともとはホットフォルダーの機能は考えていなかったのと、対象となる他社製品がシングルインスタンスでの動作しかできないので、まさか、複数のプログラムを同時に待ち受けし、自動処理をするなどとは思っていなかったわけです。

 ところが、先日、一台のコンピュータで、同時に、待ち受け自動処理をやりたいという話が出てきて、あれま、対象となる他社製品がシングルインスタンスなので、無理ですよと言って終わりにしたのですが.......。

 うーん、と考え、仕方ない、プロセス制御をするかということで、おとといから、共有メモリーを使用した、排他制御を考えていました。

 C#, .NET3.5の環境で開発したプログラムなので、どうかと思ったら、.NET4.0から、居優メモリーがNETで使えるとあり、NET3.5では、Win32APIを使うしかないのかと、それこそ、めんどくさいなぁ~という気持ちになってしまいました。

 でも、やると決めたので、さて、DLLを創るかと始めました。

 インターネットで、探しまわしても、なかなか、C#で共有メモリーを使用するというものがありませんん。どうしても、VC++の例ばかり.......。

 もう、ほとんど100%に近いくらい、C#での開発しかしていないので、DLLを作るといっても、すっかり忘れています。久しぶりに、北山さんの、「Win32API システムプログラミング with VisualC++ 6.0」を引っ張り出して、読み替え市という羽目になってしまいました。

 DEFやLIB、C#で使うには、どうすればいいんだっけと思いながら、ああでもなおこうでもないと悪戦苦闘。

 まず作ったものをC#でのテストプログラムにリンクして実行すると、「エントリーポイントが見当たりません」エラーとなってしまいました。

おかしいな、きちんとDllMainも書いているのに、と思ったら、それぞれの関数のエントリーポイントがつかめないのだということが分かり、DependencyWalkerで作成したdllを見てみると、何やら、関数名に、@YAなんちゃらかんちゃらと出てきて、ありゃぁ、これはおかしいと気がつきました。

 DEFfで解決す量なことが書いてありましたが、C#で、そもそもDEFファイルが使えるの? 使えたとしても、余分なファイルをいちいち、DLLの内容を変えるたびに、忘れないようにするということを考えると、他に何かないのと思ってしまいました。

 で、結局、関数名は、Cの形式であることを宣言すればいいのだというkとがわかり、何とかエントリーポイントエラーは、解消できました。

 さぁ、これでうまくいくと思ったら、共有メモリーの内容を読みだして、本体に「char*」で返すところで、致命的なエラーとなってしまいました。

 おかしいなぁ、共有メモリーへの書き込みでは、文字列をポインターで渡して、うまく書けているのに????

 C#でのDllimport属性定義がおかしいのかなぁと思ったのですが、@ITで見ると、char*は、C#ではStringBuilder②対応と書いてあるので、StringBuilder変数を用意し、やってみても、うまくいきません。

 おかしいなぁ、書き込みでの文字列を渡すには、stringでうまくいっているのに、、なぜ? なぜ?

 あれを変えこれを変え、ようやく気がつきました。

 いつもの癖で、StringBuilderをパラメータなしでnewして渡しているのがいけなかったのに気がつきました。

 サイズを指定して、newし、そのサイズとともにdllに渡すことで、要約、エラーがなくなり、ほっとしました。

 これで終わりかと思ったら、共有メモリーを書くときに、ロックをかけないとおかしくなるので、ミューテックスで書き込みの排他制御をおこなわなくてはと思い立ち、結局、モジュール作成だけで、1日以上を費やしてしまいました。

 .NET4.0になれば、こんな苦労しなくて済んだんですがねぇ~。

 また、dll作成の必要性がないので、忘れてしまうんですよ、きっと。

 いやぁですね。

*2010/1/7

 ちなみに、おかしかったdLL側の部分は、


char * GetString(void)
{
char buf[100];

~~~ 共有メモリーから値をbuf②コピー ~~~~

return buf;
}

というような関数を、公開し、C#サイドで、

[DllImport(xxxxx.dll)]
extern public static string GetString();

といったような宣言をして、最初は行ったのですが、見事、システムエラーで落ちてしまいました。

この戻り値の型をStringBuilderに変更しても、同じだったので、困ってしまったわけです。


最終的に、忘れましたが、どなたかのブログで、StringBuilderを引数として利用しているサンプルがあり、これをつかわさせていただいたということになります。

 基本的には、

 int GetString(char *srs, int length)
{



}

という形にし、使う側で、文字列をコピーする領域と大きさを渡してもらうという方法にしたわけです。


[DllImport(xxxxx.dll)]
extern public static int GetString(StringBuilder src, int length);


として、宣言し、

使う前に、

StringBuilder buf = new StringBuilder(100);

と、両機を確保し、

int result = GetString(buf, 100);

のようにして、データの受け渡しを行うことで、問題の解決となったわけです。

ただし、StringBuilderも、最初は、単に、new StringBuilder()として、領域を明示的に確保しない形で、パラメータの設定をしていたので、エラーとなってしまいました。

大きさを明示的に確保しないといけなかったわけでした。


Visual C++.NETではじめるWin32APIシステムプログラミング
カットシステム
北山 洋幸

ユーザレビュー:
非常に分かりやすいと ...
Windowsのシス ...
レアな本!Win32 ...
amazon.co.jpで買う
Amazonアソシエイト by ウェブリブログ商品ポータルで情報を見る


この記事へのトラックバック