VC++中使用GDI+自定义绘制信号灯

VC++中使用GDI+自定义透明效果绘制信号灯

StatusIndicatorCtrl.h 头文件

C++ 复制代码
#pragma once
#include <afxwin.h>
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")

// 状态改变事件参数
class CStatusChangedEventArgs
{
public:
    BOOL m_bOldStatus;
    BOOL m_bNewStatus;

    CStatusChangedEventArgs(BOOL oldStatus, BOOL newStatus)
        : m_bOldStatus(oldStatus), m_bNewStatus(newStatus) {}
};

// 状态改变回调函数类型
typedef void (*STATUS_CHANGED_CALLBACK)(CWnd* pSender, CStatusChangedEventArgs& args);

class CStatusIndicatorCtrl : public CWnd
{
    DECLARE_DYNAMIC(CStatusIndicatorCtrl)

public:
    CStatusIndicatorCtrl();
    virtual ~CStatusIndicatorCtrl();

    // 创建控件
    BOOL Create(LPCTSTR lpszLabelText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);

    // 属性设置
    void SetActive(BOOL bActive);
    BOOL IsActive() const { return m_bIsActive; }

    void SetLabelText(LPCTSTR lpszText);
    CString GetLabelText() const { return m_strLabelText; }

    void SetActiveColor(COLORREF color);
    COLORREF GetActiveColor() const { return m_clrActive; }

    void SetInactiveColor(COLORREF color);
    COLORREF GetInactiveColor() const { return m_clrInactive; }

    void SetIndicatorSize(int nSize);
    int GetIndicatorSize() const { return m_nIndicatorSize; }

    void SetLabelFont(const LOGFONT& lf);
    void GetLabelFont(LOGFONT& lf) const;

    // 事件回调设置
    void SetStatusChangedCallback(STATUS_CHANGED_CALLBACK pCallback) { m_pStatusChangedCallback = pCallback; }

protected:
    // 成员变量
    BOOL m_bIsActive;
    CString m_strLabelText;
    COLORREF m_clrActive;
    COLORREF m_clrInactive;
    int m_nIndicatorSize;
    CFont m_fontLabel;
    LOGFONT m_lfLabel;

    CRect m_rcIndicator;    // 指示灯区域
    CRect m_rcLabel;        // 标签区域

    BOOL m_bLabelHover;     // 鼠标是否在标签上
    HCURSOR m_hCursorHand;  // 手型光标

    STATUS_CHANGED_CALLBACK m_pStatusChangedCallback;

    // GDI+ 初始化
    static ULONG_PTR s_gdiplusToken;
    static BOOL InitializeGDIPlus();
    static void ShutdownGDIPlus();

    // 绘制函数
    void DrawIndicator(Gdiplus::Graphics& graphics);
    void DrawLabel(CDC& dc);

    // 颜色处理
    Gdiplus::Color LightenColor(COLORREF color, float factor);
    Gdiplus::Color DarkenColor(COLORREF color, float factor);
    Gdiplus::Color ColorRefToGdiColor(COLORREF color, BYTE alpha = 255);

    // 消息映射
    afx_msg void OnPaint();
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    afx_msg void OnMouseLeave();
    afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    afx_msg void OnSize(UINT nType, int cx, int cy);
    DECLARE_MESSAGE_MAP()

    // 辅助函数
    void UpdateLayout();
    void FireStatusChanged(BOOL bOldStatus, BOOL bNewStatus);
    void InvalidateIndicator();
};
C++ 复制代码
#include "pch.h"
#include "StatusIndicatorCtrl.h"

IMPLEMENT_DYNAMIC(CStatusIndicatorCtrl, CWnd)

ULONG_PTR CStatusIndicatorCtrl::s_gdiplusToken = 0;

BEGIN_MESSAGE_MAP(CStatusIndicatorCtrl, CWnd)
    ON_WM_PAINT()
    ON_WM_LBUTTONUP()
    ON_WM_MOUSEMOVE()
    ON_WM_MOUSELEAVE()
    ON_WM_SETCURSOR()
    ON_WM_ERASEBKGND()
    ON_WM_SIZE()
