LIFULL Creators Blog

「株式会社LIFULL(ライフル)」の社員によるブログです。

Simple Example: C#からC++用のDLLを使ってシリアル通信を行う

Apple原理主義者の大坪です。何故Apple原理主義者がC#を使うか?私は狂信的なApple原理主義者ですが、現実主義者でもあるのです。必要があればなんでも使ってやろうじゃないの。

とはいえ

最近昔を思い返すことが多い。当時は新しい環境に移る時まず本を買ったものです。しかし最近はGoogle先生にお伺いをたてればあれこれ情報が手に入る。とはいえ新しい環境に移るときは

「そもそも何のキーワードで探せばよいのか」

という問題にぶつかる。WindowsのPC用アプリケーションってなんて呼ばれているのか。(Macなら"Mac OS X Application"ですむのですが)

というレベルなので、今回書く内容は多分最適とか正解からはほど遠い物で、「今とりあえずこれで動いている」というものです。おまけにあれやこれやの理由から動くプロジェクトではなく、必要部分のソースだけを出しますがご容赦ください。

さて

試行錯誤の末まずたどり着いたのが"WinForm"なるキーワードです。しばらくそれを使ってあれこれやっていたのですが、どうも様子がおかしい。調べてみると最近はそれがWPFなるものに変わったらしい。でもって「ザムル」とかいろいろ恐ろしい言葉が並ぶ訳です。いつのまにかGUIはXMLで定義し、その背後のコードと分離するやり方が主流になったのだな、、などとXCodeで作る際にもInterface Builder(年がばれるか?)を使わない人間としては感慨に耽るのでした。

などとあれこれ言っている場合ではない。画面がなんとかできると、今度はC++で開発されたDLLライブラリの中の関数をC#からコールし、シリアル通信を行う必要があるのです。その部分のコード(一部)を記事の末尾に示します。ちなみにここに示したコードは「駄目なコードでもないよりはマシ。少なくとも出発点として使ってもらえるだろう」ポリシーの元公開しておりますので、左様ご承知頂きたく。

このコードは、ソーバル社のHRW-1000というRFIDリーダーからカードの情報を読み取るためのものです。ドライバはTRW社のサイトからダウンロードできます。ここからプロトコル仕様に従って、ごりごり通信を行うこともできるのでしょうか、せっかくWindowsで開発しているので、ソーバル社から提供されているDLLライブラリを利用して通信します。

やりたいことはなにか?言葉で書けば

・デバイスをオープンしてCOMポートで通信できるようにする

・「カードを読み取ってね」というコマンドをbyte列にしてリーダーに送信する

・読み取った結果を、byte列としてリーダーから受信する。

明確だし、簡単に聞こえますね。しかしこうしたハードウェアに近いところの処理というのは、その環境特有のあれこれをしないと動いてくれない。Apple原理主義者がつまづいた点を以下に列挙します。

  1. C#からC++用のDLLを呼ぶ時には、DLLImportというものを使わなくてはならない。

それまでもC#用のDLLを使う事があったのですが「参照の追加」をすればなんとかなりました。しかしC++用のDLLではそれだけではだめ。DLLImportで場所を指定する必要があります。

DllimportでGoogle先生にお伺いを立てると、シンプルな例がいくつもでてきます。そうかそうか、と

素直に

DLLImport("rfidsobal.dll);

と書いたのはだめで、ここに書いたように"CallingConvention = CallingConvention.Cdecl"とパラメータを指定する必要があるようです。なぜかというと、このDLLが「アンマネージドコード」と呼ばれるものであるため。この場合はWin32.APIを用いているようです。

  1. ライブラリとのパラメータ変換

次の問題はパラーメータの型変換です。メインのプログラムはC#で、DLLはC++用のもの。そりゃ言語も違うから型も違うだろう。ではどうしよう、とGoogle先生に聞いてみるとこんなページがヒットする。とても解りやすくかかれているのですが、ここだけ観ていればOKというほど話はストレートにいかない。

今回使うAPIでは受信データをcharにいれてくれることになっています。なるほど、ではcharは、、、そうかSystem.Text.StringBuilderにすればいいんだ、とやってみると何故か最初の一文字しか受信できない。これは困った。

後から考えれば当たり前の話で、APIではchar*になってますけど、具体的にはbyteでデータをつめて返してくれるわけです。それはもちろん文字列ではない。しかしStringBuilderで受けると「ここに入ってるのは、当然文字コードなんだよねー」と「解釈」されてしまいます。結果として、どうやっても最初の一文字しか読み取れない。(2byte目にnullがあったんだと思います)

ではどうするか、というわけでまたあちこちさがしてたどり着いたのがprivate void checkResult()に書いたような方法です。

多分これはbyte配列をそのまま受け取るようなことをしているのだと。これでめでたくデータ受信ができました。

では私はなぜApple原理主義者としての矜持を捨ててまでWindowsでプログラムを書き、RFIDリーダーと格闘しているか。その理由についてはいつかお話する機会もあろうかとおもいます。