Win32 API 编写一个串口助手

news2024/11/24 23:08:01

首先对串口操作做了一些封装:

助手类声明如下

CSerialPort.h

#pragma once

#include <string>
#include <windows.h>
#include <tchar.h>

#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif

class CSerialPort
{
public:
    CSerialPort();
    CSerialPort(const CSerialPort& r) = delete;
    ~CSerialPort();

    // 打开串口
    bool Open(
        int nComm,                      //串口号
        DWORD dwBaudRate = CBR_115200,  //波特率
        BYTE bByteSize = 8,             //数据位
        BYTE bParity = NOPARITY,        //校验位
        BYTE bStopBits = ONESTOPBIT     //停止位
    );

    // 打开串口
    bool Open(
        const _tstring& strName = _T("COM1"),        //串口名
        DWORD dwBaudRate = CBR_115200,  //波特率
        BYTE bByteSize = 8,             //数据位
        BYTE bParity = NOPARITY,        //校验位
        BYTE bStopBits = ONESTOPBIT     //停止位
    );

    // 检测是否已经打开
    bool IsOpen() const;

    // 设置状态配置
    bool SetState(
        DWORD dwBaudRate = CBR_115200,  //波特率
        BYTE bByteSize = 8,             //数据位
        BYTE bParity = NOPARITY,        //校验位
        BYTE bStopBits = ONESTOPBIT     //停止位
    );

    // 设置状态配置
    bool SetState(const LPDCB lpDcb);

    // 设置缓冲区大小
    bool SetupComm(
        DWORD dwInQueue = 14,   // 输入缓冲区 1 - 14
        DWORD dwOutQueue = 16   // 输出缓冲区 1 - 16
    );

    // 设置超时配置
    bool SetTimeOut(
        DWORD ReadInterval = 50,          // 读间隔超时
        DWORD ReadTotalMultiplier = 5,   // 读时间系数
        DWORD ReadTotalConstant = 1000,            // 读时间常量
        DWORD WriteTotalMultiplier = 10,         // 写时间系数
        DWORD WriteTotalConstant = 200            // 写时间常量
    );

    // 设置超时配置
    bool SetTimeOut(const LPCOMMTIMEOUTS lpTimeOuts);

    // 清空缓冲区
    bool Purge(
        DWORD  dwFlags = PURGE_TXCLEAR | PURGE_RXCLEAR
    );

    // 清除错误
    bool ClearError();

    // 关闭串口
    void Close();

    // 发送数据
    bool Write(
        LPCVOID lpData, 
        DWORD cbSize,
        LPDWORD lpBytesWritten = nullptr,
        DWORD nTimeOut = 1000
    );

    // 接收数据
    bool Read(
        LPVOID lpData, 
        DWORD cbSize,
        LPDWORD lpBytesRead = nullptr,
        DWORD nTimeOut = 3000
    );

private:

    // 打开串口
    bool OpenComm(int nComm);

    // 打开串口
    bool OpenComm(const _tstring& strName);

    // 等待重叠操作完成
    bool WaitForOverlapped(
        HANDLE hFile, 
        LPOVERLAPPED lpOv, 
        LPDWORD lpBytesTransferred, 
        DWORD nTimeOut
    );

private:

    HANDLE m_hComm;             // 串口句柄
    HANDLE m_hReadEvent;        // 重叠读事件句柄
    HANDLE m_hWriteEvent;       // 重叠写事件句柄
    bool m_bExit;               // 退出标记
};

串口操作具体实现部分如下, 其中读写采用异步方式, 方便退出, 避免读写时卡住

CSerialPort.cpp

#include "CSerialPort.h"
#include <strsafe.h>

CSerialPort::CSerialPort()
    :
    m_hComm(INVALID_HANDLE_VALUE),
    m_hReadEvent(nullptr),
    m_hWriteEvent(nullptr),
    m_bExit(false)
{

}

CSerialPort::~CSerialPort()
{
    Close();
}

// 打开串口
bool CSerialPort::Open(
    int nComm,
    DWORD dwBaudRate/* = CBR_115200*/,
    BYTE bByteSize/* = 8*/,
    BYTE bParity/* = NOPARITY*/,
    BYTE bStopBits/* = ONESTOPBIT*/
)
{
    if (!OpenComm(nComm))
    {
        return false;
    }

    if (!SetState(dwBaudRate, bByteSize, bParity, bStopBits))
    {
        this->Close();
        return false;
    }

    m_bExit = false;
    return true;
}

// 打开串口
bool CSerialPort::Open(
    const _tstring& strName/* = _T("COM1")*/,               //串口名
    DWORD dwBaudRate/* = CBR_115200*/,
    BYTE bByteSize/* = 8*/,
    BYTE bParity/* = NOPARITY*/,
    BYTE bStopBits/* = ONESTOPBIT*/
)
{
    if (!OpenComm(strName))
    {
        return false;
    }

    if (!SetState(dwBaudRate, bByteSize, bParity, bStopBits))
    {
        this->Close();
        return false;
    }

    m_bExit = false;
    return true;
}

bool CSerialPort::IsOpen() const
{
    return INVALID_HANDLE_VALUE != m_hComm;
}

