C#调用C++函数来与串口通信

xiaoxiao2021-02-28  37

前些日子帮朋友写个小软件,要求用C#来实现主程序,主要的功能是与一些通信设备打交道,当然就是通过串口了,以十进制发送和读取串口

的数据,考虑到C#调用API并没有C++来得方便,因此,我用C++封装了一个读写串口的DLL,只提供一个函数供外部调用,这样的好处在于,C#

只要调用这个函数发送完数据后,函数立即就能获得串口返回的数据。另一个好处在于,一些不熟悉C++的朋友,也能够直接通过这个DLL来对

串口做一些操作。

   杂话就不多讲了,直接贴这个读写串口的dll代码:

 一. C++部分:  1)头文件:   // SerialPortSync.h: interface for the CSerialPortSync class.////

#if !defined(AFX_SERIALPORTSYNC_H__7FC698BB_BF4D_449E_8DE9_62B8876187CF__INCLUDED_)#define AFX_SERIALPORTSYNC_H__7FC698BB_BF4D_449E_8DE9_62B8876187CF__INCLUDED_

#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000

class CSerialPortSync  {public: CSerialPortSync();public: bool Open(int nPort, int nBaud,int nDatabit, int nStopbit, int nParity, int nTimeOut = 500); DWORD SendData(const char *buffer, const unsigned int writebytes, char *RecBuffer, int nSendType = 1); void Close();private: HANDLE m_hCom; //串口句柄 bool m_bOpened;

 char ConvertHexChar(char ch); int String2Hex(const char *str, const unsigned int nLen, byte *senddata);};

#endif // !defined(AFX_SERIALPORTSYNC_H__7FC698BB_BF4D_449E_8DE9_62B8876187CF__INCLUDED_)

 

 2). CPP文件:

// SerialPortSync.cpp: implementation of the CSerialPortSync class.////

#include "stdafx.h"//#include "SerialPortDemo.h"#include "SerialPortSync.h"

#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif

//// Construction/Destruction//#define MAXSENDLENGTH 20 #define MAXRECEIVELENGTH 20

CSerialPortSync::CSerialPortSync(){ m_bOpened = false;}

/*******************************************************************************函数功能:打开串口,设置串口参数*参数说明:    nCom:操作的串口值,如COM1:,COM2:等等    lnBaudrate: 波特率    nStopbits: 停止位    nDatabits: 数据位    nParity:奇偶校验*返回值: 返回串口的句柄*时间:2008/10/22*作者:XiangDing*****************************************************************************/bool CSerialPortSync::Open(int nPort, int nBaud,int nDatabit,int nStopbit,int nParity, int nTimeOut){ if( m_bOpened ) return( true );

 char strPort[10]={0}; sprintf(strPort,"COM%d",nPort);

 m_hCom=CreateFile(strPort, GENERIC_READ|GENERIC_WRITE, 0, NULL ,OPEN_EXISTING, 0,NULL); if ((m_hCom==INVALID_HANDLE_VALUE) || (m_hCom==NULL )) {  m_bOpened = false;  return false; }

    COMMTIMEOUTS ct;    ct.ReadIntervalTimeout         = MAXDWORD;                                  //设置超时设置    ct.ReadTotalTimeoutMultiplier  = 0;    ct.ReadTotalTimeoutConstant    = nTimeOut;    ct.WriteTotalTimeoutMultiplier = 0;    ct.WriteTotalTimeoutConstant   = nTimeOut;  SetCommTimeouts( m_hCom, &ct );

 DCB dcb; GetCommState( m_hCom, &dcb );    dcb.BaudRate           = nBaud;    dcb.StopBits           = nStopbit;    dcb.Parity             = nParity;    dcb.ByteSize           = (BYTE)nDatabit;       // number of bits/byte, 4-8

 BOOL bl = SetCommState( m_hCom, &dcb );

 m_bOpened = TRUE;  return true;}

