1. 程式人生 > >C#呼叫C++函式來與串列埠通訊

C#呼叫C++函式來與串列埠通訊

前些日子幫朋友寫個小軟體,要求用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_FILE
static 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("%02X", 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 29
int 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之間的字元,則轉換為相應的十六進位制字元,否則返回-1
char 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++雖有多年,但覺得自已對很多底層細節技術理解仍不夠深刻,希望有機會得到高手指點