END_MESSAGE_MAP()

CStatusIndicatorCtrl::CStatusIndicatorCtrl()
    : m_bIsActive(FALSE)
    , m_strLabelText(_T("状态"))
    , m_clrActive(RGB(50, 205, 50))      // LimeGreen
    , m_clrInactive(RGB(255, 0, 0))       // Red
    , m_nIndicatorSize(30)
    , m_bLabelHover(FALSE)
    , m_pStatusChangedCallback(NULL)
{
    InitializeGDIPlus();

    // 初始化默认字体
    memset(&m_lfLabel, 0, sizeof(LOGFONT));
    _tcscpy_s(m_lfLabel.lfFaceName, _T("微软雅黑"));
    m_lfLabel.lfHeight = -14;  // 10pt
    m_lfLabel.lfWeight = FW_BOLD;
    m_fontLabel.CreateFontIndirect(&m_lfLabel);

    m_hCursorHand = ::LoadCursor(NULL, IDC_HAND);
}

CStatusIndicatorCtrl::~CStatusIndicatorCtrl()
{
    m_fontLabel.DeleteObject();
}

BOOL CStatusIndicatorCtrl::InitializeGDIPlus()
{
    if (s_gdiplusToken == 0)
    {
        Gdiplus::GdiplusStartupInput input;
        Gdiplus::GdiplusStartupOutput output;
        Gdiplus::GdiplusStartup(&s_gdiplusToken, &input, &output);
    }
    return TRUE;
}

void CStatusIndicatorCtrl::ShutdownGDIPlus()
{
    if (s_gdiplusToken != 0)
    {
        Gdiplus::GdiplusShutdown(s_gdiplusToken);
        s_gdiplusToken = 0;
    }
}

BOOL CStatusIndicatorCtrl::Create(LPCTSTR lpszLabelText, DWORD dwStyle,
    const RECT& rect, CWnd* pParentWnd, UINT nID)
{
    m_strLabelText = lpszLabelText;

    CString strClassName = AfxRegisterWndClass(
        CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
        ::LoadCursor(NULL, IDC_ARROW),
        (HBRUSH)(COLOR_WINDOW + 1),
        NULL);

    return CWnd::Create(strClassName, _T(""), dwStyle | WS_CLIPCHILDREN,
        rect, pParentWnd, nID);
}

void CStatusIndicatorCtrl::UpdateLayout()
{
    CRect rcClient;
    GetClientRect(&rcClient);

    // 指示灯区域
    int nIndicatorTotal = m_nIndicatorSize + 10;
    m_rcIndicator.SetRect(5, 5, 5 + nIndicatorTotal, 5 + nIndicatorTotal);

    // 标签区域
    CSize szText;
    {
        CClientDC dc(this);
        CFont* pOldFont = dc.SelectObject(&m_fontLabel);
        szText = dc.GetTextExtent(m_strLabelText, m_strLabelText.GetLength());
        dc.SelectObject(pOldFont);
    }

    int nLabelX = m_rcIndicator.right + 10;
    int nLabelY = (rcClient.Height() - szText.cy) / 2;
    m_rcLabel.SetRect(nLabelX, nLabelY, nLabelX + szText.cx, nLabelY + szText.cy);

    // 设置控件最小大小
    int nMinWidth = m_rcLabel.right + 5;
    int nMinHeight = max(m_rcIndicator.Height(), m_rcLabel.Height()) + 10;

    if (rcClient.Width() < nMinWidth || rcClient.Height() < nMinHeight)
    {
        SetWindowPos(NULL, 0, 0, nMinWidth, nMinHeight,
            SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
    }
}

void CStatusIndicatorCtrl::OnSize(UINT nType, int cx, int cy)
{
    CWnd::OnSize(nType, cx, cy);
    UpdateLayout();
}

void CStatusIndicatorCtrl::OnPaint()
{
    CPaintDC dc(this);

    // 使用双缓冲
    CRect rcClient;
    GetClientRect(&rcClient);

    CDC memDC;
    CBitmap memBmp;
    memDC.CreateCompatibleDC(&dc);
    memBmp.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
    CBitmap* pOldBmp = memDC.SelectObject(&memBmp);

    // 填充背景
    memDC.FillSolidRect(&rcClient, GetSysColor(COLOR_WINDOW));

    // 使用 GDI+ 绘制指示灯
    {
        Gdiplus::Graphics graphics(memDC.GetSafeHdc());
        graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
        DrawIndicator(graphics);
    }

    // 绘制标签
    DrawLabel(memDC);

    // 复制到屏幕
    dc.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &memDC, 0, 0, SRCCOPY);
    memDC.SelectObject(pOldBmp);
}

