APIのSetCommStateでStopBitを1にすると必ずエラーになるのを解消するには?

解決


KEI  2013-07-19 18:51:36  No: 148197  IP: [192.*.*.*]

はじめまして、KEIです。
APIを使ったRS232Cの通信をしています。
StopBitに1を入れるとSetCommStateの所で必ずエラーが発生してしまいます。
0、1.5、2ではエラーが発生しません。
DCBの構造体が間違っていると思い調べてみたのですが間違っていないようでした。
御教授いただけませんでしょうか。

    Private Structure DCB
        Public DCBlength As Int32   'DCB構造体の大きさ(バイト単位)
        Public BaudRate As Int32    '通信速度
        Public fBitFields As Int32  'ビット単位のフィールド定義'See Comments in Win32API.Txt
        Public wReserved As Int16   '予約領域
        Public XonLim As Int16      'XON文字送信の閾値
        Public XoffLim As Int16     'XOFF文字送信の閾値
        Public ByteSize As Byte     '1バイトのビット数
        Public Parity As Byte       'パリティの方式を指定
        Public StopBits As Byte     'ストップビットの長さ指定
        Public XonChar As Byte      'XON文字のコード指定
        Public XoffChar As Byte     'XOFF文字のコード指定
        Public ErrorChar As Byte    'パリティエラー時の代替使用文字の指定
        Public EofChar As Byte      '非バイナリモード時のデータ終了コード
        Public EvtChar As Byte      'イベント発生文字のコード指定
        Public wReserved1 As Int16  '予約領域'Reserved; Do Not Use
    End Structure

    Private Declare Auto Function SetCommState Lib "kernel32.dll" (ByVal nCid As IntPtr, _
                                                                  ByRef lpDCB As DCB) As Boolean

    Public Function Connect(ByVal PortName As String, ByVal BaudRate As int32, _
                            ByVal ByteSize As Byte, ByVal Parity As Byte, ByVal StopBit As Byte) As Boolean
        Dim MyDCB As DCB
        Dim MyCommTimeouts As COMMTIMEOUTS

        'シリアル ポートへのハンドルを取得します。
        hSerialPort = CreateFile(PortName, GENERIC_READ Or GENERIC_WRITE, 0, IntPtr.Zero, _
                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)

        '取得したハンドルが有効かどうか確認します。
        If hSerialPort.ToInt32 = -1 Then Return False

        '現在のコントロールの設定を取得します。
        If Not (GetCommState(hSerialPort, MyDCB)) Then Return False

        '必要に応じて MyDCB のプロパティを変更します。
        '警告 : 各プロパティでサポートされている値に応じて変更を行うようにしてください。
        MyDCB.BaudRate = BaudRate
        MyDCB.ByteSize = ByteSize
        MyDCB.Parity = Parity
        MyDCB.StopBits = StopBit

        'MyDCB のプロパティに基づいて COM? を再構成します。
        If Not (SetCommState(hSerialPort, MyDCB)) Then Return False
        'SetCommState(hSerialPort, MyDCB)

        '現在のタイムアウトの設定を取得します。
        'If Not (GetCommTimeouts(hSerialPort, MyCommTimeouts)) Then Return False
        GetCommTimeouts(hSerialPort, MyCommTimeouts)

        ' MyCommTimeouts のプロパティを必要に応じて変更します。
        ' 警告 : プロパティでサポートされている値に応じて変更を行うようにしてください。
        MyCommTimeouts.ReadIntervalTimeout = 0          'タイムアウトの時間
        MyCommTimeouts.ReadTotalTimeoutConstant = 4     '受信関数コール時間(インターバル0〜250ms)
        MyCommTimeouts.ReadTotalTimeoutMultiplier = 1   '受信1Byteあたりの時間
        MyCommTimeouts.WriteTotalTimeoutConstant = 1    '送信関数コール時間
        MyCommTimeouts.WriteTotalTimeoutMultiplier = 1  '送信1Byteあたりの時間

        ' MyCommTimeouts のプロパティに基づいてタイムアウトの設定を再構成します。
        If Not (SetCommTimeouts(hSerialPort, MyCommTimeouts)) Then Return False
        Return True
    End Function

環境
WIN7
VS2010

編集 削除
オショウ  2013-07-20 08:09:30  No: 148198  IP: [192.*.*.*]

これだけでは、エラーを特定できません。

