シリアルポート(COMポート)の操作方法
その3 非同期通信の巻
UINT SimReadThread( LPVOID pParam // [IN] 未使用 ) { BOOL Bool_Ret; // 関数戻り値 ULONG Read_Size; // 読み込みバイト数 static char ReadBuff[10]; // 読み込みバッファ OVERLAPPED osReader = {0}; // 非同期読み込み用構造体 BOOL fWaitingOnRead = FALSE; // 非同期中メッセージ待ち状態 BOOL Loop_End = FALSE; // 処理終了状態 DWORD dwRes; // 非同期読み込み完了待ち関数戻り値 //----------------------------------------------------------------------------- /// 端末からの電文受信処理 //----------------------------------------------------------------------------- // 電文受信待ちループ while( !Loop_End ) { // 非同期処理の割り込み用イベントの生成 // ?よく分からないがこのまま記述すること
osReader.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
// イベント生成失敗 if( osReader.hEvent == NULL ) { // エラーメッセージ表示 SimErrMsg( "%s[%d] 受信用イベント生成失敗(%d)", 0, __file__, __line__, GetLastError() ); // ループ終了フラグON Loop_End = TRUE; break; } // 読み込みバッファ初期化 memset( ReadBuff,NULL,sizeof(ReadBuff) ); // 1バイトずつ読み込み // ?ここでは1バイト読み込めたかどうかに関わらず、ReadFile()関数はすぐに // ?リターンします。
Bool_Ret = ReadFile( E_ComPort, ReadBuff, 1, NULL, &osReader );
// 読み込み結果チェック // ?1バイト読み込めればTRUEが、読み込み完了待ちまたはエラーの時は // ?FALSEが返ります。 if( Bool_Ret == FALSE ) { // 読み込みエラー // ?読み込み完了待ちの時はGetLastError()関数の戻りにERROR_IO_PENDINGが // ?返されます。それ以外の場合は本当にエラーです。
if( GetLastError() != ERROR_IO_PENDING )
{ // エラーメッセージ表示 SimErrMsg( "%s[%d] 受信失敗(%d)", 0, __file__, __line__, GetLastError() ); // ループ終了フラグON Loop_End = TRUE; break; } else { // 非同期中メッセージ待ち状態をONにする fWaitingOnRead = TRUE; } } else { // 読み込みOK // バッファリング処理 Bool_Ret = SimReadSuccess( (char)ReadBuff[0] ); // バッファリング処理失敗 if( Bool_Ret != TRUE ) { // エラーメッセージ表示 SimErrMsg( "%s[%d] バッファリング処理失敗(%d)", 0, __file__, __line__, GetLastError() ); } } // 非同期読み込み完了までループ while( fWaitingOnRead ) { // ReadFile関数の非同期読み込み完了待ち // タイムアウトは500ミリ秒 // ?ここでようやく、非同期モードの本領発揮です。WaitForSingleObject()関数は // ?読み込み完了、読み込みエラー発生、タイムアウトのどれかが発生すると // ?リターンします。
dwRes = WaitForSingleObject( osReader.hEvent, 500 );
switch( dwRes ) // 戻り値で分岐 {
case WAIT_OBJECT_0: // イベント発生
// イベントが正常がチェックする // ?読み込み完了と読み込みエラーのリターンコードが同じなので // ?正常終了したかどうかチェックします。
if( !GetOverlappedResult( E_ComPort, &osReader, &Read_Size, FALSE ))
{ // 読み込み中に回線断が発生した場合はここを通ります // エラーメッセージ表示 SimErrMsg( "%s[%d] 受信失敗(%d)", 0, __file__, __line__, GetLastError() ); Loop_End = TRUE; } else { // バッファリング処理 Bool_Ret = SimReadSuccess( (char)ReadBuff[0] ); // バッファリング処理失敗 if( Bool_Ret != TRUE ) { // エラーメッセージ表示 SimErrMsg( "%s[%d] バッファリング処理失敗(%d)", 0, __file__, __line__, GetLastError() ); } } // 非同期中メッセージ待ち状態をOFFにする fWaitingOnRead = FALSE; break;
case WAIT_TIMEOUT: // タイムアウト
// ?無通信チェックもできると思います。 break; default: break; } } } // スレッド終了 return(0); }
BOOL COMMwrite( char *WriteMsg, // [IN] 送信電文 UINT WriteLength // [IN] 送信電文長 ) { OVERLAPPED osWrite = {0}; // 非同期通信用OVERLAPPED構造体 DWORD dwWritten; // 書き込みバイト数 BOOL fRes; // 内部リターンコード BOOL Bool_Ret; // 関数戻り値 long Ret; // 関数戻り値 //----------------------------------------------------------------------------- /// 非同期通信用の処理完了イベントを設定 //----------------------------------------------------------------------------- // 非同期通信用の処理完了イベントを設定 // ?読み込みと同じでそのまま記述して下さい。
osWrite.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
// イベント設定に失敗 if( osWrite.hEvent == NULL ) { // 内部ログの書き込み (long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0, PRCNAM_COM, __file__, __line__, D_LOG_MSG08, "SetTimer", GetLastError() ); // エラーでリターンする return( FALSE ); } //----------------------------------------------------------------------------- /// 送出TCSに電文を送信する //----------------------------------------------------------------------------- // TCSに送信電文を送信する // ?WriteFile()関数は書き込みが完了したかどうかに関わらず、すぐにリターンします。
if( !WriteFile( Com2Port, WriteMsg, WriteLength, &dwWritten, &osWrite))
{ // エラーチェック // ?書き込み完了待ちならば、GetLastError()の戻りがERROR_IO_PENDINGになります。 // ?それ以外は書き込みエラーです
if( GetLastError() != ERROR_IO_PENDING )
{ // ライト完了待ち以外のエラー // 内部ログの書き込み (long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0, PRCNAM_COM, __file__, __line__, D_LOG_MSG08, "SetTimer", GetLastError() ); // 内部リターンコードをエラーにする fRes = FALSE; } else { // ?読み込み時と同じようにイベントの発生待ちを行います // ?タイマ秒数は外部変数です
fRes = WaitForSingleObject( osWrite.hEvent, E_Wri_TimMax );
switch( fRes ) { case WAIT_OBJECT_0: // ?書き込み完了と読み込みエラーのリターンコードが同じなので // ?正常終了したかどうかチェックします。
if( !GetOverlappedResult( Com2Port, &osWrite, &dwWritten, FALSE ) )
{ fRes = FALSE; } else { fRes = TRUE; } break;
default:
// ?ここを通る場合はまず、書き込みタイムアウトの場合でしょう // ?COM通信は相手がいなくても、書き込みでエラーにならないし、 // ?回線が切れていようが、分からないので、ここのタイムアウトにより、 // ?エラーと判断しています。 fRes = FALSE; } // ライト完了待ち // ?昔の処理で、タイムアウト値が無限大になっています。 // ?このバージョンだと、正常に回線断を検出できませんし、 // ?結局、GetOverlappedResult()関数でWAITになってしまい // ?非同期にした意味がありません。
// if( !GetOverlappedResult( Com2Port, &osWrite, &dwWritten, INFINITE ) )
if( fRes == FALSE ) { // ライト異常終了 // 内部ログの書き込み (long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0, PRCNAM_COM, __file__, __line__, D_LOG_MSG08, "GetOverlappedResult", GetLastError() ); // 内部リターンコードをエラーにする fRes = FALSE; } else { // ライト正常終了 // 内部リターンコードを正常にする fRes = TRUE; } } } else { // Writeが正常に完了した // 内部リターンコードを正常にする fRes = TRUE; } //----------------------------------------------------------------------------- /// 電文送信エラー処理 //----------------------------------------------------------------------------- // ライト失敗なら回線を切断する if( fRes == FALSE ) { // 回線を切断する Bool_Ret = COMMcommclose( ); if( Bool_Ret != TRUE ) { // 内部ログの書き込み (long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0, PRCNAM_COM, __file__, __line__, D_LOG_MSG09, "COMMcommclose", GetLastError() ); } // 再オープン用タイマを設定する Ret = SetTimer( E_WinHdl_MF, D_TIMER_OPEN, E_Opn_TimMax, NULL ); if( Ret == 0 ) { // 内部ログの書き込み (long)(*ZZerrlog_VC)( E_Socket, NHK_COM_COMM, "%s %s %d %s(%s:%d)", 0, PRCNAM_COM, __file__, __line__, D_LOG_MSG08, "SetTimer", GetLastError() ); } } // リターンする return( fRes ); }