void CStatusIndicatorCtrl::DrawIndicator(Gdiplus::Graphics& graphics)
{
    int x = m_rcIndicator.left + 5;
    int y = m_rcIndicator.top + 5;
    int size = m_nIndicatorSize;

    COLORREF baseColorRef = m_bIsActive ? m_clrActive : m_clrInactive;
    Gdiplus::Color baseColor = ColorRefToGdiColor(baseColorRef);

    // 绘制外阴影
    {
        Gdiplus::GraphicsPath shadowPath;
        shadowPath.AddEllipse(x + 2, y + 2, size, size);

        Gdiplus::PathGradientBrush shadowBrush(&shadowPath);
        Gdiplus::Color colors[] = { Gdiplus::Color(100, 0, 0, 0), Gdiplus::Color(0, 0, 0, 0) };
        int count = 1;
        shadowBrush.SetCenterColor(colors[0]);
        shadowBrush.SetSurroundColors(&colors[1], &count);
        graphics.FillPath(&shadowBrush, &shadowPath);
    }

    // 绘制主体 - 渐变效果
    {
        Gdiplus::GraphicsPath mainPath;
        mainPath.AddEllipse(x, y, size, size);

        Gdiplus::RectF rect((Gdiplus::REAL)x, (Gdiplus::REAL)y,
            (Gdiplus::REAL)size, (Gdiplus::REAL)size);
        Gdiplus::LinearGradientBrush mainBrush(rect,
            LightenColor(baseColorRef, 0.3f),
            DarkenColor(baseColorRef, 0.2f),
            Gdiplus::LinearGradientModeForwardDiagonal);

        graphics.FillPath(&mainBrush, &mainPath);
    }

    // 绘制高光效果
    {
        int highlightSize = (int)(size * 0.4);
        int highlightX = x + (int)(size * 0.25);
        int highlightY = y + (int)(size * 0.15);

        Gdiplus::GraphicsPath highlightPath;
        highlightPath.AddEllipse(highlightX, highlightY, highlightSize, highlightSize);

        Gdiplus::PathGradientBrush highlightBrush(&highlightPath);
        Gdiplus::Color colors[] = { Gdiplus::Color(200, 255, 255, 255), Gdiplus::Color(0, 255, 255, 255) };
        int count = 1;
        highlightBrush.SetCenterColor(colors[0]);
        highlightBrush.SetSurroundColors(&colors[1], &count);
        graphics.FillPath(&highlightBrush, &highlightPath);
    }

    // 绘制边框
    {
        Gdiplus::Pen borderPen(DarkenColor(baseColorRef, 0.4f), 2.0f);
        graphics.DrawEllipse(&borderPen, x, y, size, size);
    }

    // 如果激活状态,添加发光效果
    if (m_bIsActive)
    {
        Gdiplus::GraphicsPath glowPath;
        glowPath.AddEllipse(x - 3, y - 3, size + 6, size + 6);

        Gdiplus::PathGradientBrush glowBrush(&glowPath);
        Gdiplus::Color colors[] = {
            Gdiplus::Color(100, baseColor.GetR(), baseColor.GetG(), baseColor.GetB()),
            Gdiplus::Color(0, baseColor.GetR(), baseColor.GetG(), baseColor.GetB())
        };
        int count = 1;
        glowBrush.SetCenterColor(colors[0]);
        glowBrush.SetSurroundColors(&colors[1], &count);
        graphics.FillPath(&glowBrush, &glowPath);
    }
}