bool CSerialPort::SetState(
    DWORD dwBaudRate/* = CBR_115200*/,
    BYTE bByteSize/* = 8*/,
    BYTE bParity/* = NOPARITY*/,
    BYTE bStopBits/* = ONESTOPBIT*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    DCB dcb = { 0 };
    dcb.DCBlength = sizeof(DCB);

    if (!::GetCommState(m_hComm, &dcb))
    {
        return false;
    }

    dcb.DCBlength = sizeof(DCB);
    dcb.BaudRate = dwBaudRate;
    dcb.ByteSize = bByteSize;
    dcb.Parity = bParity;
    dcb.StopBits = bStopBits;

    return ::SetCommState(m_hComm, &dcb);
}

bool CSerialPort::SetState(const LPDCB lpDcb)
{
    if (nullptr == lpDcb)
    {
        return false;
    }

    return ::SetCommState(m_hComm, lpDcb);
}

// 设置缓冲区大小
bool CSerialPort::SetupComm(
    DWORD dwInQueue/* = 14*/,
    DWORD dwOutQueue/* = 16*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    return ::SetupComm(m_hComm, dwInQueue, dwOutQueue);
}

bool CSerialPort::SetTimeOut(
    DWORD ReadInterval/* = 50*/,
    DWORD ReadTotalMultiplier/* = 50*/,
    DWORD ReadTotalConstant/* = 100*/,
    DWORD WriteTotalMultiplier/* = 50*/,
    DWORD WriteTotalConstant/* = 200*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    COMMTIMEOUTS comTimeOuts = { 0 };

    if (!::GetCommTimeouts(m_hComm, &comTimeOuts))
    {
        return false;
    }

    comTimeOuts.ReadIntervalTimeout = ReadInterval;
    comTimeOuts.ReadTotalTimeoutMultiplier = ReadTotalMultiplier;
    comTimeOuts.ReadTotalTimeoutConstant = ReadTotalConstant;
    comTimeOuts.WriteTotalTimeoutMultiplier = WriteTotalMultiplier;
    comTimeOuts.WriteTotalTimeoutConstant = WriteTotalConstant;
    return ::SetCommTimeouts(m_hComm, &comTimeOuts);
}

bool CSerialPort::SetTimeOut(const LPCOMMTIMEOUTS lpTimeOuts)
{
    if (nullptr == lpTimeOuts)
    {
        return false;
    }

    return ::SetCommTimeouts(m_hComm, lpTimeOuts);
}

bool CSerialPort::Purge(
    DWORD  dwFlags/* = PURGE_TXCLEAR | PURGE_RXCLEAR*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    return ::PurgeComm(m_hComm, dwFlags);
}

bool CSerialPort::ClearError()
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    DWORD dwError = 0;
    COMSTAT comStat = { 0 };

    return ::ClearCommError(m_hComm, &dwError, &comStat);
}

void CSerialPort::Close()
{
    m_bExit = true;
    if (nullptr != m_hReadEvent)
    {
        ::SetEvent(m_hReadEvent);
        ::CloseHandle(m_hReadEvent);
        m_hReadEvent = nullptr;
    }

    if (nullptr != m_hWriteEvent)
    {
        ::SetEvent(m_hWriteEvent);
        ::CloseHandle(m_hWriteEvent);
        m_hWriteEvent = nullptr;
    }

    if (INVALID_HANDLE_VALUE != m_hComm)
    {
        ::CancelIoEx(m_hComm, nullptr);
        ::CloseHandle(m_hComm);
        m_hComm = INVALID_HANDLE_VALUE;
    }
}

bool CSerialPort::OpenComm(int nComm)
{
    TCHAR szBuf[MAX_PATH];
    (void)::StringCchPrintf(szBuf, _countof(szBuf), _T(R"(\\.\COM%d)"), nComm);

    // 关闭前一个已打开的串口
    this->Close();

    m_hComm = ::CreateFile(
        szBuf,
        GENERIC_READ | GENERIC_WRITE,   //读写串口
        0,                              //独占打开
        nullptr,
        OPEN_EXISTING,                  //必须存在
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,           //重叠操作
        nullptr
    );

    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    m_hReadEvent = CreateEvent(nullptr, false, false, nullptr);
    if (nullptr == m_hReadEvent)
    {
        this->Close();
        return false;
    }

    m_hWriteEvent = CreateEvent(nullptr, false, false, nullptr);
    if (nullptr == m_hWriteEvent)
    {
        this->Close();
        return false;
    }

    return true;
}

bool CSerialPort::OpenComm(const _tstring& strName)
{
    TCHAR szBuf[MAX_PATH];
    (void)::StringCchPrintf(szBuf, _countof(szBuf), _T(R"(\\.\%s)"), strName.c_str());

    // 关闭前一个已打开的串口
    this->Close();

    m_hComm = ::CreateFile(
        szBuf,
        GENERIC_READ | GENERIC_WRITE,   //读写串口
        0,                              //独占打开
        nullptr,
        OPEN_EXISTING,                  //必须存在
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,           //重叠操作
        nullptr
    );

    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    m_hReadEvent = CreateEvent(nullptr, false, false, nullptr);
    if (nullptr == m_hReadEvent)
    {
        this->Close();
        return false;
    }

    m_hWriteEvent = CreateEvent(nullptr, false, false, nullptr);
    if (nullptr == m_hWriteEvent)
    {
        this->Close();
        return false;
    }

    return true;
}

bool CSerialPort::WaitForOverlapped(
    HANDLE hFile,
    LPOVERLAPPED lpOv,
    LPDWORD lpBytesTransferred,
    DWORD nTimeOut
)
{
    bool bResult = false;
    DWORD dwBytesTransferred = 0;
    DWORD dwWait = ::WaitForSingleObject(lpOv->hEvent, nTimeOut);

    switch (dwWait)
    {
    case WAIT_OBJECT_0:
    {
        if (::GetOverlappedResult(hFile,
            lpOv,
            &dwBytesTransferred,
            true
        ))
        {
            bResult = true;
        }
        else
        {
            bResult = false;
        }

        if (lpBytesTransferred)
        {
            *lpBytesTransferred = dwBytesTransferred;
        }
    }
    break;
    case WAIT_TIMEOUT:
        break;
    default:
        break;
    }

    if (!bResult && !m_bExit)
    {
        ::CancelIoEx(hFile, lpOv);
    }

    return bResult;
}

bool CSerialPort::Write(
    LPCVOID lpData,
    DWORD cbSize,
    LPDWORD lpBytesWritten,
    DWORD nTimeOut/* = 1000*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm || 
        nullptr == lpData || 
        0 == cbSize ||
        m_bExit)
    {
        return false;
    }

    OVERLAPPED ov = { 0 };
    BOOL bResult = FALSE;
    ov.hEvent = m_hWriteEvent;

    bResult = ::WriteFile(
        m_hComm,
        lpData,
        cbSize,
        nullptr,
        &ov
    );

    if (bResult)
    {
        goto L_Cleanup;
    }

    if (ERROR_IO_PENDING != ::GetLastError())
    {
        goto L_Cleanup;
    }

    bResult = WaitForOverlapped(m_hComm, &ov, lpBytesWritten, nTimeOut);

L_Cleanup:

    return bResult;
}

bool CSerialPort::Read(
    LPVOID lpData,
    DWORD cbSize,
    LPDWORD lpBytesRead/* = nullptr*/,
    DWORD nTimeOut/* = 3000*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm ||
        nullptr == lpData ||
        0 == cbSize || 
        m_bExit)
    {
        return false;
    }

    OVERLAPPED ov = { 0 };
    BOOL bResult = FALSE;
    ov.hEvent = m_hReadEvent;

    bResult = ::ReadFile(
        m_hComm,
        lpData,
        cbSize,
        nullptr,
        &ov
    );

    if (bResult)
    {
        goto L_Cleanup;
    }

    if (ERROR_IO_PENDING != ::GetLastError())
    {
        goto L_Cleanup;
    }

    bResult = WaitForOverlapped(m_hComm, &ov, lpBytesRead, nTimeOut);
    if (bResult)
    {
        return true;
    }

L_Cleanup:

    return bResult;
}

接着就是编写Win32 窗口界面, 这里我使用的时对话框, 下面对对话框进行了一些封装, 采取MFC的消息映射方式封装, 方便后期消息映射成员函数, 极大简化编码工作量.

对话框基类声明如下:

CDialogBase.h

#pragma once

#include <Windows.h>
#include <tchar.h>
#include <string>

#ifdef UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif

class CDialogBase;

typedef LRESULT(CDialogBase::* pMapFn)(WPARAM wParam, LPARAM lParam);

struct WND_DLG_MSGMAP_ENTRY
{
    pMapFn m_pfn;
    UINT m_nMessage;
    UINT m_nID;
    WND_DLG_MSGMAP_ENTRY(UINT uMsg, pMapFn pfn) : m_nMessage(uMsg), m_pfn(pfn), m_nID(0) {}
    WND_DLG_MSGMAP_ENTRY(UINT uMsg, UINT nID, pMapFn pfn) : m_nMessage(uMsg), m_nID(nID), m_pfn(pfn) {}
};

struct WND_DLG_MSGMAP
{
    const WND_DLG_MSGMAP* (*pfnGetBaseMap)();
    const WND_DLG_MSGMAP_ENTRY* lpEntries;
};

#ifndef dlg_msg
#define dlg_msg
#endif

#define DECLARE_DLG_MESSAGE_MAP()                                   \
protected:                                                      \
static const WND_DLG_MSGMAP* GetThisMessageMap();                   \
virtual const WND_DLG_MSGMAP* GetMessageMap() const;                \
                                                                
#define BEGIN_DLG_MESSAGE_MAP(theClass, baseClass)                  \
const WND_DLG_MSGMAP* theClass::GetMessageMap() const               \
{                                                               \
    return GetThisMessageMap();                                 \
}                                                               \
const WND_DLG_MSGMAP* theClass::GetThisMessageMap()                 \
{                                                               \
    typedef baseClass TheBaseClass;					            \
static const WND_DLG_MSGMAP_ENTRY _messageEntries[] =               \
{                                                               \
                                                                
#define ON_DLG_MESSAGE(message, memberFxn)                          \
WND_DLG_MSGMAP_ENTRY(message, static_cast< LRESULT (CDialogBase::*)(WPARAM, LPARAM) >(memberFxn)),

#define ON_DLG_COMMAND(_id, memberFxn)                          \
WND_DLG_MSGMAP_ENTRY(WM_COMMAND, _id, static_cast< LRESULT (CDialogBase::*)(WPARAM, LPARAM) >(memberFxn)),

#define END_DLG_MESSAGE_MAP()  WND_DLG_MSGMAP_ENTRY(0, NULL) };         \
static const WND_DLG_MSGMAP messageMap =                            \
{&TheBaseClass::GetThisMessageMap , &_messageEntries[0] };      \
return &messageMap;                                             \
}                                                               \

#define DECLARE_DLG_BASE_MESSAGE_MAP()                              \
protected:                                                      \
static const WND_DLG_MSGMAP* GetThisMessageMap();                   \
virtual const WND_DLG_MSGMAP* GetMessageMap() const;                \
static const WND_DLG_MSGMAP* GetBaseMessageMap();                   \

#define BEGIN_DLG_BASE_MESSAGE_MAP(theClass)                        \
typedef theClass TheThisClass;					                \
const WND_DLG_MSGMAP* TheThisClass::GetMessageMap() const           \
{                                                               \
    return GetThisMessageMap();                                 \
}                                                               \
const WND_DLG_MSGMAP* TheThisClass::GetBaseMessageMap()             \
{                                                               \
    return NULL;                                                \
}                                                               \
const WND_DLG_MSGMAP* TheThisClass::GetThisMessageMap()             \
{                                                               \
static const WND_DLG_MSGMAP_ENTRY _messageEntries[] =               \
{                                                               \

#define END_DLG_BASE_MESSAGE_MAP()  WND_DLG_MSGMAP_ENTRY(0, NULL) };    \
static const WND_DLG_MSGMAP messageMap =                            \
{&TheThisClass::GetBaseMessageMap, &_messageEntries[0] };       \
return &messageMap;                                             \
}                                                               \

class CDialogBase
{
public:

    CDialogBase();
    CDialogBase(const CDialogBase& r) = delete;
    ~CDialogBase();

    dlg_msg INT_PTR DoModal(HWND hWndParent, bool bCenter = true);
    dlg_msg INT_PTR DoModal(UINT resourceID, HWND hWndParent, bool bCenter = true);
    dlg_msg INT_PTR DoModalEx(UINT resourceID, HWND hWndParent, BOOL isShow = TRUE, bool bCenter = true);
    dlg_msg HWND DoDialog(UINT resourceID, HWND hWndParent, BOOL isShow = TRUE, bool bCenter = true);
    dlg_msg LRESULT OnClose(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnLanguageChange(WPARAM wParam, LPARAM lParam);

    VOID SetResourceID(UINT resourceID);
    VOID ShowWindow(int cmdShow = SW_SHOW);
    BOOL IsVisiable() const;
    VOID MoveToCenter(HWND hParent);
    _tstring LoadString(UINT resourceID);
    VOID ModifyMenuText(HMENU hMnu, UINT uPosition, LPCTSTR lpNewItem, BOOL fByPos = TRUE);
    VOID ModifyMenuText(HMENU hMnu, UINT uPosition, UINT resourceID, BOOL fByPos = TRUE);
    HWND GetWndHandle() const;
    bool EndDialog(INT_PTR nResult);

    operator HWND() const
    {
        return m_hWnd;
    }

private:

    //假模态对话框
    dlg_msg LRESULT DoFakeModal(UINT resourceID, HWND hWndParent, BOOL isShow = TRUE);
    static INT_PTR WINAPI DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    INT_PTR ThisDialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam);

protected:

    virtual BOOL PreTranslateMessage(LPMSG pMsg);

protected:
    HWND m_hWnd;
    UINT m_uResID;
    BOOL m_bModel;
    BOOL m_bFakeModel;
    BOOL m_bCenter;
    DECLARE_DLG_BASE_MESSAGE_MAP()
};

具体实现部分如下:

CDialogBase.cpp

#include "CDialogBase.h"
#include <Windows.h>
#include <strsafe.h>

#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

BEGIN_DLG_BASE_MESSAGE_MAP(CDialogBase)
    ON_DLG_MESSAGE(WM_CLOSE, &CDialogBase::OnClose)
END_DLG_BASE_MESSAGE_MAP()

CDialogBase::CDialogBase()
    :
    m_hWnd(NULL),
    m_uResID(FALSE),
    m_bModel(FALSE),
    m_bFakeModel(FALSE),
    m_bCenter(FALSE)
{

}

CDialogBase::~CDialogBase()
{
    if (NULL != m_hWnd)
    {
        ::DestroyWindow(m_hWnd);
    }
}

HWND CDialogBase::DoDialog(UINT resourceID, HWND hWndParent, BOOL isShow, bool bCenter/* = true*/)
{
    m_bCenter = bCenter;
    m_bModel = FALSE;
    m_uResID = resourceID;
    HWND hWnd = ::CreateDialogParam(GetModuleHandle(NULL), MAKEINTRESOURCE(resourceID), hWndParent, DialogProc, (LPARAM)this);
    m_hWnd = hWnd;

    if (isShow)
    {
        ::ShowWindow(m_hWnd, SW_SHOW);
    }

    return m_hWnd;
}

INT_PTR CDialogBase::DoModalEx(UINT resourceID, HWND hWndParent, BOOL isShow, bool bCenter/* = true*/)
{
    m_bCenter = bCenter;
    m_bModel = FALSE;
    return DoFakeModal(resourceID, hWndParent, isShow);
}

INT_PTR CDialogBase::DoModal(HWND hWndParent, bool bCenter/* = true*/)
{
    m_bCenter = bCenter;
    m_bModel = TRUE;
    return ::DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(m_uResID), hWndParent, DialogProc, (LPARAM)this);
}

INT_PTR CDialogBase::DoModal(UINT resourceID, HWND hWndParent, bool bCenter/* = true*/)
{
    m_bCenter = bCenter;
    m_uResID = resourceID;
    m_bModel = TRUE;
    return ::DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(m_uResID), hWndParent, DialogProc, (LPARAM)this);
}