データビット数とパリティーがどのように設定されていたのか
エラーした時の各々の設定内容を記載して下さい。

組み合わせ上、設定できない値があります。

以上。

編集 削除
KEI  2013-07-20 10:30:21  No: 148199  IP: [192.*.*.*]

オショウさん、朝一番にありがとうございます。

今回テストしたのは以下のパターンです。
他のパターンは未だ試していません。 

BaudRate=4800
ByteSize=8
Parity=0
StopBit=1

BaudRate=19200
ByteSize=8
Parity=1
StopBit=1

BaudRate=9600
ByteSize=7
Parity=2
StopBit=1

設定できない組み合わせが有るとは知りませんでした。
ちなみに、どのような組み合わせが、「設定できない組み合わせ」でしょうか。

よろしくお願いします。

編集 削除
オショウ  2013-07-20 16:56:58  No: 148200  IP: [192.*.*.*]

回答ではありませんが・・・

.NETでしょう?
何故、API?

.NET Framework 2.0 以降なら、SerialPortクラスがあるので
APIを使う必要はありませんが・・・

以上。

編集 削除
魔界の仮面弁士  2013-07-22 10:16:48  No: 148201  IP: [192.*.*.*]

コメント文等は、下記にそっくりですね。
http://7ujm.net/VB/VBNetRS232c.html


> StopBitに1を入れるとSetCommStateの所で必ずエラーが発生してしまいます。
> 0、1.5、2ではエラーが発生しません。

StopBit というのは、
StopBitsのことでしょうか。

「1 は OK、0/1.5/2 は NG」という質問内容から、4 種の組み合わせを
試そうとしているものと読み取れますが、下記の 3 値しか
サポートしていなかったような…?

' System.IO.Ports.StopBits 列挙帯でも代用可能
Const ONESTOPBIT   As Byte = 0   '1   ストップビット
Const ONE5STOPBITS As Byte = 1   '1.5 ストップビット
Const TWOSTOPBITS  As Byte = 2   '2   ストップビット

具体的にはどのようにして値をセットされていますか?
Byte 型のフィールドに、非Byte値(1.5 など)を直接代入してはいませんか?


また、「SetCommStateの所で必ずエラーが発生」とのことですが、失敗時に
System.Runtime.InteropServices.Marshal.GetLastWin32Error
(あるいは Err.LastDLLError)は何を返していますか?


> Public fBitFields As Int32  'ビット単位のフィールド定義'See Comments in Win32API.Txt
Win32API って、旧VB向けの簡易定義ファイルですよね。
コーディングの補助的に使う分には良いですが、正式な資料として使うべきものでは無いでしょう。
ftp://ftp.microsoft.com/developr/drg/WWLive/broadcas/msdnnews/WIN32API.TXT
http://download.microsoft.com/download/7/A/E/7AE13E42-5BDE-42B1-A91E-9BBCD7C9DF83/Win32Api.exe

本来であれば、SDK およびヘッダファイルを参照すべきかと思いますよ。
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214.aspx