void CStatusIndicatorCtrl::DrawLabel(CDC& dc)
{
    CFont* pOldFont = dc.SelectObject(&m_fontLabel);

    COLORREF clrText = m_bLabelHover ? RGB(0, 0, 255) : RGB(0, 0, 0);
    dc.SetTextColor(clrText);
    dc.SetBkMode(TRANSPARENT);

    dc.DrawText(m_strLabelText, m_strLabelText.GetLength(), &m_rcLabel,
        DT_LEFT | DT_VCENTER | DT_SINGLELINE);

    // 如果鼠标悬停,添加下划线效果
    if (m_bLabelHover)
    {
        CPen pen(PS_SOLID, 1, clrText);
        CPen* pOldPen = dc.SelectObject(&pen);
        dc.MoveTo(m_rcLabel.left, m_rcLabel.bottom - 1);
        dc.LineTo(m_rcLabel.right, m_rcLabel.bottom - 1);
        dc.SelectObject(pOldPen);
    }

    dc.SelectObject(pOldFont);
}

void CStatusIndicatorCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
    if (m_rcLabel.PtInRect(point))
    {
        BOOL bOldStatus = m_bIsActive;
        SetActive(!m_bIsActive);
    }
    CWnd::OnLButtonUp(nFlags, point);
}

void CStatusIndicatorCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
    BOOL bWasHover = m_bLabelHover;
    m_bLabelHover = m_rcLabel.PtInRect(point);

    if (bWasHover != m_bLabelHover)
    {
        InvalidateRect(&m_rcLabel);

        if (m_bLabelHover)
        {
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(tme);
            tme.hwndTrack = GetSafeHwnd();
            tme.dwFlags = TME_LEAVE;
            tme.dwHoverTime = HOVER_DEFAULT;
            TrackMouseEvent(&tme);
        }
    }

    CWnd::OnMouseMove(nFlags, point);
}

void CStatusIndicatorCtrl::OnMouseLeave()
{
    if (m_bLabelHover)
    {
        m_bLabelHover = FALSE;
        InvalidateRect(&m_rcLabel);
    }
    CWnd::OnMouseLeave();
}

BOOL CStatusIndicatorCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    CPoint point;
    GetCursorPos(&point);
    ScreenToClient(&point);

    if (m_rcLabel.PtInRect(point))
    {
        ::SetCursor(m_hCursorHand);
        return TRUE;
    }

    return CWnd::OnSetCursor(pWnd, nHitTest, message);
}

BOOL CStatusIndicatorCtrl::OnEraseBkgnd(CDC* pDC)
{
    return TRUE; // 防止闪烁
}

// 属性设置实现
void CStatusIndicatorCtrl::SetActive(BOOL bActive)
{
    if (m_bIsActive != bActive)
    {
        BOOL bOldStatus = m_bIsActive;
        m_bIsActive = bActive;
        InvalidateIndicator();
        FireStatusChanged(bOldStatus, bActive);
    }
}

void CStatusIndicatorCtrl::SetLabelText(LPCTSTR lpszText)
{
    m_strLabelText = lpszText;
    UpdateLayout();
    InvalidateRect(&m_rcLabel);
}

void CStatusIndicatorCtrl::SetActiveColor(COLORREF color)
{
    m_clrActive = color;
    if (m_bIsActive)
        InvalidateIndicator();
}

void CStatusIndicatorCtrl::SetInactiveColor(COLORREF color)
{
    m_clrInactive = color;
    if (!m_bIsActive)
        InvalidateIndicator();
}