BOOL CDialogBase::PreTranslateMessage(LPMSG pMsg)
{
    return FALSE;
}

bool CDialogBase::EndDialog(INT_PTR nResult)
{
    if (NULL == m_hWnd)
    {
        return false;
    }

    if (m_bFakeModel)
    {
        //启用父窗口
        HWND hParent = ::GetParent(m_hWnd);
        if (hParent)
        {
            ::EnableWindow(hParent, TRUE);
        }

        // 投递 WM_QUIT 消息退出消息环 (窗口句柄指定为NULL时, 
        // 该函数的行为类似于对 PostThreadMessage 的调用, 
        // 其中 dwThreadId 参数设置为当前线程的标识符。)
        ::PostMessage(NULL, WM_QUIT, nResult, 0);
    }

    if (m_bModel)
    {
        ::EndDialog(m_hWnd, nResult);
        m_hWnd = NULL;
    }
    else
    {
        ::DestroyWindow(m_hWnd);
        m_hWnd = NULL;
    }

    return TRUE;
}

LRESULT CDialogBase::OnClose(WPARAM wParam, LPARAM lParam)
{
    return EndDialog(wParam);
}

LRESULT CDialogBase::DoFakeModal(UINT resourceID, HWND hWndParent, BOOL isShow)
{
    m_bModel = FALSE;
    m_uResID = resourceID;
    m_bFakeModel = TRUE;

    m_hWnd = ::CreateDialogParam(GetModuleHandle(NULL), MAKEINTRESOURCE(resourceID), hWndParent, DialogProc, (LPARAM)this);
    if (NULL == m_hWnd)
    {
        return -1;
    }

    if (isShow)
    {
        ::ShowWindow(m_hWnd, SW_SHOW);
    }

    //禁用父窗口
    HWND hParent = GetParent(m_hWnd);
    if (hParent)
    {
        ::EnableWindow(hParent, FALSE);
        ::SetForegroundWindow(hParent);
    }

    MSG msg = { 0 };
    BOOL bRet = FALSE;

    //如果 hWnd 为 NULL, 则 GetMessage 将检索属于当前线程的任何窗口的消息,以及当前线程的消息队列上 hwnd 值为 NULL 的任何消息,
    //因此,如果 hWnd 为 NULL,则同时处理窗口消息和线程消息。
    //如果 hWnd 为 - 1,则 GetMessage 仅检索当前线程的消息队列中 hwnd 值为 NULL 的消息,
    //即当 hWnd 参数为 NULL 或 PostThreadMessage 时,PostMessage 发布的线程消息。
    while (0 != (bRet = GetMessage(&msg, NULL, 0, 0)))
    {
        if (-1 == bRet)
        {
            break;
        }

        //类捕获消息处理与对话框 TAB 按键处理
        if (PreTranslateMessage(&msg) || ::IsDialogMessage(m_hWnd, &msg))
        {
            continue;
        }

        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
    }

    return msg.wParam;
}

INT_PTR WINAPI CDialogBase::DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CDialogBase* pThis = (CDialogBase*)GetProp(hWnd, _T("this"));
    //CDialogBase* pThis = (CDialogBase*)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
    if (pThis)
    {
        return pThis->ThisDialogProc(uMsg, wParam, lParam);
    }

    if (WM_INITDIALOG == uMsg)
    {
        pThis = (CDialogBase*)lParam;
        if (pThis)
        {
            pThis->m_hWnd = hWnd;
            ::SetProp(hWnd, _T("this"), pThis);
            //::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
            pThis->ThisDialogProc(uMsg, wParam, lParam);
            if (pThis->m_bCenter)
            {
                pThis->MoveToCenter(::GetParent(hWnd));
            }
        }

        return (INT_PTR)TRUE;
    }

    return (INT_PTR)FALSE;
}

INT_PTR CDialogBase::ThisDialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    const WND_DLG_MSGMAP* pEntry = GetMessageMap();
    LRESULT lResult = 0;

    while (pEntry)
    {
        const WND_DLG_MSGMAP_ENTRY* lpEntries = pEntry->lpEntries;
        while (lpEntries->m_pfn)
        {
            if (uMsg == lpEntries->m_nMessage)
            {
                //处理 WM_COMMAND 消息
                if (WM_COMMAND == uMsg)
                {
                    WORD wID = LOWORD(wParam);
                    WORD wNotify = HIWORD(wParam);

                    if (wID == lpEntries->m_nID)
                    {
                        lResult = (this->*lpEntries->m_pfn)(wParam, lParam);
                        break;
                    }
                }
                else
                {
                    lResult = (this->*lpEntries->m_pfn)(wParam, lParam);
                    break;
                }
            }

            lpEntries++;
        }

        //消息已经处理
        if (lResult)
        {
            ::SetWindowLongPtr(m_hWnd, DWLP_MSGRESULT, lResult);
            return lResult;
        }

        //获取基类的消息映射
        pEntry = pEntry->pfnGetBaseMap();
    }

    return FALSE;
}

VOID CDialogBase::SetResourceID(UINT resourceID)
{
    m_uResID = resourceID;
}

VOID CDialogBase::ShowWindow(int cmdShow)
{
    ::ShowWindow(m_hWnd, cmdShow);
}

BOOL CDialogBase::IsVisiable() const
{
    return ::IsWindowVisible(m_hWnd);
}

void CDialogBase::MoveToCenter(HWND hParent)
{
    RECT rectParent = { 0 };
    RECT rectChild = { 0 };
    LONG dwParentW = 0;
    LONG dwParentH = 0;
    LONG dwChildW = 0;
    LONG dwChildH = 0;

    if (NULL == hParent)
    {
        hParent = ::GetDesktopWindow();
    }

    ::GetWindowRect(m_hWnd, &rectChild);
    ::GetWindowRect(hParent, &rectParent);

    dwParentW = rectParent.right - rectParent.left;
    dwParentH = rectParent.bottom - rectParent.top;
    dwChildW = rectChild.right - rectChild.left;
    dwChildH = rectChild.bottom - rectChild.top;

    ::MoveWindow(
        m_hWnd, 
        rectParent.left + (dwParentW - dwChildW) / 2,
        rectParent.top + (dwParentH - dwChildH) / 2, 
        dwChildW, 
        dwChildH, 
        TRUE
    );
}

_tstring CDialogBase::LoadString(UINT uID)
{
    static TCHAR szBuf[1024] = { 0 };
    ::LoadString(GetModuleHandle(NULL), uID, szBuf, _countof(szBuf));
    return szBuf;
}

LRESULT CDialogBase::OnLanguageChange(WPARAM wParam, LPARAM lParam)
{
    return TRUE;
}

VOID CDialogBase::ModifyMenuText(HMENU hMnu, UINT uPosition, LPCTSTR lpNewItem, BOOL fByPos)
{
    MENUITEMINFO mii = { 0 };
    TCHAR szBuf[MAX_PATH] = { 0 };
    mii.cbSize = sizeof(mii);
    mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_DATA;
    mii.fType = MFT_STRING;
    mii.dwTypeData = szBuf;
    mii.cch = _countof(szBuf);

    GetMenuItemInfo(hMnu, uPosition, fByPos, &mii);
    StringCchCopy(szBuf, _countof(szBuf), lpNewItem);
    SetMenuItemInfo(hMnu, uPosition, fByPos, &mii);
}

VOID CDialogBase::ModifyMenuText(HMENU hMnu, UINT uPosition, UINT resourceID, BOOL fByPos)
{
    ModifyMenuText(hMnu, uPosition, LoadString(resourceID).c_str(), fByPos);
}

HWND CDialogBase::GetWndHandle() const
{
    if (::IsWindow(m_hWnd))
    {
        return m_hWnd;
    }

    return NULL;
}

最基础的部分就这两部分, 接下来编写UI界面:

对话框设计如下, 功能简洁, 初期就先这样吧.

 资源对应的声明如下:

resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 CDialogSerialPort.rc 使用
//
#define IDI_ICON                        101
#define IDR_MENU                        102
#define IDS_FILE_EXIT                   103
#define IDS_SETTINGS_SHOW               104
#define IDS_SETTINGS_HIDE               105
#define IDD_DIALOG_SERIAL_PORT          108
#define IDD_DIALOG_ABOUT                110
#define IDC_BUTTON_SEND                 1001
#define IDC_EDIT_READ                   1002
#define IDC_EDIT_WRITE                  1003
#define IDC_COMBO_PORT                  1004
#define IDC_COMBO_SPEED                 1005
#define IDC_COMBO_DATA                  1006
#define IDC_COMBO_PARITY                1007
#define IDC_COMBO_STOP                  1008
#define IDC_BUTTON_OPEN                 1009
#define IDC_CHECK_READ_HEX              1010
#define IDC_BUTTON_CLEAR                1011
#define IDC_CHECK_READ_HEX2             1012
#define IDC_CHECK_WRITE_HEX             1012
#define IDC_EDIT_READ_INTERVAL          1016
#define IDC_EDIT_READ_TOTAL_MULTIPLIER  1017
#define IDC_EDIT_READ_TOTAL_CONSTANT    1018
#define IDC_EDIT_WRITE_TOTAL_MULTIPLIER 1019
#define IDC_EDIT_WRITE_TOTAL_CONSTANT   1020
#define IDC_BUTTON_TIMEOUT              1021
#define IDC_STATIC_TEXT                 1022
#define IDC_SYSLINK_ADDR                1023
#define IDC_EDIT_AUTO_TIME              1024
#define IDC_CHECK_AUTO_SEND             1025
#define ID_FILE_EXIT                    40001
#define ID_SETTINGS_SHOW                40002
#define ID_SETTINGS_HIDE                40003
#define ID_STR_CODE_UNICODE             40008
#define ID_SETTINGS_CODE_UNICODE        40009
#define ID_SETTINGS_CODE_ANSI           40010
#define ID_SETTINGS_CODE_UTF_8          40011
#define ID_HELP_ABOUT                   40013
#define ID_SETTINGS_TOPMOST             40015

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        112
#define _APS_NEXT_COMMAND_VALUE         40016
#define _APS_NEXT_CONTROL_VALUE         1026
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

CDialogSerialPort.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/
#undef APSTUDIO_READONLY_SYMBOLS

/
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)

#ifdef APSTUDIO_INVOKED
/
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON                ICON                    "icon.ico"


/
//
// Version
//