// nSendType 1: 以十六进制发送.  0: 直接发送字符串//返回值是已接收的个数//返回 -1: 写串口失败. -2:清除串口错误;  -3: 串口返回数据为0;DWORD CSerialPortSync::SendData(const char *sendBuffer, const unsigned int writebytes, char *RecBuffer, int nSendType){

 if( !m_bOpened ) return 0;     DWORD dwWritten = 0;    DWORD dwError; DWORD dwBytesRead = 0;

 if (nSendType == 1) {  byte bHexData[MAXSENDLENGTH] = {0};  memset(bHexData, 0, MAXSENDLENGTH);

  int len = String2Hex(sendBuffer, writebytes, bHexData);  BOOL bWriteRet = FALSE;        bWriteRet = WriteFile(m_hCom, bHexData, len, &dwWritten, NULL);    BOOL bReadStatus;  BYTE bReadBuf[MAXRECEIVELENGTH] = {0};

  bReadStatus = ReadFile( m_hCom, bReadBuf, MAXRECEIVELENGTH, &dwBytesRead, NULL);    if (dwBytesRead <1 ) return dwBytesRead;

  CString strBuf;  CString strTemp;  for(int i=0; i<dwBytesRead; i++ )  {   strTemp.Format("X", bReadBuf[i]);   strBuf += strTemp;  }  strBuf.TrimRight();  strncpy(RecBuffer, (LPCTSTR)strBuf, dwBytesRead * 2 + 1);

  return dwBytesRead; } return dwBytesRead;}

void CSerialPortSync::Close(){                                                                                   if(m_hCom != INVALID_HANDLE_VALUE)    {        CloseHandle(m_hCom);   m_hCom = INVALID_HANDLE_VALUE;    }

 if( m_bOpened ) m_bOpened = false;}

//由于这个转换函数的格式限制,在发送框中的十六制字符应该每两个字符之间插入一个空隔//如:A1 23 45 0B 00 29int CSerialPortSync::String2Hex(const char *str, const unsigned int nLen, byte *senddata){ int hexdata,lowhexdata; int hexdatalen=0; int len=nLen; for(int i=0;i<len;) {  char lstr,hstr=str[i];  if(hstr==' ')  {   i++;   continue;  }  i++;  if(i>=len)   break;  lstr=str[i];  hexdata=ConvertHexChar(hstr);  lowhexdata=ConvertHexChar(lstr);  if((hexdata==16)||(lowhexdata==16))   break;  else    hexdata=hexdata*16+lowhexdata;  i++;  senddata[hexdatalen]=(char)hexdata;  hexdatalen++; }// senddata.SetSize(hexdatalen); return hexdatalen;}

//这是一个将字符转换为相应的十六进制值的函数//功能:若是在0-F之间的字符,则转换为相应的十六进制字符,否则返回-1char CSerialPortSync::ConvertHexChar(char ch) { if((ch>='0')&&(ch<='9'))  return ch-0x30; else if((ch>='A')&&(ch<='F'))  return ch-'A'+10; else if((ch>='a')&&(ch<='f'))  return ch-'a'+10; else   return (-1);}

3) DLL导出函数实现:/*返回值: -9: 打开串口失败。 -1: 往串口写数据失败。 -2: 清除串口错误失败。 -3: 串口返回数据为0。  正值: 返回正常。*/

SERIALPORT_DLL int __stdcall SendData(int nPort, int nBaud,int nDatabit,int nStopbit,  int nParity, const char *sendBuffer, int writebytes,  char *RecBuffer, int nSendType, int nTimeOut)

{

 CSerialPortSync sPort; if (!sPort.Open(nPort,nBaud,nDatabit,nStopbit,nParity)) {  return -9; }  int nReadCount = sPort.SendData(sendBuffer, writebytes, RecBuffer);  sPort.Close();

 return nReadCount;}

 4). 我为什么要用类来实现C++的串口读写呢,主要也是方便C++开发人员可以直接使用该类,而C#的开发人员,直可以通过上面第三步,导出

到dll中,在C#中直接调用。

二. C#调用的代码就更简单啦,像平常调API函数一样,用DllImport声明一下即可。这时就不多讲了。

本人一直从事mobile/wince/linux平台下开发,使用C++虽有多年,但觉得自已对很多底层细节技术理解仍不够深刻,希望有机会得到高手指点

转载请注明原文地址: https://www.6miu.com/read-2619219.html

最新回复(0)