« マネージコードで、propertyキーワードでgetter/setterを書く | トップページ | Stringのマネージ ⇒ アンマネージ変換ってこんな感じ »

2016/11/21

C++ でシリアル通信クラスを書いてみた

■ヘッダーファイル
#include <Windows.h>
#include  "Winbase.h"

#pragma once

unsigned __stdcall ThreadFunc(void *p);

class CSerial
{
public:
    CSerial(void);
    ~CSerial(void);

    // 指定ポートをオープンし受信開始
    void Start(LPCSTR comPortName, DCB* portConfig);
    // COMポートを閉じ処理終了
    void End(void);

    DWORD RecvData(void);

    // 受信済みのデータを buffer に格納し buffer に格納したデータのバイト数を返す
    DWORD GetRecvData(LPVOID buffer);

    // buffer に格納されたデータを length バイト送信する
    DWORD SendData(LPVOID buffer, int toWriteBytes);

private:
    HANDLE mHComPort;           // COM ポート用ハンドル
    HANDLE mThreadId;           // スレッド ID
    HANDLE mMutex;              // mRecvDataBuff,mRecvDataBuffLen を排他するためのMutex
    BYTE* mRecvDataBuff;        // 受信用内部データバッファ
    DWORD mRecvDataBuffLen;     // mRecvDataBuff に格納されている受信データのバイト数
};

■ソースファイル
#include "CSerial.h"
#include <process.h>

unsigned __stdcall ThreadFunc(void *p)
{
    CSerial *pp = (CSerial*)p;

    return pp->RecvData();
}

CSerial::CSerial(void)
    : mHComPort(NULL), mRecvDataBuff(NULL), mRecvDataBuffLen(0)
{
    mMutex = CreateMutex(NULL, TRUE, NULL);
}

CSerial::~CSerial(void)
{
    CloseHandle(mMutex);
    if(mRecvDataBuff != NULL) {
        delete mRecvDataBuff;
        mRecvDataBuff = NULL;
    }
    mMutex = NULL;
}

void CSerial::Start(LPCSTR comPortName, DCB* portConfig)
{
    bool    nBret;

    // 指定ポートを開く
    mHComPort = CreateFile(
        (LPCTSTR)comPortName,
        GENERIC_READ | GENERIC_WRITE,               // 読み書きを指定
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if(mHComPort != INVALID_HANDLE_VALUE)
    {
        // ポートのボーレート、パリティ等を設定
        nBret = SetCommState(mHComPort, portConfig);
    }

    if(nBret != FALSE )
    {
        mThreadId = (HANDLE)_beginthreadex(
            NULL,
            0,
            ThreadFunc,
            this,
            0,
            NULL);
    }
}

void CSerial::End()
{
    if (mHComPort != NULL)
    {
        CloseHandle(mHComPort);
    }
}

DWORD CSerial::RecvData(void)
{
    BYTE buffer[1024];
    DWORD toReadBytes = 1024;
    DWORD readBytes;

    while(ReadFile(mHComPort, buffer, toReadBytes, &readBytes, NULL))
    {
        if(readBytes > 0)
        {
            WaitForSingleObject(mMutex, 0);

            // 受信したデータは、mRecvDataBuff に受信済みで取り出されていない
            // データに続けて保持しておく
            BYTE* tmpBuf = new BYTE[mRecvDataBuffLen + readBytes];
            if(mRecvDataBuff != NULL)
            {
                memcpy(tmpBuf, mRecvDataBuff, mRecvDataBuffLen);
                delete [] mRecvDataBuff;
            }
            memcpy(tmpBuf, buffer, readBytes);
            mRecvDataBuffLen += readBytes;
            mRecvDataBuff = tmpBuf;

            ReleaseMutex(mMutex);
        }
    }

    return S_OK;
}

DWORD CSerial::GetRecvData(LPVOID buffer)
{
    DWORD length;

    WaitForSingleObject(mMutex, 0);

    buffer = new BYTE[mRecvDataBuffLen];
    memcpy(buffer, mRecvDataBuff, mRecvDataBuffLen);
    length = this->mRecvDataBuffLen;

    ReleaseMutex(mMutex);

    return length;
}

DWORD CSerial::SendData(LPVOID buffer, int toWriteBytes)
{
    DWORD writeBytes;
    DWORD index = 0;

    // 指定されたデータを全て書き込む為にループを廻す
    while (toWriteBytes > 0)
    {
        WriteFile(
            mHComPort,
            ((BYTE*)buffer) + index,
            toWriteBytes,
            &writeBytes,
            NULL);
        index += writeBytes;
        toWriteBytes -= writeBytes;
    }

    return writeBytes;
}

« マネージコードで、propertyキーワードでgetter/setterを書く | トップページ | Stringのマネージ ⇒ アンマネージ変換ってこんな感じ »

VC++」カテゴリの記事

トラックバック


この記事へのトラックバック一覧です: C++ でシリアル通信クラスを書いてみた:

« マネージコードで、propertyキーワードでgetter/setterを書く | トップページ | Stringのマネージ ⇒ アンマネージ変換ってこんな感じ »