void CStatusIndicatorCtrl::SetIndicatorSize(int nSize)
{
    m_nIndicatorSize = max(20, min(100, nSize));
    UpdateLayout();
    Invalidate();
}

void CStatusIndicatorCtrl::SetLabelFont(const LOGFONT& lf)
{
    m_fontLabel.DeleteObject();
    memcpy(&m_lfLabel, &lf, sizeof(LOGFONT));
    m_fontLabel.CreateFontIndirect(&m_lfLabel);
    UpdateLayout();
    Invalidate();
}

void CStatusIndicatorCtrl::GetLabelFont(LOGFONT& lf) const
{
    memcpy(&lf, &m_lfLabel, sizeof(LOGFONT));
}

// 辅助函数
Gdiplus::Color CStatusIndicatorCtrl::ColorRefToGdiColor(COLORREF color, BYTE alpha)
{
    return Gdiplus::Color(alpha, GetRValue(color), GetGValue(color), GetBValue(color));
}

Gdiplus::Color CStatusIndicatorCtrl::LightenColor(COLORREF color, float factor)
{
    int r = min(255, (int)(GetRValue(color) + (255 - GetRValue(color)) * factor));
    int g = min(255, (int)(GetGValue(color) + (255 - GetGValue(color)) * factor));
    int b = min(255, (int)(GetBValue(color) + (255 - GetBValue(color)) * factor));
    return Gdiplus::Color(255, r, g, b);
}

Gdiplus::Color CStatusIndicatorCtrl::DarkenColor(COLORREF color, float factor)
{
    int r = (int)(GetRValue(color) * (1 - factor));
    int g = (int)(GetGValue(color) * (1 - factor));
    int b = (int)(GetBValue(color) * (1 - factor));
    return Gdiplus::Color(255, r, g, b);
}

void CStatusIndicatorCtrl::InvalidateIndicator()
{
    if (GetSafeHwnd())
    {
        InvalidateRect(&m_rcIndicator);
        UpdateWindow();
    }
}

void CStatusIndicatorCtrl::FireStatusChanged(BOOL bOldStatus, BOOL bNewStatus)
{
    CStatusChangedEventArgs args(bOldStatus, bNewStatus);

    if (m_pStatusChangedCallback)
    {
        m_pStatusChangedCallback(this, args);
    }

    // 发送 MFC 消息通知父窗口
    GetParent()->SendMessage(WM_USER + 100, (WPARAM)GetDlgCtrlID(), (LPARAM)&args);
}

测试效果:

说明:我这里使用了信号灯来显示串口的连接状态,以及通过串口获取显示下位机的状态,使用信号灯来显示。

如下操作:

串口类源代码:

c++ 复制代码
#pragma once
#include<process.h>
#include<string>
#include<sstream>
#include<iomanip>
#include<algorithm>
#include<iterator>
#include<cctype>
#include<windows.h>
using namespace std;

// 声明最大接受字符数
#define MaxReceiveChar 100
#define HighReadSpeed 1

#ifndef  UM_COMM_MSG_BASE 
#define UM_COMM_MSG_BASE  WM_USER + 617
#endif // ! UM_COMM_MSG_BASE 

// 声明消息用于串口线程和主线程之间的的通信
#ifndef  UM_COMM_RXCHAR
#define UM_COMM_RXCHAR UM_COMM_MSG_BASE + 10
#endif // ! UM_COMM_MSG_BASE 

typedef enum AxisType
{
	AXIS_XX = 0,
	AXIS_YY = 0,
	AXIS_ZZ = 0,
	AXIS_OO = 0
}AXIS_TYPE;

// 串口信息类
struct SerialPortInfo
{
	UINT nPortNr;
	DWORD dwByteRead;
};