VS_VERSION_INFO VERSIONINFO
 FILEVERSION 1,0,0,1
 PRODUCTVERSION 1,0,0,1
 FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x40004L
 FILETYPE 0x1L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "080404b0"
        BEGIN
            VALUE "CompanyName", "TODO: <公司名>"
            VALUE "FileDescription", "TODO: <文件说明>"
            VALUE "FileVersion", "1.0.0.1"
            VALUE "InternalName", "CWindow.exe"
            VALUE "LegalCopyright", "Copyright (C) 2023"
            VALUE "OriginalFilename", "CWindow.exe"
            VALUE "ProductName", "TODO: <产品名>"
            VALUE "ProductVersion", "1.0.0.1"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x804, 1200
    END
END


/
//
// Menu
//

IDR_MENU MENU
BEGIN
    POPUP "文件(&F)"
    BEGIN
        MENUITEM "退出(&X)",                      ID_FILE_EXIT
    END
    POPUP "设置(&S)"
    BEGIN
        MENUITEM "最前端显示(&T)",                   ID_SETTINGS_TOPMOST
        POPUP "文本编码(&C)"
        BEGIN
            MENUITEM "UNICODE",                     ID_SETTINGS_CODE_UNICODE, CHECKED
            MENUITEM "ANSI",                        ID_SETTINGS_CODE_ANSI
            MENUITEM "UTF-8",                       ID_SETTINGS_CODE_UTF_8
        END
        MENUITEM "显示(&S)",                      ID_SETTINGS_SHOW
        MENUITEM "隐藏(&H)",                      ID_SETTINGS_HIDE
    END
    POPUP "帮助(&H)"
    BEGIN
        MENUITEM "关于(&A)",                      ID_HELP_ABOUT
    END
END


/
//
// Dialog
//

IDD_DIALOG_SERIAL_PORT DIALOGEX 0, 0, 509, 301
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "串口助手 By FlameCyclone"
MENU IDR_MENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    GROUPBOX        "串口配置",IDC_STATIC,6,4,121,290
    GROUPBOX        "状态配置",IDC_STATIC,13,19,103,110
    LTEXT           "串口号:",IDC_STATIC,21,33,28,8
    LTEXT           "波特率:",IDC_STATIC,21,52,28,8
    LTEXT           "数据位:",IDC_STATIC,21,71,28,8
    LTEXT           "校验位:",IDC_STATIC,21,90,28,8
    LTEXT           "停止位:",IDC_STATIC,21,109,28,8
    COMBOBOX        IDC_COMBO_PORT,58,31,48,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
    COMBOBOX        IDC_COMBO_SPEED,58,50,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    COMBOBOX        IDC_COMBO_DATA,58,69,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    COMBOBOX        IDC_COMBO_PARITY,58,88,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    COMBOBOX        IDC_COMBO_STOP,58,107,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
    GROUPBOX        "超时配置",IDC_STATIC,13,137,103,103
    LTEXT           "读间隔超时:",IDC_STATIC,21,153,44,8
    LTEXT           "读时间系数:",IDC_STATIC,21,170,44,8
    LTEXT           "读时间常量:",IDC_STATIC,21,187,44,8
    LTEXT           "写时间系数:",IDC_STATIC,21,204,44,8
    LTEXT           "写时间常量:",IDC_STATIC,21,221,44,8
    EDITTEXT        IDC_EDIT_READ_INTERVAL,71,150,35,14,ES_NUMBER
    EDITTEXT        IDC_EDIT_READ_TOTAL_MULTIPLIER,71,167,35,14,ES_NUMBER
    EDITTEXT        IDC_EDIT_READ_TOTAL_CONSTANT,71,184,35,14,ES_NUMBER
    EDITTEXT        IDC_EDIT_WRITE_TOTAL_MULTIPLIER,71,201,35,14,ES_NUMBER
    EDITTEXT        IDC_EDIT_WRITE_TOTAL_CONSTANT,71,218,35,14,ES_NUMBER
    PUSHBUTTON      "打开串口",IDC_BUTTON_OPEN,13,264,103,21
    GROUPBOX        "接收",IDC_STATIC,134,4,368,192
    CONTROL         "HEX",IDC_CHECK_READ_HEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,464,19,29,10
    EDITTEXT        IDC_EDIT_READ,142,19,316,168,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL
    PUSHBUTTON      "清空",IDC_BUTTON_CLEAR,462,170,32,17
    GROUPBOX        "发送",IDC_STATIC,134,203,368,92
    CONTROL         "HEX",IDC_CHECK_WRITE_HEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,464,217,29,10
    EDITTEXT        IDC_EDIT_WRITE,142,217,316,72,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL
    PUSHBUTTON      "发送",IDC_BUTTON_SEND,462,270,32,17
    EDITTEXT        IDC_EDIT_AUTO_TIME,68,244,30,14,ES_NUMBER
    CONTROL         "自动发送",IDC_CHECK_AUTO_SEND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,246,48,10
    LTEXT           "ms",IDC_STATIC,101,247,10,8
END

IDD_DIALOG_ABOUT DIALOGEX 0, 0, 149, 116
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "关于"
FONT 12, "Microsoft Sans Serif", 400, 0, 0x0
BEGIN
    PUSHBUTTON      "关闭",IDOK,58,96,31,14
    CTEXT           "FC串口助手\r\n\r\nFlameCyclone\r\n\r\n2023.5.29\r\n\r\n版权我有,  盗版难究",IDC_STATIC_TEXT,17,10,113,70,NOT WS_GROUP
    CONTROL         "<a href=""http://flamecyclone.ysepan.com/"">访问 FlameCyclone 的网盘主页</a>",IDC_SYSLINK_ADDR,
                    "SysLink",0x0,21,82,106,15
END


/
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_DIALOG_SERIAL_PORT, DIALOG
    BEGIN
    END

    IDD_DIALOG_ABOUT, DIALOG
    BEGIN
        LEFTMARGIN, 7
        TOPMARGIN, 7
    END
END
#endif    // APSTUDIO_INVOKED


/
//
// AFX_DIALOG_LAYOUT
//

IDD_DIALOG_SERIAL_PORT AFX_DIALOG_LAYOUT
BEGIN
    0
END

IDD_DIALOG_ABOUT AFX_DIALOG_LAYOUT
BEGIN
    0
END


/
//
// String Table
//

STRINGTABLE
BEGIN
    IDS_FILE_EXIT           "退出(&X)"
    IDS_SETTINGS_SHOW       "显示(&S)"
    IDS_SETTINGS_HIDE       "隐藏(&H)"
END

#endif    // 中文(简体,中国) resources
/



#ifndef APSTUDIO_INVOKED
/
//
// Generated from the TEXTINCLUDE 3 resource.
//


/
#endif    // not APSTUDIO_INVOKED

接着实现对话框的消息:

主对话框声明:

CDialogSerialPort.h

#pragma once
#include "CWindow/CDialogBase.h"
#include <vector>
#include "Win32Utils/CSerialPort.h"
#include <imm.h>
#include "CAboutDialog.h"

#ifdef UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif

class CDialogSerialPort :
	public CDialogBase
{
public:


protected:

    dlg_msg LRESULT OnInitDialog(WPARAM wParam, LPARAM lParam);
	dlg_msg LRESULT OnClose(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnTrayIcon(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnRButtonUp(WPARAM wParam, LPARAM lParam); 
    dlg_msg LRESULT OnColor(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnTimer(WPARAM wParam, LPARAM lParam);

    dlg_msg LRESULT OnCommandExit(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandShow(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandHide(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandReadClear(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandReadHexShow(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandWriteHexShow(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandUnicode(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandUtf8(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandAnsi(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandTopmost(WPARAM wParam, LPARAM lParam);

    dlg_msg LRESULT OnStatePort(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnStateSpeed(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnStateData(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnStateParity(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnStateStop(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommOpen(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommSend(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnApplyTime(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandAbout(WPARAM wParam, LPARAM lParam);
    dlg_msg LRESULT OnCommandAutoSend(WPARAM wParam, LPARAM lParam);

protected:

    virtual BOOL PreTranslateMessage(LPMSG pMsg);

private:
    void ShowTrayIcon();
    void DeleteTrayIcon();
    VOID CreatePopMenu(int xPos, int yPos, bool isTop);
    bool ShowForeground(HWND hWnd);
    int GetAllSerials(std::vector<_tstring>& CommList);
    void UpdateSerialDcb();
    void ApplyTimeOut();

    _tstring HexToStr(LPCBYTE lpData, size_t nSize);
    int StrToHex(const _tstring& str, LPBYTE lpData, size_t nSize);

private:
    HMENU m_hPopMenu = NULL;
    HMENU m_hMainMenu = NULL;
    HWND m_hHideWnd = NULL;
    CSerialPort m_SerialPort;
    DCB m_SerialDcb;
    _tstring m_strRead;
    bool m_bReadHex = false;
    bool m_bWriteHex = false;
    size_t m_nReadCount = 0;
    HIMC m_hImc = nullptr;
    HWND m_hWndSend = nullptr;
    HBRUSH m_hBrush = nullptr;
    CAboutDialog m_dlgAbout;
    int m_nStrCode = 0;
    UINT m_uTimer = 0;

	DECLARE_DLG_MESSAGE_MAP()
};

主对话框实现:

CDialogSerialPort.cpp

#include "CDialogSerialPort.h"
#include "Win32Utils/CStrUtils.h"
#include <Windows.h>
#include <strsafe.h>
#include <thread>
#include "resource.h"
#pragma comment (lib ,"imm32.lib") 

#define WM_TRAYICON                 (WM_USER + 10)

BEGIN_DLG_MESSAGE_MAP(CDialogSerialPort, CDialogBase)
    ON_DLG_MESSAGE(WM_INITDIALOG, &CDialogSerialPort::OnInitDialog)
    ON_DLG_MESSAGE(WM_TRAYICON, &CDialogSerialPort::OnTrayIcon)
    ON_DLG_MESSAGE(WM_CLOSE, &CDialogSerialPort::OnClose)
    ON_DLG_MESSAGE(WM_RBUTTONUP, &CDialogSerialPort::OnRButtonUp)
    ON_DLG_MESSAGE(WM_CTLCOLORSTATIC, &CDialogSerialPort::OnColor)
    //ON_DLG_MESSAGE(WM_CTLCOLOREDIT, &CDialogSerialPort::OnColor)
    ON_DLG_MESSAGE(WM_CTLCOLORBTN, &CDialogSerialPort::OnColor)
    ON_DLG_MESSAGE(WM_CTLCOLORDLG, &CDialogSerialPort::OnColor)
    ON_DLG_MESSAGE(WM_TIMER, &CDialogSerialPort::OnTimer)

    ON_DLG_COMMAND(ID_FILE_EXIT, &CDialogSerialPort::OnCommandExit)
    ON_DLG_COMMAND(ID_SETTINGS_SHOW, &CDialogSerialPort::OnCommandShow)
    ON_DLG_COMMAND(ID_SETTINGS_HIDE, &CDialogSerialPort::OnCommandHide)
    ON_DLG_COMMAND(IDC_CHECK_READ_HEX, &CDialogSerialPort::OnCommandReadHexShow)
    ON_DLG_COMMAND(IDC_CHECK_WRITE_HEX, &CDialogSerialPort::OnCommandWriteHexShow)
    ON_DLG_COMMAND(IDC_BUTTON_CLEAR, &CDialogSerialPort::OnCommandReadClear)

    ON_DLG_COMMAND(IDC_COMBO_PORT, &CDialogSerialPort::OnStatePort)
    ON_DLG_COMMAND(IDC_COMBO_SPEED, &CDialogSerialPort::OnStateSpeed)
    ON_DLG_COMMAND(IDC_COMBO_DATA, &CDialogSerialPort::OnStateData)
    ON_DLG_COMMAND(IDC_COMBO_PARITY, &CDialogSerialPort::OnStateParity)
    ON_DLG_COMMAND(IDC_COMBO_STOP, &CDialogSerialPort::OnStateStop)
    ON_DLG_COMMAND(IDC_BUTTON_OPEN, &CDialogSerialPort::OnCommOpen)
    ON_DLG_COMMAND(IDC_BUTTON_SEND, &CDialogSerialPort::OnCommSend)
    ON_DLG_COMMAND(IDC_BUTTON_TIMEOUT, &CDialogSerialPort::OnApplyTime)
    ON_DLG_COMMAND(ID_HELP_ABOUT, &CDialogSerialPort::OnCommandAbout)
    ON_DLG_COMMAND(ID_SETTINGS_CODE_UNICODE, &CDialogSerialPort::OnCommandUnicode)
    ON_DLG_COMMAND(ID_SETTINGS_CODE_UTF_8, &CDialogSerialPort::OnCommandUtf8)
    ON_DLG_COMMAND(ID_SETTINGS_CODE_ANSI, &CDialogSerialPort::OnCommandAnsi)
    ON_DLG_COMMAND(IDC_CHECK_AUTO_SEND, &CDialogSerialPort::OnCommandAutoSend)
    ON_DLG_COMMAND(ID_SETTINGS_TOPMOST, &CDialogSerialPort::OnCommandTopmost)
    
END_DLG_MESSAGE_MAP()

BOOL CDialogSerialPort::PreTranslateMessage(LPMSG pMsg)
{
    if (WM_KEYDOWN == pMsg->message && pMsg->hwnd == m_hWndSend && m_bWriteHex)
    {
        TCHAR ch = (TCHAR)pMsg->wParam;
        SHORT ctrlState = GetAsyncKeyState(VK_CONTROL) & 0x8000;
        if (ctrlState)
        {
            if (ch == _T('C') ||
                ch == _T('V') ||
                ch == _T('X') ||
                ch == _T('A')
                )
            {
                return FALSE;
            }
            else
            {
                return TRUE;
            }
        }

        if (VK_BACK == ch || VK_SPACE == ch || VK_TAB == ch || VK_RETURN == ch)
        {
            return FALSE;
        }

        if ((ch >= _T('0') && ch <= _T('9')) ||
            (ch >= _T('A') && ch <= _T('F'))
            )
        {
            return FALSE;
        }

        return TRUE;
    }

    return FALSE;
}

int CDialogSerialPort::GetAllSerials(std::vector<_tstring>& CommList)
{
    HKEY hKey = nullptr;
    int nretval = 0;
    nretval = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
        _T("Hardware\\DeviceMap\\SerialComm"),
        NULL, KEY_READ, &hKey);

    int i = 0;
    if (nretval == ERROR_SUCCESS)
    {
        TCHAR szPortName[MAX_PATH];

        DWORD dwLong, dwSize;
        while (true)
        {
            TCHAR szCommName[MAX_PATH] = { 0 };
            dwLong = MAX_PATH;
            dwSize = MAX_PATH;
            nretval = RegEnumValue(hKey, i, szPortName, &dwLong,
                NULL, NULL, (PUCHAR)szCommName, &dwSize);
            if (nretval != ERROR_NO_MORE_ITEMS)
            {
                CommList.push_back(szCommName);
            }
            else
            {
                break;
            }
            i++;
        }
        RegCloseKey(hKey);
    }

    return i;
}

void CDialogSerialPort::UpdateSerialDcb()
{
    // 波特率
    {
        TCHAR szBuf[MAX_PATH] = { 0 };
        HWND hWndSpeed = GetDlgItem(m_hWnd, IDC_COMBO_SPEED);
        LRESULT nCurSel = SendMessage(hWndSpeed, CB_GETCURSEL, 0, 0);
        if (CB_ERR != nCurSel)
        {
            SendMessage(hWndSpeed, CB_GETLBTEXT, nCurSel, (LPARAM)szBuf);
            m_SerialDcb.BaudRate = _tcstoul(szBuf, nullptr, 10);
        }
    }

    // 数据位
    {
        HWND hWndSpeed = GetDlgItem(m_hWnd, IDC_COMBO_DATA);
        LRESULT nCurSel = SendMessage(hWndSpeed, CB_GETCURSEL, 0, 0);
        if (0 <= nCurSel && nCurSel <= 3)
        {
            m_SerialDcb.ByteSize = (BYTE)(5 + nCurSel);
        }
        else
        {
            m_SerialDcb.ByteSize = 8;
        }
    }

    // 校验位
    {
        HWND hWndParity = GetDlgItem(m_hWnd, IDC_COMBO_PARITY);
        LRESULT nCurSel = SendMessage(hWndParity, CB_GETCURSEL, 0, 0);

        if (NOPARITY <= nCurSel && nCurSel <= SPACEPARITY)
        {
            m_SerialDcb.Parity = (BYTE)nCurSel;
        }
        else
        {
            m_SerialDcb.Parity = NOPARITY;
        }
    }

    // 停止位
    {
        HWND hWndStop = GetDlgItem(m_hWnd, IDC_COMBO_STOP);
        LRESULT nCurSel = SendMessage(hWndStop, CB_GETCURSEL, 0, 0);
        if (ONESTOPBIT <= nCurSel && nCurSel <= TWOSTOPBITS)
        {
            m_SerialDcb.StopBits = (BYTE)nCurSel;
        }
        else
        {
            m_SerialDcb.StopBits = ONESTOPBIT;
        }
    }

    m_SerialPort.SetState(&m_SerialDcb);
}

void CDialogSerialPort::ApplyTimeOut()
{
    if (m_SerialPort.IsOpen())
    {
        DWORD ReadInterval = ::GetDlgItemInt(m_hWnd, IDC_EDIT_READ_INTERVAL, nullptr, false);
        DWORD ReadTotalMultiplier = ::GetDlgItemInt(m_hWnd, IDC_EDIT_READ_TOTAL_MULTIPLIER, nullptr, false);
        DWORD ReadTotalConstant = ::GetDlgItemInt(m_hWnd, IDC_EDIT_READ_TOTAL_CONSTANT, nullptr, false);
        DWORD WriteTotalMultiplier = ::GetDlgItemInt(m_hWnd, IDC_EDIT_WRITE_TOTAL_MULTIPLIER, nullptr, false);
        DWORD WriteTotalConstant = ::GetDlgItemInt(m_hWnd, IDC_EDIT_WRITE_TOTAL_CONSTANT, nullptr, false);

        m_SerialPort.Purge(PURGE_TXCLEAR | 
            PURGE_RXCLEAR | 
            PURGE_TXABORT | 
            PURGE_RXABORT);

        m_SerialPort.ClearError();
        m_SerialPort.SetTimeOut(ReadInterval,
            ReadTotalMultiplier,
            ReadTotalConstant,
            WriteTotalMultiplier,
            WriteTotalConstant);
    }
}

LRESULT CDialogSerialPort::OnInitDialog(WPARAM wParam, LPARAM lParam)
{
    // 设置图标
    HICON hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON));
    if (hIcon)
    {
        ::SendMessage(m_hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
        ::SendMessage(m_hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
    }

    m_hHideWnd = CreateWindow(
        _T("Static"), _T("Hide"), WS_CHILDWINDOW, 0, 0, 0, 0,
        m_hWnd, NULL, GetModuleHandle(NULL), NULL);
    if (m_hHideWnd)
    {
        ::SetParent(m_hHideWnd, NULL);
    }

    m_hMainMenu = ::GetMenu(m_hWnd);
    if (nullptr == m_hMainMenu)
    {
        m_hMainMenu = ::LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MENU));
    }
    if (m_hMainMenu)
    {
        ::SetMenu(m_hWnd, m_hMainMenu);
        ShowTrayIcon();
    }

    {
        HWND hWndPort = GetDlgItem(m_hWnd, IDC_COMBO_PORT);
        std::vector<_tstring> CommList;
        GetAllSerials(CommList);
        for (auto item : CommList)
        {
            SendMessage(hWndPort, CB_ADDSTRING, 0, (LPARAM)item.c_str());
        }

        if (!CommList.empty())
        {
            SendMessage(hWndPort, CB_SETCURSEL, 0, 0);
        }
    }

    {
        std::vector<int> vSpeed = {
        75,
        110,
        134,
        150,
        300,
        600,
        1200,
        1800,
        2400,
        4800,
        7200,
        9600,
        14400,
        19200,
        38400,
        56000,
        57600,
        115200,
        128000,
        256000,
        };

        HWND hWndSpeed = GetDlgItem(m_hWnd, IDC_COMBO_SPEED);
        for (auto item : vSpeed)
        {
            TCHAR szBuf[MAX_PATH] = { 0 };
            _ultot_s(item, szBuf, 10);
            SendMessage(hWndSpeed, CB_ADDSTRING, 0, (LPARAM)szBuf);
        }

        if (!vSpeed.empty())
        {
            SendMessage(hWndSpeed, CB_SETCURSEL, vSpeed.size() - 3, 0);
        }
    }

    {
        std::vector<int> vData = { 5,6,7,8 };
        HWND hWndData = GetDlgItem(m_hWnd, IDC_COMBO_DATA);
        for (auto item : vData)
        {
            TCHAR szBuf[MAX_PATH] = { 0 };
            _ultot_s(item, szBuf, 10);
            SendMessage(hWndData, CB_ADDSTRING, 0, (LPARAM)szBuf);
        }

        if (!vData.empty())
        {
            SendMessage(hWndData, CB_SETCURSEL, 3, 0);
        }
    }

    {
        std::vector<_tstring> vParity = {
        _T("None"),
        _T("Odd"),
        _T("Even"),
        _T("Mark"),
        _T("Space")

        };
        HWND hWndParity = GetDlgItem(m_hWnd, IDC_COMBO_PARITY);
        for (auto item : vParity)
        {
            SendMessage(hWndParity, CB_ADDSTRING, 0, (LPARAM)item.c_str());
        }

        if (!vParity.empty())
        {
            SendMessage(hWndParity, CB_SETCURSEL, 0, 0);
        }
    }

    {
        std::vector<_tstring> vParity = {
        _T("1"),
        _T("1.5"),
        _T("2"),

        };
        HWND hWndStop = GetDlgItem(m_hWnd, IDC_COMBO_STOP);
        for (auto item : vParity)
        {
            SendMessage(hWndStop, CB_ADDSTRING, 0, (LPARAM)item.c_str());
        }

        if (!vParity.empty())
        {
            SendMessage(hWndStop, CB_SETCURSEL, 0, 0);
        }
    }

    CheckDlgButton(m_hWnd, IDC_CHECK_READ_HEX, BST_CHECKED);
    m_bReadHex = true;
    CheckDlgButton(m_hWnd, IDC_CHECK_WRITE_HEX, BST_CHECKED);
    m_bWriteHex = true;
    m_hWndSend = ::GetDlgItem(m_hWnd, IDC_EDIT_WRITE);
    ::SetDlgItemText(m_hWnd, IDC_EDIT_AUTO_TIME, _T("1000"));
    m_hImc = ImmAssociateContext(m_hWndSend, nullptr);

    UpdateSerialDcb();

    ::SetDlgItemText(m_hWnd, IDC_EDIT_WRITE, _T("E7 00 00 22 02 00 00 00 0F 33 ED"));

    ::SetDlgItemInt(m_hWnd, IDC_EDIT_READ_INTERVAL, 50, false);
    ::SetDlgItemInt(m_hWnd, IDC_EDIT_READ_TOTAL_MULTIPLIER, 5, false);
    ::SetDlgItemInt(m_hWnd, IDC_EDIT_READ_TOTAL_CONSTANT, 500, false);
    ::SetDlgItemInt(m_hWnd, IDC_EDIT_WRITE_TOTAL_MULTIPLIER, 0, false);
    ::SetDlgItemInt(m_hWnd, IDC_EDIT_WRITE_TOTAL_CONSTANT, 0, false);

    return TRUE;
}

LRESULT CDialogSerialPort::OnClose(WPARAM wParam, LPARAM lParam)
{
    if (m_hBrush)
    {
        ::DeleteObject(m_hBrush);
    }

    DeleteTrayIcon();
    return (LRESULT)FALSE;
}

LRESULT CDialogSerialPort::OnCommandExit(WPARAM wParam, LPARAM lParam)
{
    m_dlgAbout.EndDialog(0);
    EndDialog(IDOK);
    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnCommandShow(WPARAM wParam, LPARAM lParam)
{
    ShowWindow(SW_SHOW);
    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnCommandHide(WPARAM wParam, LPARAM lParam)
{
    ShowWindow(SW_HIDE);
    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnCommandReadHexShow(WPARAM wParam, LPARAM lParam)
{
    HWND hReadHex = ::GetDlgItem(m_hWnd, IDC_CHECK_READ_HEX);
    LRESULT hr = ::SendMessage(hReadHex, BM_GETCHECK, 0, 0);

    if (BST_CHECKED == hr)
    {
        m_bReadHex = true;
    }
    else
    {
        m_bReadHex = false;
    }

    return (LRESULT)TRUE;
}

_tstring CDialogSerialPort::HexToStr(LPCBYTE lpData, size_t nSize)
{
    _tstring strRes;
    TCHAR szBuf[MAX_PATH] = { 0 };

    for (int i = 0; i < nSize; i++)
    {
        if (i + 1 < nSize)
        {
            StringCchPrintf(szBuf, _countof(szBuf), _T("%0.2X "), lpData[i]);
        }
        else
        {
            StringCchPrintf(szBuf, _countof(szBuf), _T("%0.2X"), lpData[i]);
        }
        strRes += szBuf;
    }

    return strRes;
}

int CDialogSerialPort::StrToHex(const _tstring& str, LPBYTE lpData, size_t nSize)
{
    _tstring strWide;
    std::string strMulti;
    int nDataIndex = 0;

    strWide = str;
    _tstring strHex;
    int nIndex = 0;
    for (int i = 0; i < strWide.size(); i++)
    {
        if (0 == nIndex)
        {
            strHex.clear();
        }

        if (str[i] == _T(' ') || str[i] == _T('\r') || str[i] == _T('\n'))
        {
            continue;
        }

        strHex += str[i];
        nIndex++;

        if (2 == nIndex && nDataIndex < nSize)
        {
            nIndex = 0;
            lpData[nDataIndex++] = (BYTE)_tcstoul(strHex.c_str(), nullptr, 16);
        }
    }

    return nDataIndex;
}

LRESULT CDialogSerialPort::OnApplyTime(WPARAM wParam, LPARAM lParam)
{
    ApplyTimeOut();
    return TRUE;
}

LRESULT CDialogSerialPort::OnCommandAbout(WPARAM wParam, LPARAM lParam)
{
    m_dlgAbout.DoModalEx(IDD_DIALOG_ABOUT, m_hWnd);
    return TRUE;
}

LRESULT CDialogSerialPort::OnCommandAutoSend(WPARAM wParam, LPARAM lParam)
{
    LRESULT ret = SendMessage(
        ::GetDlgItem(m_hWnd, IDC_CHECK_AUTO_SEND), 
        BM_GETCHECK, 
        0, 
        0);

    if (BST_CHECKED == ret)
    {
        UINT uTime = ::GetDlgItemInt(m_hWnd, IDC_EDIT_AUTO_TIME, nullptr, false);
        if (uTime < USER_TIMER_MINIMUM)
        {
            uTime = USER_TIMER_MINIMUM;
            ::SetDlgItemInt(m_hWnd, IDC_EDIT_AUTO_TIME, uTime, false);
        }
        m_uTimer = ::SetTimer(m_hWnd, 0x200, uTime, nullptr);
        ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT_AUTO_TIME), false);
    }
    else
    {
        ::KillTimer(m_hWnd, m_uTimer);
        ::EnableWindow(::GetDlgItem(m_hWnd, IDC_EDIT_AUTO_TIME), true);
    }

    return TRUE;
}

LRESULT CDialogSerialPort::OnCommandWriteHexShow(WPARAM wParam, LPARAM lParam)
{
    HWND hWriteHex = ::GetDlgItem(m_hWnd, IDC_CHECK_WRITE_HEX);
    LRESULT ret = SendMessage(hWriteHex, BM_GETCHECK, 0, 0);
    static TCHAR szWriteBuf[32768] = { 0 };

    if (BST_CHECKED == ret)
    {
        m_bWriteHex = true;
        UINT uLen = ::GetDlgItemText(m_hWnd, IDC_EDIT_WRITE, szWriteBuf, _countof(szWriteBuf));
        _tstring strText;

        if (0 == m_nStrCode)
        {
            _tstring strDest = szWriteBuf;
            strText = HexToStr((LPBYTE)strDest.c_str(), uLen * sizeof(TCHAR));
        }
        else if (1 == m_nStrCode)
        {
            std::string strDest = CStrUtils::TStrToAStr(szWriteBuf);
            strText = HexToStr((LPBYTE)strDest.c_str(), strDest.size());
        }
        else if (2 == m_nStrCode)
        {
            std::string strDest = CStrUtils::TStrToU8Str(szWriteBuf);
            strText = HexToStr((LPBYTE)strDest.c_str(), strDest.size());
        }

        ::SetDlgItemText(m_hWnd, IDC_EDIT_WRITE, strText.c_str());
        HWND hEditSend = ::GetDlgItem(m_hWnd, IDC_EDIT_WRITE);
        m_hImc = ::ImmAssociateContext(hEditSend, nullptr);
    }
    else
    {
        m_bWriteHex = false;
        ::GetDlgItemText(m_hWnd, IDC_EDIT_WRITE, szWriteBuf, _countof(szWriteBuf));
        static BYTE szCharBuf[32768] = { 0 };
        ::ZeroMemory(szCharBuf, sizeof(szCharBuf));
        StrToHex(szWriteBuf, szCharBuf, sizeof(szCharBuf));

        if (0 == m_nStrCode)
        {
            ::SetDlgItemText(m_hWnd, IDC_EDIT_WRITE, (LPCTSTR)szCharBuf);
        }
        else if (1 == m_nStrCode)
        {
            ::SetDlgItemText(m_hWnd, IDC_EDIT_WRITE, CStrUtils::AStrToTStr((LPCSTR)szCharBuf).c_str());
        }

        else if (2 == m_nStrCode)
        {
            ::SetDlgItemText(m_hWnd, IDC_EDIT_WRITE, CStrUtils::U8StrToTStr((LPCSTR)szCharBuf).c_str());
        }

        HWND hEditSend = ::GetDlgItem(m_hWnd, IDC_EDIT_WRITE);
        ::ImmAssociateContext(hEditSend, m_hImc);
    }

    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnCommandUnicode(WPARAM wParam, LPARAM lParam)
{
    m_nStrCode = 0;
    for (int i = ID_SETTINGS_CODE_UNICODE; i <= ID_SETTINGS_CODE_UTF_8; i++)
    {
        ::CheckMenuItem(::GetMenu(m_hWnd), i, MF_BYCOMMAND | MF_UNCHECKED);
    }
    ::CheckMenuItem(::GetMenu(m_hWnd), wParam, MF_BYCOMMAND | MF_CHECKED);
    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnCommandAnsi(WPARAM wParam, LPARAM lParam)
{
    m_nStrCode = 1;
    for (int i = ID_SETTINGS_CODE_UNICODE; i <= ID_SETTINGS_CODE_UTF_8; i++)
    {
        ::CheckMenuItem(::GetMenu(m_hWnd), i, MF_BYCOMMAND | MF_UNCHECKED);
    }
    ::CheckMenuItem(::GetMenu(m_hWnd), wParam, MF_BYCOMMAND | MF_CHECKED);
    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnCommandTopmost(WPARAM wParam, LPARAM lParam)
{
    DWORD dwState = ::GetMenuState(GetMenu(m_hWnd), wParam, MF_BYCOMMAND | MF_CHECKED);
    ::SetWindowPos(m_hWnd, 
        MF_CHECKED & dwState ? HWND_NOTOPMOST : HWND_TOPMOST,
        0, 0, 0, 0, 
        SWP_NOSIZE | SWP_NOMOVE);
    ::CheckMenuItem(GetMenu(m_hWnd), wParam, MF_BYCOMMAND | (MF_CHECKED & dwState ? MF_UNCHECKED : MF_CHECKED));
    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnCommandUtf8(WPARAM wParam, LPARAM lParam)
{
    m_nStrCode = 2;
    for (int i = ID_SETTINGS_CODE_UNICODE; i <= ID_SETTINGS_CODE_UTF_8; i++)
    {
        ::CheckMenuItem(::GetMenu(m_hWnd), i, MF_BYCOMMAND | MF_UNCHECKED);
    }
    ::CheckMenuItem(::GetMenu(m_hWnd), wParam, MF_BYCOMMAND | MF_CHECKED);
    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnCommandReadClear(WPARAM wParam, LPARAM lParam)
{
    m_strRead.clear();

    HWND hWndRead = ::GetDlgItem(m_hWnd, IDC_EDIT_READ);
    ::SetWindowText(hWndRead, _T(""));
    m_nReadCount = 0;
    return TRUE;
}

LRESULT CDialogSerialPort::OnStatePort(WPARAM wParam, LPARAM lParam)
{
    if (CBN_SELCHANGE == HIWORD(wParam))
    {
        UpdateSerialDcb();
        if (m_SerialPort.IsOpen())
        {
            m_SerialPort.Close();
            ::SetDlgItemText(m_hWnd, IDC_BUTTON_OPEN, _T("打开串口"));
            ::PostMessage(m_hWnd, WM_COMMAND, IDC_BUTTON_OPEN, 0);
        }
    }

    return TRUE;
}

LRESULT CDialogSerialPort::OnStateSpeed(WPARAM wParam, LPARAM lParam)
{
    if (CBN_SELCHANGE == HIWORD(wParam))
    {
        UpdateSerialDcb();
    }
    return TRUE;
}

LRESULT CDialogSerialPort::OnStateData(WPARAM wParam, LPARAM lParam)
{
    if (CBN_SELCHANGE == HIWORD(wParam))
    {
        UpdateSerialDcb();
    }
    return TRUE;
}

LRESULT CDialogSerialPort::OnStateParity(WPARAM wParam, LPARAM lParam)
{
    if (CBN_SELCHANGE == HIWORD(wParam))
    {
        UpdateSerialDcb();
    }
    return TRUE;
}

LRESULT CDialogSerialPort::OnStateStop(WPARAM wParam, LPARAM lParam)
{
    if (CBN_SELCHANGE == HIWORD(wParam))
    {
        UpdateSerialDcb();
    }
    return TRUE;
}

LRESULT CDialogSerialPort::OnCommOpen(WPARAM wParam, LPARAM lParam)
{
    if (m_SerialPort.IsOpen())
    {
        m_SerialPort.Close();
        ::SetDlgItemText(m_hWnd, IDC_BUTTON_OPEN, _T("打开串口"));

        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_READ_INTERVAL), TRUE);
        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_READ_TOTAL_MULTIPLIER), TRUE);
        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_READ_TOTAL_CONSTANT), TRUE);
        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_WRITE_TOTAL_MULTIPLIER), TRUE);
        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_WRITE_TOTAL_CONSTANT), TRUE);
    }
    else
    {
        TCHAR szBuf[MAX_PATH] = { 0 };
        HWND hWndPort = GetDlgItem(m_hWnd, IDC_COMBO_PORT);
        LRESULT nCurSel = SendMessage(hWndPort, CB_GETCURSEL, 0, 0);
        bool bOpen = false;
        if (CB_ERR != nCurSel)
        {
            SendMessage(hWndPort, CB_GETLBTEXT, nCurSel, (LPARAM)szBuf);
            bOpen = m_SerialPort.Open(
                szBuf, 
                m_SerialDcb.BaudRate,
                m_SerialDcb.ByteSize,
                m_SerialDcb.Parity,
                m_SerialDcb.StopBits);

            ApplyTimeOut();
        }

        if (!bOpen)
        {
            MessageBox(m_hWnd, _T("打开串口失败!"), _T("提示"), MB_OK | MB_ICONERROR);
            return TRUE;
        }

        ::SetDlgItemText(m_hWnd, IDC_BUTTON_OPEN, _T("关闭串口"));
        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_READ_INTERVAL), FALSE);
        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_READ_TOTAL_MULTIPLIER), FALSE);
        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_READ_TOTAL_CONSTANT), FALSE);
        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_WRITE_TOTAL_MULTIPLIER), FALSE);
        ::EnableWindow(GetDlgItem(m_hWnd, IDC_EDIT_WRITE_TOTAL_CONSTANT), FALSE);

        std::thread([this]()->void {
            static BYTE szReadBuf[32768] = { 0 };
            bool bSuccess = false;
            DWORD dwReadSize = 0;

            HWND hWndRead = GetDlgItem(m_hWnd, IDC_EDIT_READ);
            while (m_SerialPort.IsOpen())
            {
                m_SerialPort.Purge(PURGE_RXCLEAR);
                bSuccess = m_SerialPort.Read(szReadBuf,
                    sizeof(szReadBuf),
                    &dwReadSize,
                    5000);

                if (bSuccess)
                {
                    TCHAR szBuf[MAX_PATH] = { 0 };
                    StringCchPrintf(szBuf, _countof(szBuf), _T("[%0.4d: %d Bytes] "), ++m_nReadCount, dwReadSize);
                    _tstring strText = szBuf;
                    szReadBuf[dwReadSize] = 0;

                    if (m_bReadHex)
                    {
                        szReadBuf[dwReadSize + 1] = 0;
                        strText += HexToStr((LPCBYTE)szReadBuf, dwReadSize);
                    }
                    else
                    {
                        _tstring strContent;
                        if (0 == m_nStrCode)
                        {
                            strContent = (LPCTSTR)szReadBuf;
                        }
                        if (1 == m_nStrCode)
                        {
                            strContent = CStrUtils::AStrToTStr((LPCSTR)szReadBuf);
                        }
                        if (2 == m_nStrCode)
                        {
                            strContent = CStrUtils::U8StrToTStr((LPCSTR)szReadBuf);
                        }

                        strText += strContent;
                    }

                    if (!m_strRead.empty())
                    {
                        m_strRead += _T("\r\n");
                    }

                    m_strRead += strText;
                    ::SendMessage(hWndRead, WM_SETREDRAW, FALSE, 0);
                    ::SetWindowText(hWndRead, m_strRead.c_str());
                    ::SendMessage(hWndRead, EM_SCROLL, SB_BOTTOM, 0);
                    ::SendMessage(hWndRead, WM_SETREDRAW, TRUE, 0);
                }
            }
            
            }).detach();
    }

    return TRUE;
}

LRESULT CDialogSerialPort::OnCommSend(WPARAM wParam, LPARAM lParam)
{
    if (m_SerialPort.IsOpen())
    {
        static TCHAR szBuf[32768] = { 0 };
        HWND hWndEditSend = GetDlgItem(m_hWnd, IDC_EDIT_WRITE);
        ::GetWindowText(hWndEditSend, szBuf, _countof(szBuf));
        _tstring strData = szBuf;

        if (strData.empty())
        {
            return TRUE;
        }

        m_SerialPort.Purge(PURGE_TXCLEAR);
        if (m_bWriteHex)
        {
            static BYTE szByteBuf[32768] = { 0 };
            ZeroMemory(szByteBuf, sizeof(szByteBuf));
            int nSize = StrToHex(strData, szByteBuf, sizeof(szByteBuf));
            m_SerialPort.Write(szByteBuf, nSize, nullptr, 1000);
        }
        else
        {
            if (0 == m_nStrCode)
            {
                size_t nSize = strData.size() * sizeof(TCHAR);
                m_SerialPort.Write(strData.c_str(), (DWORD)nSize, nullptr, (DWORD)1000);
            }
            else if (1 == m_nStrCode)
            {
                std::string strDest = CStrUtils::TStrToAStr(szBuf);
                m_SerialPort.Write(strDest.c_str(), (DWORD)strDest.size(), nullptr, (DWORD)1000);
            }
            else if (2 == m_nStrCode)
            {
                std::string strDest = CStrUtils::TStrToU8Str(szBuf);
                m_SerialPort.Write(strDest.c_str(), (DWORD)strDest.size(), nullptr, (DWORD)1000);
            }
        }
    }

    return TRUE;
}

LRESULT CDialogSerialPort::OnTrayIcon(WPARAM wParam, LPARAM lParam)
{
    if (WM_LBUTTONDBLCLK == lParam)
    {
        ShowForeground(m_hWnd);
    }

    if (NULL != m_hHideWnd)
    {
        ::SetForegroundWindow(m_hHideWnd);
    }

    if (WM_RBUTTONUP == lParam)
    {
        HWND hForegroundWindow = GetForegroundWindow();
        if (hForegroundWindow == m_hHideWnd || hForegroundWindow == m_hWnd)
        {
            POINT pt = { 0 };
            ::GetCursorPos(&pt);
            CreatePopMenu(pt.x, pt.y, false);
        }
    }

    return FALSE;
}

LRESULT CDialogSerialPort::OnRButtonUp(WPARAM wParam, LPARAM lParam)
{
    POINT pt = { 0 };
    ::GetCursorPos(&pt);
    CreatePopMenu(pt.x, pt.y, false);
    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnColor(WPARAM wParam, LPARAM lParam)
{
    HDC hdc = (HDC)wParam;
    SetBkColor(hdc, RGB(255, 0, 0));

    SetBkMode(hdc, TRANSPARENT);
    if (NULL == m_hBrush)
    {
        m_hBrush = CreateSolidBrush(RGB(192, 224, 255));
    }

    return (LRESULT)m_hBrush;

    return (LRESULT)TRUE;
}

LRESULT CDialogSerialPort::OnTimer(WPARAM wParam, LPARAM lParam)
{
    if (wParam == m_uTimer)
    {
        ::PostMessage(m_hWnd, WM_COMMAND, IDC_BUTTON_SEND, 0);
    }

    return (LRESULT)TRUE;
}

bool CDialogSerialPort::ShowForeground(HWND hWnd)
{
    HWND hForeWnd = ::GetForegroundWindow();
    DWORD dwForeID = ::GetWindowThreadProcessId(hForeWnd, NULL);
    DWORD dwCurID = ::GetCurrentThreadId();
    ::AttachThreadInput(dwCurID, dwForeID, TRUE);
    BOOL isSuc = ::SetForegroundWindow(hWnd);
    ::AttachThreadInput(dwCurID, dwForeID, FALSE);

    if (isSuc)
    {
        //return true;
    }

    if (!::IsWindowVisible(hWnd))
    {
        ::ShowWindow(hWnd, SW_SHOW);
    }

    if (IsIconic(hWnd))
    {
        ::ShowWindow(hWnd, SW_SHOWNORMAL);
    }

    return ::SetForegroundWindow(hWnd);
}

void CDialogSerialPort::ShowTrayIcon()
{
    NOTIFYICONDATA nid = { 0 };
    nid.cbSize = sizeof(NOTIFYICONDATA);
    nid.hWnd = m_hWnd;
    nid.uID = 0;
    nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    nid.uCallbackMessage = WM_TRAYICON;
    nid.hIcon = ::LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON));
    StringCchCopy(nid.szTip, _countof(nid.szTip), _T("Demo"));
    Shell_NotifyIcon(NIM_ADD, &nid);
}

void CDialogSerialPort::DeleteTrayIcon()
{
    NOTIFYICONDATA nid = { 0 };
    nid.cbSize = sizeof(nid);
    nid.hWnd = m_hWnd;
    nid.uID = 0;
    nid.uFlags = 0;
    Shell_NotifyIcon(NIM_DELETE, &nid);
}

VOID CDialogSerialPort::CreatePopMenu(int xPos, int yPos, bool isTop)
{
    MENUITEMINFO mii = { 0 };
    TCHAR szBuf[MAX_PATH] = { 0 };

    mii.cbSize = sizeof(mii);
    mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_SUBMENU | MIIM_ID;
    mii.fType = MFT_STRING | MFT_OWNERDRAW;
    mii.dwTypeData = szBuf;

    if (!m_hWnd)
    {
        return;
    }

    {
        HMENU hMenu = m_hMainMenu;
        ModifyMenuText(hMenu, ID_FILE_EXIT, IDS_FILE_EXIT, FALSE);
        ModifyMenuText(hMenu, ID_SETTINGS_SHOW, IDS_SETTINGS_SHOW, FALSE);
        ModifyMenuText(hMenu, ID_SETTINGS_HIDE, IDS_SETTINGS_HIDE, FALSE);
    }

    int nIndex = 0;

    if (NULL == m_hPopMenu)
    {
        m_hPopMenu = CreatePopupMenu();
        for (int i = 0; i < 4; i++)
        {
            mii.cch = _countof(szBuf);
            ::GetMenuItemInfo(GetSubMenu(m_hMainMenu, 1), i, TRUE, &mii);
            ::InsertMenuItem(m_hPopMenu, nIndex++, TRUE, &mii);
        }
        mii.cch = _countof(szBuf);
        ::GetMenuItemInfo(GetSubMenu(m_hMainMenu, 0), 0, TRUE, &mii);
        ::InsertMenuItem(m_hPopMenu, nIndex++, TRUE, &mii);
    }
    else
    {
        for (int i = 0; i < 4; i++)
        {
            mii.cch = _countof(szBuf);
            ::GetMenuItemInfo(GetSubMenu(m_hMainMenu, 1), i, TRUE, &mii);
            ::SetMenuItemInfo(m_hPopMenu, nIndex++, TRUE, &mii);
        }
        mii.cch = _countof(szBuf);
        ::GetMenuItemInfo(GetSubMenu(m_hMainMenu, 0), 0, TRUE, &mii);
        ::SetMenuItemInfo(m_hPopMenu, nIndex++, TRUE, &mii);
    }

    TrackPopupMenuEx(m_hPopMenu, TPM_LEFTALIGN | (isTop ? TPM_BOTTOMALIGN : TPM_TOPALIGN),
        xPos, yPos, m_hWnd, NULL);
}

主体编码就算完成了, 资源视图如下:

 解决方案视图如下:

运行效果如下:

 

 

 资源链接详见: Win32api编写的串口助手源码与二进制资源-CSDN文库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/588054.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JetBrains的Go语言集成开发环境GoLand 2023版本在Linux系统的下载与安装配置教程

目录 前言一、GoLand 安装二、使用配置总结 前言 GoLand是一款专为Go语言开发人员设计的集成开发环境&#xff08;IDE&#xff09;。它提供了丰富的功能和工具&#xff0c;可以帮助开发人员更高效地编写、调试和部署Go应用程序。注&#xff1a;已在CentOS7.9和Ubuntu20.04安装…

删除排序数组的重复项

给定一个排序数组&#xff0c;你需要在原地删除重复出现的元素&#xff0c;使得每个元素只出现一次&#xff0c;返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 示例 1: 给定数组 nums [1,1,2…

JavaEE进阶5/25(属性注入)

目录 1.更简单的存取Spring对象 2.获取Bean对象&#xff08;对象装配&#xff09;DI 3. Resource注入 4.Resource注入和Autowired注入的区别 1.更简单的存取Spring对象 2.获取Bean对象&#xff08;对象装配&#xff09;DI 对象装配&#xff08;对象注入&#xff09;有三种方…

含镍废水树脂吸附工艺方案

项目基本信息 工艺及产品信息 甲方 欣兴同泰科技(昆山)有限公司 采用工艺 沉淀工艺过滤系统离子交换放流池 工程公司 / 工艺原理 镍离子以氢氧化物的形式做成镍泥&#xff0c;清液中微量镍离子通过螯合离子交换原理实现出水稳定达标 开始时间 2019/6/20 工艺特点 …

遥感云大数据在灾害、水体与湿地领域典型案例实践及GPT模型

近年来遥感技术得到了突飞猛进的发展&#xff0c;航天、航空、临近空间等多遥感平台不断增加&#xff0c;数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量猛增&#xff0c;遥感数据已经越来越具有大数据特征。遥感大数据的出现为相关研究提供了前所未有的机遇&#xf…

TortoiseGit的安装及使用, 并配合Gitee码云使用

Windows10下, TortoiseGit的安装及使用, 并配合Gitee码云使用! 1) 安装TortoiseGit 官网, 32位, 64位, 自选 Download – TortoiseGit – Windows Shell Interface to Git 2) 点击下载, 安装, 我选择的是64位, 中文安装包(说明: 中文安装包也分32, 64位), 这两个下载. Tips:…

在ubuntu上安装splint

lint lint是最著名的C语言工具之一&#xff0c;是由贝尔实验室SteveJohnson于1979在PCC(PortableC Compiler)基础上开发的静态代码分析&#xff0c;一般由UNIX系统提供。 工具介绍 与大多数C语言编译器相比&#xff0c;lint可以对程序进行更加广泛的错误分析&#xff0c;是一…

2023数博会 | 李雨航:新一代数据安全国际实践,CSA引领数据安全3.0时代

5月26-28日&#xff0c;2023中国国际大数据产业博览会&#xff08;简称&#xff1a;数博会&#xff09;在贵州贵阳举办。大会由国家发展和改革委员会、工业和信息化部、国家互联网信息办公室和贵州省人民政府共同主办&#xff0c;是全球首个以大数据为主题的博览会。今年数博会…

css 实现丰富的序号效果

<ol><li><p>日本新潟佐渡岛</p></li><li><p>宣告“飞岛萱草”</p><ol><li><p>迎来最佳观赏期。</p><ol><li><p>据观光协会介绍&#xff0c;</p></li><li><p&…

Linux内核驱动 --- CCF框架 provider驱动的编写

Provider驱动编写流程 复制上节内容中对Provider驱动编写流程的总结&#xff1a; 1&#xff09;分析硬件的clock tree&#xff0c;按照上面所描述的分类&#xff0c;将这些clock分类。 2&#xff09;将clock tree在DTS中描述出来&#xff0c;需要注意以下几2点&#xff1a; …

自定义集合和ES6集合

概念 集合是由一组无序且唯一的项组成的。 空集是指不含任何元素的集合。 说在前面 虽然es6已经有了Set类。但是我们还是希望自己来实现Set类。 原生的Set类参考我这篇博文&#xff1a; JS中数组如何去重&#xff08;ES6新增的Set集合类型&#xff09;经典two sum面试题ht…

C++进阶——哈希的实现

C进阶——哈希的实现 unordered系列关联式容器 在C11出现中有了重大更新就是添加了移动构造和unordered关联容器。在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到O( l o g 2 N log_2 N log2​N)&#xff0c;即最差情况下需要…

MYSQL高级之关联查询优化

建表 CREATE TABLE IF NOT EXISTS class ( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS book ( bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRI…

vue router实现路由跳转方法

今天在学习 vue的过程中&#xff0c;看到了 vue的 router&#xff0c;用它来实现路由跳转&#xff0c;非常方便&#xff0c;于是就尝试了一下。效果还不错。 首先我们需要了解一个概念&#xff1a; Router。 Router是一个接口&#xff0c;它提供了一个接口让我们可以从一个地方…

Flutter 笔记 | Flutter 核心原理(二)关键类和启动流程

Widget、Element、BuildContext 和 RenderObject Widget Widget关键类及其子类继承关系如图所示&#xff1a; 其中&#xff0c;Widget是Widget Tree所有节点的基类。Widget的子类主要分为3类&#xff1a; 第1类是RenderObjectWidget的子类&#xff0c;具体来说又分为SingleCh…

08. 算法之递归算法

前言 递归&#xff0c;字面意思是递出去&#xff0c;拿回来&#xff0c;通过不断递过去&#xff0c;拿回来的过程&#xff0c;将每次调用结果保存起来&#xff0c;最后实现循环调用。递归在某些情况下会极大降低我们编程的复杂度。是软件开发工程师一定要掌握的技能。 1. 概念…

Linux—实操篇:vi和vim编辑器

1.vi和vim基本介绍 Linux系统会内置vi文本编辑器 vim具有程序编写的能力&#xff0c;可以看做是vi的增强版本&#xff0c;被程序员广泛使用 2、vi和vim常用的三种模式 2.1、正常模式 以vim打开一个档案就直接进入一般模式了(这是默认的模式)。在这个模式中&#xff0c;你可…

溯源取证 - 流量分析 中等难度

使用工具&#xff1a; Brim 链接: https://www.brimdata.io/download/ Networkminer 链接: https://www.netresec.com/?pageNetworkMiner Wireshark Strings ida pro 知识点&#xff1a; 通过本篇文章&#xff0c;学习ssh协议特点、学习流量导出文件、学习简单的逆向分析、…

卫星定位北斗芯片AT6558一款高性能BDS/GNSS多模卫星导航接收机SOC单芯片

1 芯片简介 AT6558R是一款高性能BDS/GNSS多模卫星导航接收机SOC单芯片,片上集成射频前端&#xff0c; 数字基带处理器&#xff0c;32位的RISCCPU&#xff0c;电源管理功能。 芯片支持多种卫星导航系统&#xff0c;包括中国的北斗卫星导航系统BDS&#xff0c;美国的GPS,俄罗斯 的…

Mysql DDL执行方式-pt-osc介绍 | 京东云技术团队

1 引言 大家好&#xff0c;接着上次和大家一起学习了《MySQL DDL执行方式-Online DDL介绍》&#xff0c;那么今天接着和大家一起学习另一种MySQL DDL执行方式之pt-soc。 在MySQL使用过程中&#xff0c;根据业务的需求对表结构进行变更是个普遍的运维操作&#xff0c;这些称为…