> Public Function Connect(ByVal PortName As String, ByVal BaudRate As int32, _
宣言部が As Int32 ではなく As int32 になっているようですが、もしかして、
実際に使っているコードを貼ったものでは無く、掲示板上で書き起こしたコードですか?

編集 削除
KEI  2013-07-22 14:18:20  No: 148202  IP: [192.*.*.*]

オショウさん、魔界の仮面弁士さん、ありがとうございます。

先輩からの引継ぎなのでそのまま使用しています。
ですので、
オショウさんのSerialPortクラス案も、APIがダメだったら使用する予定です。

魔界の仮面弁士さんの似たソースも上記理由だと思います。
魔界の仮面弁士さんの指摘通り、「1 ストップビット」を渡すつもりがそのまま「1」を与えていました。

ヘッダファイルの参照から以下のようにしてみました。(かなり時間が掛かりました)

    Enum DTR_CONTROL As Int32
        DISABLE = &H0       
        ENABLE = &H1        
        HANDSHAKE = &H2     
    End Enum
    Enum RTS_CONTROL As Int32
        DISABLE = &H0       
        ENABLE = &H1        
        HANDSHAKE = &H2     
        TOGGLE = &H3        
    End Enum
    Enum Parity As Int32
        NOPARITY = 0        'パリティなし: NOPARITY
        ODDPARITY = 1       '奇数パリティ: ODDPARITY
        EVENPARITY = 2      '偶数パリティ: EVENPARITY
        MARKPARITY = 3      '常にマーク
        SPACEPARITY = 4     'スペース
    End Enum
    Enum StopBits As Byte
        ONESTOPBIT = &H0    '1ビット: ONESTOPBIT
        ONE5STOPBITS = &H1  '1.5ビット: ONE5STOPBITS
        TWOSTOPBITS = &H2   '2ビット: TWOSTOPBITS 
    End Enum

    Private Structure DCB
        Public DCBlength As Int32          
        Public BaudRate As Int32            '通信速度
        Public fBinary As Boolean           
        Public fParity As Boolean           
        Public fOutxCtsFlow As Boolean      
        Public fOutxDsrFlow As Boolean      
        Public fDtrControl As DTR_CONTROL   
        Public fDsrSensitivity As Boolean   
        Public fTXContinueOnXoff As Boolean 
        Public fOutX As Boolean             
        Public fErrorChar As Boolean        
        Public fNull As Boolean             
        Public fRtsControl As RTS_CONTROL   
        Public fAbortOnError As Boolean     
        Public fDummy2 As Int32             '予約済み。
        Public wReserved As Int16           '予約済み。
        Public XonLim As Int16              
        Public XoffLim As Int16             
        Public ByteSize As Byte             
        Public Parity As Parity             
        Public StopBits As Byte             
        Public XonChar As Char              
        Public XoffChar As Char             
        Public ErrorChar As Char           
        Public EofChar As Char              
        Public EvtChar As Char             
        Public wReserved1 As Int16        
    End Structure

編集 削除
オショウ  2013-07-22 15:48:08  No: 148203  IP: [192.*.*.*]

> オショウさんのSerialPortクラス案も、APIがダメだったら使用する予定です。

    ではなく・・・
    .NET では、APIをなるべく使わずに提供されている
    機能を使った方が、安全で確実。と言う話です。

以上。参考まで

編集 削除
魔界の仮面弁士  2013-07-22 16:51:46  No: 148204  IP: [192.*.*.*]

> ヘッダファイルの参照から以下のようにしてみました。(かなり時間が掛かりました)

どのような対処をしたのか、は分かりましたが、
その結果はどうだったのでしょうか?

同じような問題を抱えたままなのか、それとも結果が変わったのか。
引き続きエラーがでるのだとしたら、先に確認をお願いしていた
Marshal.GetLastWin32Error の結果はどうだったのか。


>「1 ストップビット」を渡すつもりがそのまま「1」を与えていました。

という事は、「1 ストップビット(= 0)」や「2 ストップビット(= 2)」では
エラーが発生せず、「1.5 ストップビット(= 1)」のみがエラーになる、という
質問に変更ということでしょうか。
それとも、1 ストップビットは指定できているので、問題は解決したのでしょうか。


> 先輩からの引継ぎなのでそのまま使用しています。
恐らくは .NET 1.x 時代のコードを使いまわしていたのであろうと推測します。

今回は VB2010 なので、最低でも .NET 2.0 向け…できれば .NET 4 用に
コードを見直すべきかと思いますが、そのような工数を取りにくいかと思います。

それでも、標準の SerialPort クラスでも同じ現象になっていまうのかどうかについては
簡単な実験用アプリを作るなどして、試しておいた方が良いと思いますよ。

その結果、SerialPort クラスでも失敗するようであれば、プログラムではなく
機器側環境側あるいはハード仕様について確認が必要かも知れません。
あるいはその逆に SerialPort であれば成功するようであれば、
API の呼び出しを見直す工数と、SerialPort に変更するための工数を
それぞれ天秤にかけて、調査を続行するか修正するかを判断できるかと。

編集 削除
KEI  2013-07-27 16:00:37  No: 148205  IP: [192.*.*.*]

オショウさん
速度を上げるためにAPIを使ったそうです。

魔界の仮面弁士さん
正式な資料として使用しないほうが良いとの事でしたので作り直しましたが、作り直しにかかわらすエラーの原因は「1.5 ストップビット(= 1)」を与えていたせいでした。

今回は私が担当なので、SerialPortクラスも作成して双方較しよう考えています。
オショウさん、魔界の仮面弁士さん 本当にありがとうございました。

編集 削除