// 串口类
class CComPort
{
public:
	CComPort();
	~CComPort();
	// 初始化串口使用波特率,串口号,奇偶校验位,数据位,停止位初始化串口
	bool InitPort(HWND hWnd,UINT nPort,UINT nBaud,char chParity,UINT nDataBit,UINT nStopBit,DWORD dwComEvent);
	// 使用DCB直接初始化串口
	bool InitPort(UINT nPort,LPDCB& lpDcb);
	// 开启串口监听线程
	bool OpenListenThread();
	// 关闭串口监听线程
	bool CloseListenThread();
	// 往串口写数据
	bool WriteData(unsigned char* pData,int nLen);
	// 从串口获取字节数
	UINT GetByteInCOM();
	// 从串口读取字节数
	bool ReadData(unsigned char& nRecd);

	void GetByteData(CByteArray& arr);

public:
	// 串口号
	UINT m_nPortNr;
	// 主窗口句柄
	HWND m_hOwnerWnd;

private:
	// 打开串口
	bool OpenPort(UINT nPort);
	// 关闭串口
	bool ClosePort();
	// 串口监听线程入口函数
	static UINT WINAPI ListenThread(void* lpParam);

private:
	// 串口句柄
	HANDLE m_hComm;
	// 串口监听线程是否退出
	static bool s_bExit;
	// 串口监听线程句柄
	HANDLE m_hListenThread;
	// 临界区,用于保护串口的读取和写入操作
	CRITICAL_SECTION m_csComminicationSync;

	CByteArray m_arrByte;
};
c++ 复制代码
#include "pch.h"
#include "CNewComPort.h"

bool CComPort::s_bExit = false;
const int SLEEP_TIME_VALUE = 5;

// 构造函数初始化
CComPort::CComPort()
{
	m_hComm = NULL;
	m_hListenThread = INVALID_HANDLE_VALUE;
	InitializeCriticalSection(&m_csComminicationSync);
}

CComPort::~CComPort()
{
	CloseListenThread();
	ClosePort();
	DeleteCriticalSection(&m_csComminicationSync);
}

// 初始化串口函数
bool CComPort::InitPort(HWND hWnd, UINT nPort, UINT nBaud, char chParity, UINT nDataBit, UINT nStopBit, DWORD dwComEvent)
{
	m_hOwnerWnd = hWnd;
	m_nPortNr = nPort;

	// 临时变量,将定制化参数转化为字符串形式,以构造DCB结构
	char szDCBData[50];
	sprintf_s(szDCBData,"Baud = %d,Parity = %c,DataBit = %d,StopBit = %d",nBaud, chParity, nDataBit, nStopBit);

	// 打开指定串口,该函数内部已有临界区保护,前面不加保护
	if (!OpenPort(nPort))
	{
		return false;
	}

	// 进入临界区
	EnterCriticalSection(&m_csComminicationSync);

	// 是否发生错误
	bool bSuccess = true;

	// 设置串口的超时时间,设置为0,表示没有超时限制
	COMMTIMEOUTS commtimeout;
	commtimeout.ReadIntervalTimeout = 0;
	commtimeout.ReadTotalTimeoutConstant = 0;
	commtimeout.ReadTotalTimeoutMultiplier = 0;
	commtimeout.WriteTotalTimeoutConstant = 0;
	commtimeout.WriteTotalTimeoutMultiplier = 0;

	if (bSuccess)
	{
		bSuccess = SetCommTimeouts(m_hComm,&commtimeout);
	}

	DCB dcb;
	if (bSuccess)
	{
		// 获取当前串口配置参数,并且构造DCB
		bSuccess = GetCommState(m_hComm,&dcb);
		if (bSuccess)
			bSuccess = BuildCommDCBA(szDCBData,&dcb);
		// 开启RTS flow控制
		dcb.fRtsControl = RTS_CONTROL_ENABLE;
	}

	if (bSuccess)
	{
		// 使用DCB配置串口状态
		bSuccess = SetCommState(m_hComm,&dcb);
	}
	// 清空缓冲区
	PurgeComm(m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);

    // 离开临界区
	LeaveCriticalSection(&m_csComminicationSync);

	return bSuccess = true;
}


