お世話になります。
WinXP、VC6、ダイアログベースの環境です。
現在シリアル入出力のアプリケーションを作成中です。
仮想COMに特定の文字を送信して、返ってきた文字を表示する、という内容です。
CreateFile、WriteFile、ReadFileを使用しているのですが
ReadFileの部分でアプリが固まってしまいます。
(その他の部分もうまくいっているかは?ですが・・。)
原因についてご教授いただけませんでしょうか。
<ソース>
HANDLE hComm;
DCB myDCB;
hComm = CreateFile( COM3, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, 0, NULL );
BuildCommDCB( 115200,n,8,1, &myDCB );
SetCommState( hComm, &myDCB );
char wbuf[10] = WON\n, rbuf[50];
DWORD wbyte, rbyte;
WriteFile( hComm, wbuf, 10, &wbyte, NULL );
ReadFile( hComm, &rbuf, sizeof( rbuf ), &rbyte, NULL );
m_StcResult.SetWindowText( rbuf );
CloseHandle( hComm );
シリアル通信をするのは初めてで、全体の流れもこれで合っているのか
正直わかりません。
お手数ですが、よろしくお願いいたします。
通信仕様ってのは通信ターゲットとなる機器の仕様なので、我々読者にはわからない。
なのでそのプログラムが「正しい」かどうかは、我々では知りえないんだが・・・
提示プログラムは ReadFile を同期方式で使っている。ということは、
・ ReadFile が終了する条件が満たされるまで ReadFile から帰ってこない
のは正しい動きだ。
> ReadFileの部分でアプリが固まってしまいます。
そうなるように組んでいるのだから当然の動き。
どうなってほしいのか解説してくんないと
どうすればよいかの提案は不可能。
既に指摘されていますが……。
http://msdn.microsoft.com/ja-jp/library/cc429679.aspx
より。
>通信デバイスからデータを読み取っているとき、ReadFile 関数の動作は、現在の通信タ
イムアウト値の影響を受けます。SetCommTimeouts と GetCommTimeouts の各関数は、こ
の通信タイムアウト値の設定と取得を行います。タイムアウト値の設定を怠ると、予測で
きない結果が生じることがあります。通信タイムアウトの詳細については、MSDN ライブ
ラリの「」構造体を参照してください。
日本語版だと構造体の名前とリンクが入っていませんねぇ…。
構造体名はCOMMTIMEOUTSです。
英語版は下記。
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx
>シリアル通信をするのは初めてで、全体の流れもこれで合っているのか
>正直わかりません
「VC シリアル通信」とかで検索するといくつか見つかると思いますけどね…。
http://www.softist.com/programming/simple232c/simple232c.htm
http://homepage2.nifty.com/nonnon/SoftSample/VC/SampleRs232c.html
そうですね。
1.通信を行うのであれば、その対象となる相手が必要ですね。
2.送信したデータは、相手に届くのであって、自分に戻ることはありません。
3.従って、これらを開発デバッグするには、一般に、相互に接続された
2台PCが必要になります。
4.又は正常に動作する通信可能な機器を相手としなければなりません。
以上の様に、ReadFile()をデバッグするには、相手から、
何らかの送信データが送られて来なければなりません。
さて、本プログラムのデバッグにおいて
5.「相手」には何がつながっていますか?。
6.「相手」は何を送信してきますか?。
などが、気になるところです。
まあいつものパターンで、俺がなにかアドバイスするとしたら
・本当に相手とつながっているか(クロス・ストレートは大丈夫か)
・フロー制御は相手機器の仕様どおりか
・送受信は専用スレッドでしろ、 UI スレッドでするな
・信頼できるラインモニターを用意しろ
あたりが定番になるわけだ。
追加しておくなら
・CreateFile の対象は \\.\COM3 にしておけ
sprintf(portname, \\\\.\\COM%d, comno); で作るといい
・WriteFile で10文字送っていいの?この例だと4なんぢゃないの?
・直に COM にアクセスせず、1段階抽象層をかましておくと
RS232 だけでなく USB や TCP や UDP に対応しやすかったりするよ
ってところだろうか。
具体的にどうプログラムすべきか、が知りたいのであれば
案件というか仕様というか、そのへんの解説しておくんなまし。
みなさんご丁寧なアドバイスありがとうございます。
リンク先含めまだ勉強中です・・。
接続先は画像処理装置で、開始文字を送信して
読み取った文字を返信する、という仕様です。
装置が反応していないところを見ると、送信がまだうまくできていないように
思います。
インターフェイスはUSBを使用します。
>・直に COM にアクセスせず、1段階抽象層をかましておくと
> RS232 だけでなく USB や TCP や UDP に対応しやすかったりするよ
抽象層をかますとはどういうことでしょうか。
232CとUSBで記述は変わってきますか。
質問ばかりですみません(^^;)
> インターフェイスはUSBを使用します。
シリアルポート (RS232/EIA574) ぢゃなかったの?
USB-COM 変換機を使うのであれば、ソフト上は RS232/EIA574 として扱うことになる
ので、そうであるものとして話を継続するものとして:
似たような装置があったとして、物理的接続方法ってのは
・ EIA574(RS232) で接続
・ USB で直結(マウスとか USB メモリとかみたいに)
・ IEEE1394 で直結
・ネットワーク接続で TCP アクセスする
・その他
などなどいっぱいありうるわけだ。けど、
・PC→機器に読み取りコマンドを送ると
・機器→PCにデータが返却されるので取り込む
とかそういう手順は同じであることが多い。
ならば
scanner.SendCommand(WON); // WON というコマンドを送る
scanner.ReceiveData(buffer, 50); // 50 バイト以下のデータを取り込む
のように、ロジック層と通信下請け層を分離しておき、
ロジック層はベタベタに RS232 に依存しないようにしておけば
後からの拡張が簡単になるであろう。ということ。
>232CとUSBで記述は変わってきますか。
現在のパソコンには、いわゆるRS-232Cの標準コネクタ(D-Sub等)が
無いのが普通ですね。
ハード的にもRS-232Cを担当するチップは実装されていません。
従って、現在では純粋にハード的にRS-232Cインターフェースで接続する機器は
まれなわけです。それらは一般的にBluetoothやUSBで接続されており、
それでインターフェースするのが基本となります。
とは言うものの、BluetoothやUSBを「まんま」オープンし通信するのは
ソフト的に結構手間です。そこで、その機器のメーカーから
提供されている「仮想COMポートドライバ」等を使って、
「なんちゃってRS-232C」接続された機器として使えるようにしたわけですね。
これを「仮想COMポート」による接続といいます。
この場合、機器側はUSBなどでつながっていると認識しており、
一方ソフト側はRS-232Cでつながっていると思っているので、
この認識の齟齬が問題になる場合があります。
これとは別に、そもそもRS-232Cでしかインターフェースできない旧式の機器を、
PCに接続することも可能です。「変換ケーブル」を使う場合ですね。
この場合も「仮想COMポート」を使いますが、中間の変換を無視すれば、
双方ともRS-232C接続との認識なので齟齬はあまり無く、
問題は発生しにくいわけです。
さて、このように「仮想COMポート」で手抜き接続ができるようになっているのは
良いのですが、デフォルトで設定されているポート番号が、大抵3番
だったりして、複数の機器を接続するとバッティングする場合があります。
これが問題になる場合もありますね。
従って、USB機器の場合、提供されている純粋なUSB接続方法が
あるのならば、それを使うべきであって、「仮想COMポート」を使うのは、
一般的には推薦できない、ということになります。
機器との接続はUSB直結です。
また、付属の通信ソフトにて接続確認もできていますので
あとはプログラムの問題だと考えているのですが・・・。
>あとはプログラムの問題だと考えているのですが・・・。
既に回答者の方から、いくつかの指摘がされました。
特に、tetrapodさんの指摘の中には「明らかなバグの指摘」もありました。
それについてはどのようにしましたでしょうか(質問)。
教えていただいた、
http://homepage2.nifty.com/nonnon/SoftSample/VC/SampleRs232c.html
を参考(というかまんま)にして変更しました。
char send[32];
DWORD dwSize;
sprintf( send, WON );
if( WriteFile(hComm, send, strlen( send ), &dwSize, NULL ) == FALSE ){
MessageBox( Write Err );
}
ポートのオープンや設定ではTRUEが返ってきてますが、
上記WriteFile()でFALSEになります。
ポートの設定が良くないのか、色々試しています。
機器の仕様の確認をしたい。
・当該機器と PC は USB 線で直結している
・当該機器がソフト的には COM3 の先につながっているように見える
(機器が仮想 COM ポート機能を内蔵している)
ということ?
ならばまず「ハイパーターミナル」「 TeraTerm 」等の、実績ある COM 通信ソフトで
通信してみるといい。こういう信頼できる第三者製汎用ソフトを使うと、
機器付属の専用ソフトが暗黙のうちに行っているであろう、
通信条件設定やフロー制御設定やエコー設定を自分で行う必要が生じる。
そうするとその辺の理解ができるはず。
・・・理解できていないことはプログラムにできないので・・・
あと別なサンプルを挙げとく。
Microsoft 謹製 MTTTY サンプル
http://msdn.microsoft.com/en-us/library/ms810467.aspx
Microsoft 謹製 VCTERM サンプル
http://msdn.microsoft.com/en-us/library/ms386500.aspx
>tetrapodさんの指摘の中には「明らかなバグの指摘」もありました。
4バイトぢゃねーの?って奴?この件に関しては俺はバグと断言する気はないよ。
機器の仕様が
・入力は常に固定バイト数(この例では10バイト)
・実際に使用するのは先頭側バイトのみ(ないしは改行コードまで)
って可能性も0ではないので。
なんだか先は長そうだな・・・限りなく1抜けた感がする。
>if( WriteFile(hComm, send, strlen( send ), &dwSize, NULL ) == FALSE ){
> MessageBox( Write Err );
>}
>ポートのオープンや設定ではTRUEが返ってきてますが、
>上記WriteFile()でFALSEになります。
でしたら…せめてGetLastError()でどんなエラーだったのか?
くらいは確認しましょう。
通信内容に妙な制御コードが入らないのでしたら、tetrapodさんが書かれているようにハ
イパーターミナルやTeraTerm使うのもありです。
制御コードが入り込むとか、そういう場合は……シリアル通信モニタ系のソフトとかが必要
かも知れません。
http://www.vector.co.jp/vpack/filearea/win/hardware/comm/
接続にシリアルケーブルを使用しているのであれば、そういう機器を使う。というのもア
リです。
http://www.lineeye.co.jp/
高いですけどね。
>・当該機器と PC は USB 線で直結している
⇒はい。
>・当該機器がソフト的には COM3 の先につながっているように見える
(機器が仮想 COM ポート機能を内蔵している)
⇒デバイスマネージャで確認するとCOM3です。
>なんだか先は長そうだな・・・限りなく1抜けた感がする。
⇒そんなこと言わないで下さいよぉ・・(涙)
>でしたら…せめてGetLastError()でどんなエラーだったのか?
>くらいは確認しましょう。
⇒そういう方法を教えていただけると非常に助かります。
確認しましたところ、997(重複したIO処理を実行している?)でした。
色々情報をいただいているので、整理しつつ
やっていこうと思います。
本題に関係なく小ネタを
HyperTerminal/TeraTerm でできるかどうかは確認していないけど
ALT+テンキーの数字キーでコントロールコードでもなんでも入れられる。
コードは10進数表記。
ALT を押す→押したままテンキーの数字キーで 65 → ALT を離す
で、大文字の A が入力できる(XP のメモ帳で確認済み)
ALT を押す→押したままテンキーの数字キーで 8 → ALT を離す
で、バックスペースが入力できる
13 でも 27 でもお好きな制御コードを入力できる。
# IE 等ではダメ。
HyperTerminal の「テキストファイルの送信」でバイナリファイルを送れば、
コントロールコードを送れる。
# バイナリコードを受信するほうは表示が化け化けで面倒だが。