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

2023-05-16

前些日子帮朋友写个小软件,要求用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++虽有多年,但觉得自已对很多底层细节技术理解仍不够深刻,希望有机会得到高手指点

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C#调用C++函数来与串口通信 的相关文章

  • C语言 内存分配 地址 指针 数组 参数 解析

    指针简介 指针式保存变量地址 的变量 增加阅读难度 指针 和 goto 语句会增加程序的理解难度 容易出现错误 ANSI C American National Standards Institute 美国国家标准学会 即标准C 通用指针类
  • C++数组与指针概念

    指向数组元素的指针 一个变量有地址 xff0c 一个数组包含若干元素 xff0c 每个数组元素都在内存中占用存储单元 xff0c 它们都有相应的地址 指针变量既然可以指向变量 xff0c 当然也可以指向数组元素 xff08 把某一元素的地址
  • [代码实例][C]Linux实现线程池

    ThreadPool h span class token macro property span class token directive keyword ifndef span THREADPOOL H span span class
  • C语言中指针的初始化和赋值

    1 指针的初始化 指针初始化时 xff0c 61 的右操作数必须为内存中数据的地址 xff0c 不可以是变量 xff0c 也不可以直接用整型地址值 但是int p 61 0 除外 xff0c 该语句表示指针为空 此时 xff0c p只是表示
  • Aspose.Cells使用总结大全

    使用到 Aspose Cells 插件 xff0c 整理一下 一 xff1a 新建解决方案 xff0c 目录如下 目录说明 xff1a Program cs 入口类 ExcelGenerator cs Aspose Cells 操作类 As
  • C#图像处理基础概念知识

    图像是人类获取和交换信息的主要来源 xff0c 因此 xff0c 图像处理的应用领域必然涉及到人类生活和工作的方方面面 随着人类活动范围的不断扩大 xff0c 图像处理的应用领域也将随之不断扩大 xff08 1 xff09 航天和航空技术方
  • c#中字节数组byte[]、图片image、流stream,字符串string、内存流MemoryStream、文件file,之间的转换

    字节数组byte 与图片image之间的转化 字节数组转换成图片 span class hljs keyword style color 0088 public span span class hljs keyword style colo
  • C/C++ 文件操作之CreateFile、ReadFile和WriteFile

    1 CreateFile 这个函数的功能是创建或者打开一个文件或者I O设备 xff0c 通常使用的I O形式有文件 文件流 目录 物理磁盘 卷 终端流等 如执行成功 xff0c 则返回文件句柄 INVALID HANDLE VALUE 表
  • String到底是值类型还是引用类型(C#)

    MSDN 中明确指出 String 是引用类型而不是值类型 xff0c 但 String 表面上用起来却像是值类型 xff0c 这又是什么原因呢 xff1f 首先从下面这个例子入手 xff1a span class hljs comment
  • VS2013 MFC基于对话框编程(创建工程)

    一 新建MFC项目 选择 xff1a 基于对话框MFC的使用 xff1a 在共享DLL中使用MFC xff08 程序运行需要dll xff09 在静态库中使用MFC xff08 程序较大 xff0c 运行时不需要dll xff09 设置MF
  • MFC中画直线和曲线的几种方法

    一 画直线 要想在MFC中画出有颜色的线条 xff0c 首先就要设置DC的画笔 xff0c 我们可以按如下方法来设置画笔 xff1a 第一步 xff1a 在View类中添加一个COLORREF类型的数据成员m Color xff08 用来保
  • C++函数的传入参数是指针的指针(**)的详解

    要修改变量的值 xff0c 需要使用变量类型的指针作为参数或者变量的引用 如果变量是一般类型的变量 xff0c 例如int xff0c 则需要使用int 类型的指针类型int 作为参数或者int的引用类型int amp 但是如果变量类型是指
  • 零基础学习WinCE开发

    在接触WinCE时候的基础 xff1a 软件语言基础 xff1a C C 43 43 C 我不是计算机专业的 xff0c 所以这些语言基础也是比较业余的 经验不丰富 xff0c 遇到问题就是查MSDN或者到网上查找相关解决方案即可 xff0
  • SD-WAN为什么备受欢迎?

    SD WAN即软件定义广域网 xff0c 通过集中控制器 xff0c 将广阔地理范围内的计算机 云服务 数据中心集中起来统一管理 比较多地用于企业组网场景 xff0c 那么为什SD WAN如此受欢迎呢 xff1f 近年来 xff0c 企业的
  • C#实现缩放和剪裁图片的方法示例

    C 实现缩放和剪裁图片的方法 分享给大家供大家参考 xff0c 具体如下 xff1a 1 2 3 4 5 6 7 8 9 10 11
  • 虚拟内存解疑

    虚拟内存别称 虚拟存储器 xff08 Virtual Memory xff09 电脑 中所运行的 程序均需经由 内存执行 xff0c 若执行的程序占用内存很大或很多 xff0c 则会导致内存消耗殆尽 为解决该问题 xff0c Windows
  • windows内存结构概述

    13 1 Windows的虚拟地址空间安排 13 1 1虚拟地址空间的分区 xff08 即虚拟地址空间布局 xff09 进程的地址空间划分 分区 x86 32位 Windows 3GB用户模式下的x86 32位Windows X64 64位
  • 变量名和内存地址及符号表

    1 变量名是给编译器看的 xff0c 编译器根据变量是局部还是全局分配内存地址或栈空间 xff0c 所谓的变量名在内存中不存在 xff0c 操作时转换成地址数存放在寄存器中了 其实可以理解为是符号表起到了连接作用 2 符号表 xff08 此
  • C/C++编译和链接过程详解 概述 (重定向表,导出符号表,未解决符号表)

    详解link 有 些人写C C 43 43 以下假定为C 43 43 程序 xff0c 对unresolved external link或者duplicated external simbol的错误信息不知所措 xff08 因为这样的错误
  • 编译器构造概述(详细)

    一 编译器简介 前面谈到静态链接器构造的基本流程 xff0c 最后提到所构造的链接器若要能正常工作的前提是需要构造一个能生成符合链接器输入文件格式的编译器 xff0c 本文构造一个符合这种具体格式要求编译器 但是编译器的直接编译的结果一般是

随机推荐

  • nm命令中符号类型详解

    nm命令介绍的很多 xff0c 但大多不介绍其函数符号标志的含义 最近在调试动态库时常用到 xff0c 其中用的最多的用法 nm A grep aaa c 43 43 filt A 为了显示文件 xff0c c 43 43 filt转换为可
  • 电脑怎样执行编程语言的?

    链接 xff1a https www zhihu com question 29227521 answer 154819061 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xff0c 非商业转载请注明出处 这个问题
  • 汇编中的标号概念

    地址标号和数据标号 地址标号 1 assume cs code 2 code segment 3 a db 1 2 3 4 5 6 7 8 4 b dw 0 5 start mov si offset a 6 mov bx offset b
  • 汇编语言不带冒号标号的用法

    汇编语言中 xff0c 有一种编程方法 xff1a 直接定址表 这种方法和高级语言 xff0c C语言中的数组有类似的思想 xff0c 就是将参数编排在一起 xff0c 然后通过数组名的调用 xff0c 得到需要的数值 汇编语言中 xff0
  • 实战录 | 基于openflow协议的抓包分析

    实战录 导语 云端卫士 实战录 栏目定期会向粉丝朋友们分享一些在开发运维中的经验和技巧 xff0c 希望对于关注我们的朋友有所裨益 本期分享人为云端卫士安全SDN工程师宋飞虎 xff0c 将带来基于openflow协议的抓包分析 一 什么是
  • 汇编中的标号

    当程序中要跳转到另一位置时 xff0c 需要有一个标识来指示新的位置 xff0c 这就是标号 xff0c 通过在目标地址的前面放上一个标号 xff0c 可以在指令中使用标号来代替直接使用地址 使用变量是任何编程语言都要遇到的工作 变量是计算
  • win32常用的汇编指令和寄存器

    通用寄存器 EAX 累加 Accumulator 寄存器 AX AH AL 常用于乘 除法和函数返回值 EBX 基址 Base 寄存器 BX BH BL 常做内存数据的指针 或者说常以它为基址来访问内存 ECX 计数器 Counter 寄存
  • win32汇编寄存器汇总

    32位CPU所含有的寄存器有 xff1a 4个数据寄存器 EAX EBX ECX和EDX 2个变址和指针寄存器 ESI和EDI 2个指针寄存器 ESP和EBP 6个段寄存器 ES CS SS DS FS和GS 1个指令指针寄存器 EIP 1
  • 汇编指令ebp与esp的关系与作用

    可以看到 xff0c 初始情况下 xff0c ebp此时值为0012FEDC xff0c 也就是栈帧的地址 xff0c 而栈顶地址esp值为0012FDFC 可以看到两个值有一定的关系 而 帧指针 的地址较高 然后我们让它执行前两句 xff
  • win32 汇编基础概念整理

    一 关于寄存器 寄存器有EAX EBX ECX EDX EDI ESI ESP EBP等 xff0c 似乎IP也是寄存器 xff0c 但只有在CALL RET在中会默认使用它 xff0c 其它情况很少使用到 xff0c 暂时可以不用理会 E
  • 汇编中的寻址方式

    存储器 存储器 xff08 Memory xff09 是现代信息技术中用于保存信息的记忆设备 其概念很广 xff0c 有很多层次 xff0c 在数字系统中 xff0c 只要能保存二进制数据的都可以是存储器 xff1b 在集成电路中 xff0
  • x86的32位汇编快速入门

    本文描述基本的32位X86汇编语言的一个子集 xff0c 其中涉及汇编语言的最核心部分 xff0c 包括寄存器结构 xff0c 数据表示 xff0c 基本的操作指令 xff08 包括数据传送指令 逻辑计算指令 算数运算指令 xff09 xf
  • 80x86汇编指令详解

    80x86指令系统 xff0c 指令按功能可分为以下七个部分 1 数据传送指令 2 算术运算指令 3 逻辑运算指令 4 串操作指令 5 控制转移指令 6 处理器控制指令 7 保护方式指令 3 3 1数据传送指令 数据传送指令包括 xff1a
  • 汇编中addr和offset

    汇编中addr和offset的异同点 xff01 xff01 xff01 一 相同点 1 addr 和 offset 操作符都是获得操作数的偏移地址 xff1b 2 addr 和 offset 的处理都是先检查处理的是全局还是局部变量 xf
  • 汇编语言,数据段中,标号前面加不加offset有什么区别?

    data segment string db 40h dup 0 string 1 db 39 Input characters 39 0dh 39 39 data ends 那么 xff0c mov bx string 和 mov bx
  • SD-WAN如何推动企业上云

    企业上云是指企业将其业务系统 应用程序 数据等资源迁移到云端 xff08 云服务提供商的数据中心 xff09 xff0c 以实现更高效 灵活 安全 可靠的信息化运营和服务 麦肯锡公司发布了名为 企业上云2 0 xff1a 加速数字化转型 x
  • nvm常用命令,配置镜像

    1 配置镜像 阿里云 nvm npm mirror https npmmirror com mirrors npm nvm node mirror https npmmirror com mirrors node 腾讯云 nvm npm m
  • 汇编语言中MOV和OFFSET指令的两个问题?

    xff08 1 xff09 往段寄存器送段地址时要写成 MOV AX DATA MOV DS AX 不能写成 MOV DS DATA 这是为什么 xff1f 有人说由于段寄存只能进行16位的读写 xff0c 因此需要用ax来倒一下 xff0
  • C++动态链接库的制作

    输入函式 declspec dllimport 与输出函式 declspec dllexport 有什么区别呢 xff1f 我知道他们不同 xff0c 但差别在哪呢 我用的全是 declspec dllexport xff0c declsp
  • C#调用C++函数来与串口通信

    前些日子帮朋友写个小软件 xff0c 要求用C 来实现主程序 xff0c 主要的功能是与一些通信设备打交道 xff0c 当然就是通过串口了 xff0c 以十进制发送和读取串口 的数据 xff0c 考虑到C 调用API并没有C 43 43 来