// 初始化串口函数
bool CComPort::InitPort(UINT nPort, LPDCB& lpDcb)
{
	m_nPortNr = nPort;

	// 打开指定串口,该函数内部已有临界区保护,前面不加保护
	if (!OpenPort(nPort))
	{
		return false;
	}

	// 进入临界区
	EnterCriticalSection(&m_csComminicationSync);

	// 配置串口参数
	if (!SetCommState(m_hComm, lpDcb))
	{
		return false;
	}
	// 清空串口缓冲区
	PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

	// 离开临界区
	LeaveCriticalSection(&m_csComminicationSync);

	return true;

}
// 开启串口监听线程
bool CComPort::OpenListenThread()
{
	// 检测线程是否开启
	if (m_hListenThread != INVALID_HANDLE_VALUE)
	{
		return false;
	}

	s_bExit = false;
	// 线程ID
	UINT nThreadId;
	// 启动串口监听线程
	m_hListenThread = (HANDLE)_beginthreadex(NULL, 0, ListenThread, this, 0, &nThreadId);

	if (!m_hListenThread)
	{
		return false;
	}

	// 设置线程的优先级
	if (!SetThreadPriority(m_hListenThread, THREAD_PRIORITY_ABOVE_NORMAL))
	{
		return false;
	}

	return true;
}

// 关闭串口监听线程
bool CComPort::CloseListenThread()
{
	if (m_hListenThread != INVALID_HANDLE_VALUE)
	{
		// 通知线程退出
		s_bExit = true;
		// 等待线程退出
		Sleep(10);
		
		CloseHandle(m_hListenThread);
		m_hListenThread = INVALID_HANDLE_VALUE;
	}
	return true;
}

// 向串口写数据,将缓冲区中的数据写入到串口中
bool CComPort::WriteData(unsigned char* pData, int nLen)
{
	bool bResult = false;
	DWORD dwByteSend = 0;
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		return false;
	}
	// 进入临界区
	EnterCriticalSection(&m_csComminicationSync);
	// 向缓冲区写入指定量的数据
	bResult = WriteFile(m_hComm, pData,nLen,&dwByteSend,NULL);

	// 数据写入失败
	if (!bResult)
	{
		DWORD dwError = GetLastError();
		PurgeComm(m_hComm, PURGE_TXCLEAR| PURGE_TXABORT);
		LeaveCriticalSection(&m_csComminicationSync);
		return false;
	}
	// 离开临界区
	LeaveCriticalSection(&m_csComminicationSync);
	return false;
}


// 获取串口缓冲区中的字节数
UINT CComPort::GetByteInCOM()
{
	char RxBuffer[100];
	DWORD dwErrors, dwByteRead;
	COMSTAT comstat;
	UINT nByteInQue = 0;
	while(!s_bExit)
	{
		ClearCommError(m_hComm,&dwErrors,&comstat);
		if (comstat.cbInQue > 0)
		{
			Sleep(1);
			if (nByteInQue == comstat.cbInQue)  // 读取的字节数没有增加,说明这次接受读取完成
			{
				return nByteInQue;
			}
			nByteInQue = comstat.cbInQue;
		}
	}
	return nByteInQue;
}

// 读取串口接收缓冲区的一个字节
bool CComPort::ReadData(unsigned char& nRecd)
{
	bool bResult = true;
	DWORD dwByteRead = 0;
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		return false;
	}
	// 进入临界区
	EnterCriticalSection(&m_csComminicationSync);
	// 从缓冲区读取一个字节的数据
	bResult = ReadFile(m_hComm,&nRecd,1,&dwByteRead,NULL);
	// 读取错误
	if (!bResult)
	{
		DWORD dwError = GetLastError();
		// 清空缓冲区
		PurgeComm(m_hComm, PURGE_RXABORT|PURGE_RXCLEAR);
		LeaveCriticalSection(&m_csComminicationSync);
		return false;
	}
	// 离开临界区
	LeaveCriticalSection(&m_csComminicationSync);
	return true;

}

// 打开串口
bool CComPort::OpenPort(UINT nPort)
{
	// 初始化临界区
	InitializeCriticalSection(&m_csComminicationSync);
	// 进入临界区
	EnterCriticalSection(&m_csComminicationSync);
	// 判断串口是否已经打开,若打开就关闭
	if (m_hComm != NULL)
	{
		CloseHandle(m_hComm);
		m_hComm = NULL;
	}
	// 把串口的编号转化为设备名
	TCHAR szPort[50];

	swprintf_s(szPort,_T("COM%d"),nPort);

	// 打开指定的串口
	m_hComm = CreateFileW(szPort,             // 设备名COM1,COM2等
		GENERIC_READ | GENERIC_WRITE,         // 访问模式,可同时读写
		0,                                    // 共享模式0表示不共享
		NULL,                                 // 安全性设置,一般为NULL
		OPEN_EXISTING,                        // 该参数表示设备必须存在,否则创建失败
		FILE_ATTRIBUTE_NORMAL, 
		NULL);
	// 如果创建失败,释放资源并且返回
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		LeaveCriticalSection(&m_csComminicationSync);
		return false;
	}
	// 创建成功并且退出
	LeaveCriticalSection(&m_csComminicationSync);
	return true;
}

// 关闭串口
bool CComPort::ClosePort()
{
	// 如果串口被打开,就关闭
	if (m_hComm != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hComm);
		m_hComm = INVALID_HANDLE_VALUE;
	}
	return false;
}

// 串口监听线程入口函数
UINT WINAPI CComPort::ListenThread(void* lpParam)
{
	CComPort* pComPort = reinterpret_cast<CComPort*>(lpParam);

	// 线程循环,轮询方式读取串口数据
	while (!pComPort->s_bExit)
	{
		UINT nByteRead = pComPort->GetByteInCOM();
	    // 如果串口缓冲区中没有数据,则过一会查询
		if (nByteRead == 0)
		{
			Sleep(SLEEP_TIME_VALUE);
			continue;
		}


		// 读取输入缓冲区中的数据,并且显示
		unsigned char chReceiveChar[MaxReceiveChar] = {0};
		unsigned char chRecv = 0x00;
		int i = 0;
		int j = nByteRead;

		SerialPortInfo SerialInfo;
		SerialInfo.nPortNr = pComPort->m_nPortNr;
		SerialInfo.dwByteRead = nByteRead;
		do
		{
			chRecv = 0x00;
			if (pComPort->ReadData(chRecv) == true)
			{
				chReceiveChar[i++] = chRecv;
				continue;
			}
		} while (--j);

		pComPort->m_arrByte.RemoveAll();
		for (int i=0;i< nByteRead;i++)
		{
			pComPort->m_arrByte.Add(chReceiveChar[i]);
		}
		
		::SendMessage(pComPort->m_hOwnerWnd,UM_COMM_RXCHAR,(WPARAM)chReceiveChar,(LPARAM)&SerialInfo);
	}
	return 0;
}

void CComPort::GetByteData(CByteArray& arr)
{
	arr.Copy(m_arrByte);
}
相关推荐
liulilittle2 小时前
C++实现广播地址计算
开发语言·c++
载数而行52010 小时前
QT的五类布局
c++·qt·学习
故事和你9110 小时前
sdut-程序设计基础Ⅰ-实验五一维数组(8-13)
开发语言·数据结构·c++·算法·蓝桥杯·图论·类和对象
载数而行52010 小时前
QT的QString类
c++·qt·学习
bu_shuo11 小时前
Visual C++2010学习版(全国计算机等级二级考试版)安装记录
c++·cpp·visual c++·计算机二级
Titan202414 小时前
Linux环境变量个人笔记
linux·服务器·c++
记忆多14 小时前
c++名字空间 函数模版 左右值
开发语言·c++·算法
2401_8898846615 小时前
高性能计算通信库
开发语言·c++·算法
肆忆_16 小时前
# cilly-vm-cpp 重构复盘(第 1 阶段:SRP)
c++