VC串口通讯DLL
- 格式:doc
- 大小:130.00 KB
- 文档页数:36
visual c++串口通信与工程应用实践Visual C++是一款强大的集成开发环境,可以用于开发串口通信和工程应用。
它提供了一套丰富的工具和函数库,可以方便地进行串口通信和工程应用的开发。
需要掌握相关的编程技巧和串口通信协议知识,才能更好地实践和应用。
下面是一些实践的步骤:1. 配置串口:在Visual C++中,可以使用WinAPI函数SetCommState和GetCommState来配置串口的波特率、数据位、停止位等参数。
需要定义一个DCB结构体来保存串口的配置信息,然后使用GetCommState获取当前串口的配置信息,修改对应的参数,最后使用SetCommState来设置新的配置。
2. 打开串口:使用WinAPI函数CreateFile来打开串口。
需要提供串口的名称(如"COM1")、访问权限和共享模式等参数。
打开成功后,会返回一个句柄,可以用于后续的读写操作。
3. 读取串口数据:使用ReadFile函数从串口中读取数据。
可以设置一个缓冲区来保存读取的数据,并指定读取的字节数。
ReadFile函数会阻塞,直到有足够的数据可供读取。
4. 写入串口数据:使用WriteFile函数将数据写入串口。
可以将要发送的数据保存到一个缓冲区,然后指定要发送的字节数。
WriteFile函数会阻塞,直到所有数据都被发送。
5. 关闭串口:使用CloseHandle函数关闭串口句柄。
以上是串口通信的基本步骤,可以根据具体的应用需求和功能进行扩展。
在工程应用中,可以根据串口通信的需求来设计和开发相应的功能模块,例如数据采集、控制命令发送等。
可以使用Visual C++的其他功能,如图形界面设计和多线程编程,来增强应用的用户体验和性能。
c++ dll之间传递结构C++ DLL(动态链接库)是一种可包含函数、数据和资源的二进制文件格式,可以供其他程序在运行时动态加载和调用。
在DLL之间传递结构是一种常见的需求,可以通过以下几种方式实现。
1. 使用结构指针传递这是一种简单且常见的方式,即在DLL函数之间传递结构指针。
在调用方定义一个结构,并将其地址传递给DLL函数。
DLL函数可以通过指针访问和修改结构的成员。
示例:调用方(DLL1)定义结构:```typedef struct {int value;char name[20];} MyStruct;extern "C" __declspec(dllexport) void DLLFunction(MyStruct* pStruct);```DLL函数(DLL2)接收结构指针:```extern "C" __declspec(dllexport) void DLLFunction(MyStruct* pStruct) {// 访问结构成员pStruct->value = 10;strcpy(pStruct->name, "example");}```调用方调用DLL函数并传递结构指针:```MyStruct myStruct;DLLFunction(&myStruct);```这种方法的优点是简单明了,但需要调用方和DLL之间共享结构定义。
2. 使用二进制流传递如果结构较为复杂或者需要传递的数据量较大,可以使用二进制流在DLL之间传递结构。
调用方将结构序列化为二进制流,并将其传递给DLL函数。
DLL函数可以解析二进制流并还原成结构。
示例:调用方(DLL1)将结构序列化为二进制流:```MyStruct myStruct = {10, "example"};// 将结构序列化为二进制流std::ostringstream oss;oss.write(reinterpret_cast<char*>(&myStruct), sizeof(myStruct)); std::string data = oss.str();// 将二进制流传递给DLL函数DLLFunction(data.c_str(), data.size());```DLL函数(DLL2)解析二进制流:```extern "C" __declspec(dllexport) void DLLFunction(const char* data, size_t size) {assert(size == sizeof(MyStruct));// 解析二进制流并还原结构MyStruct myStruct;memcpy(&myStruct, data, sizeof(myStruct));// 访问结构成员myStruct.value = 20;strcpy(, "example2");}```这种方法可以灵活地传递结构,但需要在调用方和DLL之间定义结构的布局和大小。
在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信。
串口通信方便易行,应用广泛。
一般情况下,工控机和各智能仪表通过RS485总线进行通信。
RS485的通信方式是半双工的,只能由作为主节点的工控PC机依次轮询网络上的各智能控制单元子节点。
每次通信都是由PC机通过串口向智能控制单元发布命令,智能控制单元在接收到正确的命令后作出应答。
在Win32下,可以使用两种编程方式实现串口通信,其一是使用ActiveX控件,这种方法程序简单,但欠灵活。
其二是调用Windows的API函数,这种方法可以清楚地掌握串口通信的机制,并且自由灵活。
本文我们只介绍API串口通信部分。
串口的操作可以有两种操作方式:同步操作方式和重叠操作方式(又称为异步操作方式)。
同步操作时,API函数会阻塞直到操作完成以后才能返回(在多线程方式中,虽然不会阻塞主线程,但是仍然会阻塞监听线程);而重叠操作方式,API函数会立即返回,操作在后台进行,避免线程的阻塞。
无论那种操作方式,一般都通过四个步骤来完成:(1)打开串口(2)配置串口(3)读写串口(4)关闭串口(1)打开串口Win32系统把文件的概念进行了扩展。
无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。
该函数的原型为:HANDLE CreateFile( LPCTSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDistribution,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile);•lpFileName:将要打开的串口逻辑名,如“COM1”;•dwDesiredAccess:指定串口访问的类型,可以是读取、写入或二者并列;•dwShareMode:指定共享属性,由于串口不能共享,该参数必须置为0;•lpSecurityAttributes:引用安全性属性结构,缺省值为NULL;•dwCreationDistribution:创建标志,对串口操作该参数必须置为OPEN_EXISTING;•dwFlagsAndAttributes:属性描述,用于指定该串口是否进行异步操作,该值为FILE_FLAG_OVERLAPPED,表示使用异步的I/O;该值为0,表示同步I/O操作;•hTemplateFile:对串口而言该参数必须置为NULL;同步I/O方式打开串口的示例代码:HANDLE hCom; //全局变量,串口句柄hCom=CreateFile("COM1",//COM1口GENERIC_READ|GENERIC_WRITE, //允许读和写0, //独占方式NULL,OPEN_EXISTING, //打开而不是创建0, //同步方式NULL);if(hCom==(HANDLE)-1){AfxMessageBox("打开COM失败!");return FALSE;}return TRUE;重叠I/O打开串口的示例代码:HANDLE hCom; //全局变量,串口句柄hCom =CreateFile("COM1", //COM1口GENERIC_READ|GENERIC_WRITE, //允许读和写0, //独占方式NULL,OPEN_EXISTING, //打开而不是创建FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重叠方式NULL);if(hCom ==INVALID_HANDLE_VALUE){AfxMessageBox("打开COM失败!");return FALSE;}return TRUE;(2)、配置串口在打开通讯设备句柄后,常常需要对串口进行一些初始化配置工作。
在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信。
串口通信方便易行,应用广泛。
一般情况下,工控机和各智能仪表通过RS485总线进行通信。
RS485的通信方式是半双工的,只能由作为主节点的工控PC机依次轮询网络上的各智能控制单元子节点。
每次通信都是由PC机通过串口向智能控制单元发布命令,智能控制单元在接收到正确的命令后作出应答。
在Win32下,可以使用两种编程方式实现串口通信,其一是使用ActiveX控件,这种方法程序简单,但欠灵活。
其二是调用Windows的API函数,这种方法可以清楚地掌握串口通信的机制,并且自由灵活。
本文我们只介绍API串口通信部分。
串口的操作可以有两种操作方式:同步操作方式和重叠操作方式(又称为异步操作方式)。
同步操作时,API函数会阻塞直到操作完成以后才能返回(在多线程方式中,虽然不会阻塞主线程,但是仍然会阻塞监听线程);而重叠操作方式,API函数会立即返回,操作在后台进行,避免线程的阻塞。
无论那种操作方式,一般都通过四个步骤来完成:(1)打开串口(2)配置串口(3)读写串口(4)关闭串口1、打开串口Win32系统把文件的概念进行了扩展。
无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。
该函数的原型为:C++代码HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWOR D dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dw CreationDistribution, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);lpFileName:将要打开的串口逻辑名,如“COM1”;dwDesiredAccess:指定串口访问的类型,可以是读取、写入或二者并列;dwShareMode:指定共享属性,由于串口不能共享,该参数必须置为0;lpSecurityAttributes:引用安全性属性结构,缺省值为NULL;dwCreationDistribution:创建标志,对串口操作该参数必须置为OPEN_EXISTING;dwFlagsAndAttributes:属性描述,用于指定该串口是否进行异步操作,该值为FILE_FLAG_OVERLAPPED,表示使用异步的I/O;该值为0,表示同步I/O 操作;hTemplateFile:对串口而言该参数必须置为NULL。
如何实现VC应用程序与PLC的数据交换当今,随着工业自动化的广泛应用,越来越多的工业企业开始采用可编程逻辑控制器(PLC)来控制和管理生产过程。
而随着信息技术的不断发展,将计算机技术与工业控制相结合,实现VC应用程序与PLC的数据交换,已经成为很多企业提高生产效率、提升产品质量的一种重要手段。
本文将从硬件和软件两个角度来介绍如何实现VC应用程序与PLC的数据交换。
硬件层面:在实现VC应用程序与PLC的数据交换之前,首先需要确保计算机和PLC之间能够进行可靠的通信。
一般而言,可以通过以下几种方式来实现计算机和PLC之间的连接和通信。
1.串口通信:串口通信是最常见的一种方式,通过串口线将计算机的串口与PLC的COM口相连接。
在VC应用程序中,通过编程方式来读写串口数据,从而实现与PLC的数据交互。
2.以太网通信:在以太网通信方式下,计算机和PLC通过以太网进行连接。
可以使用TCP/IP协议来实现数据的传输。
在VC应用程序中,可以使用套接字编程来实现与PLC的通信。
B通信:有些PLC具备USB接口,可以通过USB线将计算机和PLC进行连接。
在VC应用程序中,可以通过USB编程来与PLC进行数据交互。
软件层面:在硬件连接完成之后,下一步就是通过软件来实现VC应用程序与PLC的数据交换。
以下是一些常用的软件开发工具和编程语言,可以帮助我们实现这个目标。
1.Visual Studio:Visual Studio是一个功能强大的集成开发环境,可以用于开发各种类型的应用程序,包括VC应用程序。
在VC应用程序中,可以通过编写相应的代码来与PLC进行数据交换。
2.C#编程语言:C#是一种简单、现代化的编程语言,广泛应用于Windows平台的应用程序开发。
在VC应用程序中,可以使用C#编写代码来实现与PLC的数据交互。
3.编程库:PLC通常配备有相关的编程库,包括DLL、API等,可以用于编写与PLC通信的代码。
在VC应用程序中,可以引用这些编程库,从而实现与PLC的数据交换。
1.建立项目:打开VC++6.0,建立一个基于对话框的MFC应用程序SCommTest;2.在项目中插入MSComm控件选择Project菜单下Add To Project子菜单中的Components and Controls…选项,在弹出的对话框中双击Registered ActiveX Controls项(稍等一会,这个过程较慢),则所有注册过的ActiveX控件出现在列表框中。
选择Microsoft Communications Control, version 6.0,,单击Insert按钮将它插入到我们的Project中来,接受缺省的选项。
(如果你在控件列表中看不到Microsoft Communications Control, version 6.0,那可能是你在安装VC6时没有把ActiveX 一项选上,重新安装VC6,选上ActiveX就可以了),这时在ClassView视窗中就可以看到CMSComm类了,(注意:此类在ClassWizard中看不到,重构clw文件也一样),并且在控件工具栏Controls中出现了电话图标(如图1所示),现在要做的是用鼠标将此图标拖到对话框中,程序运行后,这个图标是看不到的。
3.利用ClassWizard定义CMSComm类控制对象打开ClassWizard->Member Viariables选项卡,选择CSCommTestDlg类,为IDC_MSCOMM1添加控制变量:m_ctrlComm,这时你可以看一看,在对话框头文件中自动加入了//{{AFX_INCLUDES() #i nclude "mscomm.h"//}}AFX_INCLUDES 。
4.在对话框中添加控件向主对话框中添加两个编辑框,一个用于接收显示数据ID为IDC_EDIT_RXDATA,另一个用于输入发送数据,ID为IDC_EDIT_TXDATA,再添加一个按钮,功能是按一次就把发送编辑框中的内容发送一次,将其ID设为IDC_BUTTON_MANUALSEND。
如何用VC创建及调用DLL使用Visual C++(VC++)创建和调用动态链接库(DLL)可以提供一种模块化的方式来组织和重用代码。
本文将介绍如何使用VC++创建DLL,并在另一个VC++项目中调用该DLL。
创建DLL以下是使用VC++创建DLL的步骤:1.打开VC++,在“文件”菜单中选择“新建”->“项目”。
2. 在“新建项目”对话框中,选择“Win32控制台应用程序”。
点击“下一步”。
3.输入项目名称,并选择项目位置,点击“下一步”。
4.在“应用程序类型”对话框中,选择“DLL”并取消勾选“预编译头”。
点击“下一步”。
5.在“进入代码”对话框中,选择“空项目”。
点击“完成”。
6. 创建一个新的源文件,例如“MyDLL.cpp”。
7. 在“MyDLL.cpp”中,编写所需的函数并导出。
例如:```C++#include <Windows.h>// 导出的函数需要使用__declspec(dllexport)修饰extern "C" __declspec(dllexport) int AddNumbers(int a, int b) return a + b;```8. 在项目属性中,选择“链接器”->“高级”,将“入口点”设置为“DllMain”。
9.在“生成”菜单中选择“生成解决方案”,以生成DLL文件。
以下是在VC++项目中调用DLL的步骤:1.打开VC++,在“文件”菜单中选择“新建”->“项目”。
2. 在“新建项目”对话框中,选择“Win32控制台应用程序”。
点击“下一步”。
3.输入项目名称,并选择项目位置,点击“下一步”。
4.在“应用程序类型”对话框中,选择“控制台应用程序”并取消勾选“预编译头”。
点击“下一步”。
5.在“附加选项”对话框中,勾选“空项目”。
点击“完成”。
6.将之前生成的DLL文件复制到新项目的文件夹中。
7.在项目属性中,选择“C/C++”->“常规”,将“附加包含目录”设置为包含DLL文件的文件夹路径。
vc dll导出方法VC DLL导出方法在VC(Visual C++)中,DLL(Dynamic Link Library)是一种动态链接库,它可以包含多个可执行文件。
在使用VC开发过程中,经常需要将函数或变量导出为DLL,以供其他程序使用。
本文将介绍如何在VC中导出方法为DLL,并详细讲解相关的技术和注意事项。
一、什么是VC DLL导出方法?VC DLL导出方法是指在VC中将函数或变量导出为动态链接库的过程。
通过将函数或变量导出为DLL,其他程序可以使用这些函数或变量,实现代码的共享和重用。
在VC中,导出方法可以通过使用宏、关键字或修饰符等方式实现。
二、VC DLL导出方法的实现步骤1. 创建VC项目:首先,在VC中创建一个新的项目。
可以选择“Win32 Console Application”或“Win32 DLL”等项目类型,根据实际需求选择。
2. 编写代码:在项目中编写需要导出的函数或变量。
可以使用C或C++语言编写代码,根据实际需求进行逻辑处理和功能实现。
3. 声明导出函数或变量:在函数或变量的前面加上宏、关键字或修饰符等,声明它们为导出的。
例如,在VC中可以使用__declspec(dllexport)关键字来声明导出函数或变量。
4. 生成DLL文件:编译项目并生成DLL文件。
在生成过程中,VC 会将导出的函数或变量打包为动态链接库,生成对应的DLL文件。
5. 使用DLL文件:在其他程序中引用生成的DLL文件,调用其中的导出函数或变量。
可以通过函数指针、LoadLibrary()和GetProcAddress()等方式实现。
三、VC DLL导出方法的注意事项1. 函数或变量的命名:导出的函数或变量的命名应具有一定的规范性和易读性,以方便其他程序员使用和理解。
2. 函数或变量的参数和返回值:导出的函数或变量在设计时应考虑参数的合理性和返回值的正确性,以避免出现错误或歧义。
3. 调用约定和位宽:在导出函数或变量时,需要指定调用约定和位宽,以确保在不同的编译环境中能够正确地调用和使用。
VC串口通讯DLL串口在工业应用是极为普遍的,我用API封装了同步和异步的串口类,以及一个具有监视线程的异步串口类;使用简单高效,具有工业强度,我在BC, BCB, VC, BCBX, GCC下编译通过,相信足够应付大多数情况,而且还可以继承扩展,下面简单介绍使用方法库的层次结构:_base_com:虚基类,基本接口,可自行扩展自己的串口类_sync_com:_base_com 的子类,同步应用,适合简单应用_asyn_com:_base_com 的子类,异步应用(重叠I/O),适合较高效应用,NT平台_thread_com:_asyn_com 的子类,异步应用,监视线程,适合较复杂应用,窗口通知消息和继承扩展的使用方式;几个问题:结束线程如何从WaitCommEvent(pcom->_com_handle, &mask, &pcom->_wait_o)这个API退出以便顺利结束线程:方案1:SetCommMask(_com_handle, 0); 这个方法在MSDN有载,当在一些情况下并不完全有效,原因未知;方案2:SetEvent(_wait_o.hEvent); 直接激活重叠IO结构中的事件句柄,绝对有效;这份代码我两种都用;打开10以上的COM端口在NT/2000下打开编号10以上端口用_com_handle = CreateFile(“COM10“,GENERIC_READ GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL FILE_FLAG_OVERLAPPED, //重叠I/ONULL);将提示错误,这样就OK:_com_handle = CreateFile(“\\\\.\\COM10“,//对应的就是\\.\COM10GENERIC_READ GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL FILE_FLAG_OVERLAPPED, //重叠I/ONULL);线程中循环的低效率问题使用SetCommMask(pcom->_com_handle, EV_RXCHAR EV_ERR)监视接受字符和错误消息;一旦有个字符来就会激活WaitCommEvent 通常作以下接受操作:if(!WaitCommEvent(pcom->_com_handle, &mask, &pcom->_wait_o)) {if(GetLastError() == ERROR_IO_PENDING){GetOverlappedResult(pcom->_com_handle, &pcom->_wait_o, &length, true);}}if(mask & EV_ERR) // == EV_ERRClearCommError(pcom->_com_handle, &error, &stat);if(mask & EV_RXCHAR) // == EV_RXCHAR{pcom->on_receive();//接收到字符//或发送到窗口消息}这样频繁的函数调用或接受发送消息,效率低下,我添加扫描缓冲区的代码,当字符数超过设定的字符数才作接受字符的操作;if(mask & EV_RXCHAR) // == EV_RXCHAR{ClearCommError(pcom->_com_handle, &error, &stat);if(stat.cbInQue > pcom->_notify_num) //_notify_num 是设定得字符数pcom->on_receive();}类似于流的输出方式我编了一个简单的写串口的方式,可以类似于流将简单的数据类型输出template<typename T>_asyn_com& operator << (T x){strstream s;s << x ;write(s.str(), s.pcount());return *this;}就可以这样使用_sync_com com1;com1.open(1, 9600);com1 << “ then random() 's return value is “<< rand() << “ .\n“ ;com1.close();本串口类库的主要接口class _base_com{bool open(int port);bool open(int port, int baud_rate);bool open(int port, char * set_str); // set_str : “9600, 8, n, 1“bool set_state(int BaudRate, int ByteSize = 8, int Parity = NOPARITY, int StopBits = ONESTOPBIT)//设置内置结构串口参数:波特率,停止位bool set_state(char *set_str)bool is_open();HANDLE get_handle();virtual bool open_port()=0; //继承用的重要函数virtual close();}class _sync_com :public _base_com //同步{int read(char *buf, int buf_size); //自动补上'\0',将用去一个字符的缓冲区int write(char *buf, int len);int write(char *buf);}class _asyn_com :public _base_com //异步{int read(char *buf, int buf_size); //自动补上'\0',将用去一个字符的缓冲区int write(char *buf, int len);int write(char *buf);}class _thread_com :public _asyn_com //线程{virtual void on_receive() //供线程接受到字符时调用,可继承替换之{if(_notify_hwnd)PostMessage(_notify_hwnd, ON_COM_RECEIVE, WPARAM(_port), LPARAM(0));else{if(_func)_func(_port);}}void set_hwnd(HWND hwnd); //设置窗口句柄,发送ON_COM_RECEIVE WM_USER + 618void set_func(void (*f)(int)); //设置调用函数,窗口句柄优先void set_notify_num(int num); //设定发送通知, 接受字符最小值}一些应用范例当然首先#include "_com.h"一、打开串口1同步写char str[] = "com_class test";_sync_com com1; //同步com1.open(1); // 相当于com1.open(1, 9600); com1.open(1, "9600,8,n,1");for(int i=0; i<100; i++){Sleep(500);com1.write(str); //也可以com1.write(str, strlen(str));}com1.close();二、打开串口2异步读char str[100];_asyn_com com2; //异步com2.open(2); // 相当于com2.open(2, 9600); com2.open(2, "9600,8,n,1");if(!com2.is_open())cout << "COM2 not open , error : " << GetLastError() << endl;也可以如下用法if(!com2.open(2))cout << "COM2 not open , error : " << GetLastError() << endl; */for(int i=0; i<100; i++){Sleep(500);if(com2.read(str, 100) > 0) //异步读,返回读取字符数cout << str;}com2.close();三、扩展应用具有监视线程的串口类class _com_ex : public thread_com{public:virtual on_receive(){char str[100];if(read(str, 100) > 0) //异步读,返回读取字符数cout << str;}int main(int argc, char *argv[]){try{char str[100];_com_ex com2; //异步扩展com2.open(2);Sleep(10000);com2.close();}catch(exception &e){cout << e.what() << endl;}return 0;}四、桌面应用可发送消息到指定窗口(在C++ Builder 和VC ++ 测试通过)VC ++接受消息BEGIN_MESSAGE_MAP(ComDlg, CDialog)//{{AFX_MSG_MAP(ComDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_WM_DESTROY()//}}AFX_MSG_MAPON_MESSAGE(ON_COM_RECEIVE, On_Receive)END_MESSAGE_MAP()打开串口,传递窗口句柄_thread_com com2;com2.open(2);com2.set_hwnd(ComDlg->m_hWnd);处理消息LRESULT ComDlg::On_Receive(WPARAM wp, LPARAM lp) {char str[100];com2.read(str, 100);char com_str[10];strcpy(com_str, "COM");ltoa((long)wp, com_str + 3, 10); // WPARAM 保存端口号MessageBox(str, com_str, MB_OK);return 0;}C++ Builderclass TForm1 : public TForm{__published: // IDE-managed Componentsvoid __fastcall FormClose(TObject *Sender, TCloseAction &Action); void __fastcall FormCreate(TObject *Sender);private: // User declarationspublic: // User declarationsvoid On_Receive(TMessage& Message);__fastcall TForm1(TComponent* Owner);_thread_com com2;BEGIN_MESSAGE_MAPMESSAGE_HANDLER(ON_COM_RECEIVE, TMessage, On_Receive) END_MESSAGE_MAP(TForm)};void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action){com2.close();}//---------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender){com2.open(2);com2.set_hwnd(Handle);}//---------------------------------------------------------------------------void TForm1::On_Receive(TMessage& Message){char xx[20];int port = Message.WParam;if(com2.read(xx, 20) > 0)ShowMessage(xx);}错误和缺陷在所难免,欢迎来信批评指正;wushaojian@ 附完整源代码_com.h/*串口基础类库(WIN32)ver 0.1编译器:BC++ 5; C++ BUILDER 4, 5, 6, X; VC++ 5, 6; ; GCC; class _base_com : 虚基类基本串口接口;class _sync_com : 同步I/O 串口类;class _asyn_com : 异步I/O 串口类;class _thread_com : 异步I/O 辅助读监视线程可转发窗口消息串口类(可继承虚函数on_receive用于读操作);class _com : _thread_com 同名copyright(c) 2004.8 llbird wushaojian@*//*Example :*/#ifndef _COM_H_#define _COM_H_#pragma warning(disable: 4530)#pragma warning(disable: 4786)#pragma warning(disable: 4800)#include <cassert>#include <strstream>#include <algorithm>#include <exception>#include <iomanip>using namespace std;#include <windows.h>class _base_com //虚基类基本串口接口{protected:volatile int _port; //串口号volatile HANDLE _com_handle;//串口句柄char _com_str[20];DCB _dcb; //波特率,停止位,等COMMTIMEOUTS _co; // 超时时间virtual bool open_port() = 0;void init() //初始化{memset(_com_str, 0, 20);memset(&_co, 0, sizeof(_co));memset(&_dcb, 0, sizeof(_dcb));_dcb.DCBlength = sizeof(_dcb);_com_handle = INVALID_HANDLE_VALUE; }virtual bool setup_port(){if(!is_open())return false;if(!SetupComm(_com_handle, 8192, 8192))return false; //设置推荐缓冲区if(!GetCommTimeouts(_com_handle, &_co))return false;_co.ReadIntervalTimeout = 0xFFFFFFFF;_co.ReadTotalTimeoutMultiplier = 0;_co.ReadTotalTimeoutConstant = 0;_co.WriteTotalTimeoutMultiplier = 0;_co.WriteTotalTimeoutConstant = 2000;if(!SetCommTimeouts(_com_handle, &_co))return false; //设置超时时间if(!PurgeComm(_com_handle, PURGE_TXABORT PURGE_RXABORT PURGE_TXCLEAR PURGE_RXCLEAR ))return false; //清空串口缓冲区return true;}inline void set_com_port(int port){char p[12];_port = port;strcpy(_com_str, "\\\\.\\COM");ltoa(_port, p, 10);strcat(_com_str, p);}public:_base_com(){init();}virtual ~_base_com(){close();}//设置串口参数:波特率,停止位,等支持设置字符串"9600, 8, n, 1"bool set_state(char *set_str){if(is_open()){if(!GetCommState(_com_handle, &_dcb))return false;if(!BuildCommDCB(set_str, &_dcb))return false;return SetCommState(_com_handle, &_dcb) == TRUE;}return false;}//设置内置结构串口参数:波特率,停止位bool set_state(int BaudRate, int ByteSize = 8, int Parity = NOPARITY, int StopBits = ONESTOPBIT){if(is_open()){if(!GetCommState(_com_handle, &_dcb))return false;_dcb.BaudRate = BaudRate;_dcb.ByteSize = ByteSize;_dcb.Parity = Parity;_dcb.StopBits = StopBits;return SetCommState(_com_handle, &_dcb) == TRUE;}return false;}//打开串口缺省9600, 8, n, 1inline bool open(int port){return open(port, 9600);}//打开串口缺省baud_rate, 8, n, 1 inline bool open(int port, int baud_rate) {if(port < 1 port > 1024)return false;set_com_port(port);if(!open_port())return false;if(!setup_port())return false;return set_state(baud_rate);}//打开串口inline bool open(int port, char *set_str) {if(port < 1 port > 1024)return false;set_com_port(port);if(!open_port())return false;if(!setup_port())return false;return set_state(set_str);}inline bool set_buf(int in, int out){return is_open() ? SetupComm(_com_handle, in, out) : false; }//关闭串口inline virtual void close(){if(is_open()){CloseHandle(_com_handle);_com_handle = INVALID_HANDLE_VALUE;}}//判断串口是或打开inline bool is_open(){return _com_handle != INVALID_HANDLE_VALUE; }//获得串口句炳HANDLE get_handle(){return _com_handle;}operator HANDLE(){return _com_handle;}};class _sync_com : public _base_com{protected://打开串口virtual bool open_port(){if(is_open())close();_com_handle = CreateFile(_com_str,GENERIC_READ GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL ,NULL);assert(is_open());return is_open();//检测串口是否成功打开}public:_sync_com(){}//同步读int read(char *buf, int buf_len){if(!is_open())return 0;buf[0] = '\0';COMSTAT stat;DWORD error;if(ClearCommError(_com_handle, &error, &stat) && error > 0) //清除错误{PurgeComm(_com_handle, PURGE_RXABORT PURGE_RXCLEAR); /*清除输入缓冲区*/return 0;}unsigned long r_len = 0;buf_len = min(buf_len - 1, (int)stat.cbInQue);if(!ReadFile(_com_handle, buf, buf_len, &r_len, NULL))r_len = 0;buf[r_len] = '\0';return r_len;}//同步写int write(char *buf, int buf_len){if(!is_open() !buf)return 0;DWORD error;if(ClearCommError(_com_handle, &error, NULL) && error > 0) //清除错误PurgeComm(_com_handle, PURGE_TXABORT PURGE_TXCLEAR); unsigned long w_len = 0;if(!WriteFile(_com_handle, buf, buf_len, &w_len, NULL))w_len = 0;return w_len;}//同步写inline int write(char *buf){assert(buf);return write(buf, strlen(buf));}//同步写, 支持部分类型的流输出template<typename T>_sync_com& operator << (T x){strstream s;s << x;write(s.str(), s.pcount());return *this;}};class _asyn_com : public _base_com{protected:OVERLAPPED _ro, _wo; // 重叠I/Ovirtual bool open_port(){if(is_open())close();_com_handle = CreateFile(_com_str,GENERIC_READ GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL FILE_FLAG_OVERLAPPED, //重叠I/O NULL);assert(is_open());return is_open();//检测串口是否成功打开}public:_asyn_com(){memset(&_ro, 0, sizeof(_ro));memset(&_wo, 0, sizeof(_wo));_ro.hEvent = CreateEvent(NULL, true, false, NULL); assert(_ro.hEvent != INVALID_HANDLE_VALUE);_wo.hEvent = CreateEvent(NULL, true, false, NULL); assert(_wo.hEvent != INVALID_HANDLE_VALUE); }virtual ~_asyn_com(){close();if(_ro.hEvent != INVALID_HANDLE_VALUE)CloseHandle(_ro.hEvent);if(_wo.hEvent != INVALID_HANDLE_VALUE)CloseHandle(_wo.hEvent);}//异步读int read(char *buf, int buf_len, int time_wait = 20) {if(!is_open())return 0;buf[0] = '\0';COMSTAT stat;DWORD error;if(ClearCommError(_com_handle, &error, &stat) && error > 0) //清除错误{PurgeComm(_com_handle, PURGE_RXABORT PURGE_RXCLEAR); /*清除输入缓冲区*/return 0;}if(!stat.cbInQue)// 缓冲区无数据return 0;unsigned long r_len = 0;buf_len = min((int)(buf_len - 1), (int)stat.cbInQue);if(!ReadFile(_com_handle, buf, buf_len, &r_len, &_ro)) //2000 下ReadFile 始终返回True{if(GetLastError() == ERROR_IO_PENDING) // 结束异步I/O{//WaitForSingleObject(_ro.hEvent, time_wait); //等待20msif(!GetOverlappedResult(_com_handle, &_ro, &r_len, false)){if(GetLastError() != ERROR_IO_INCOMPLETE)//其他错误r_len = 0;}}elser_len = 0;}buf[r_len] = '\0';return r_len;}//异步写int write(char *buf, int buf_len){if(!is_open())return 0;DWORD error;if(ClearCommError(_com_handle, &error, NULL) && error > 0) //清除错误PurgeComm(_com_handle, PURGE_TXABORT PURGE_TXCLEAR); unsigned long w_len = 0, o_len = 0;if(!WriteFile(_com_handle, buf, buf_len, &w_len, &_wo))if(GetLastError() != ERROR_IO_PENDING)w_len = 0;return w_len;}//异步写inline int write(char *buf){assert(buf);return write(buf, strlen(buf));}//异步写, 支持部分类型的流输出template<typename T>_asyn_com& operator << (T x){strstream s;s << x ;write(s.str(), s.pcount());return *this;}};//当接受到数据送到窗口的消息#define ON_COM_RECEIVE WM_USER + 618 // WPARAM 端口号class _thread_com : public _asyn_com{protected:volatile HANDLE _thread_handle; //辅助线程volatile HWND _notify_hwnd; // 通知窗口volatile long _notify_num;//接受多少字节(>_notify_num)发送通知消息volatile bool _run_flag; //线程运行循环标志void (*_func)(int port);OVERLAPPED _wait_o; //WaitCommEvent use//线程收到消息自动调用, 如窗口句柄有效, 送出消息, 包含窗口编号virtual void on_receive(){if(_notify_hwnd)PostMessage(_notify_hwnd, ON_COM_RECEIVE, WPARAM(_port), LPARAM(0));else{if(_func)_func(_port);}}//打开串口,同时打开监视线程virtual bool open_port(){if(_asyn_com::open_port()){_run_flag = true;DWORD id;_thread_handle = CreateThread(NULL, 0, com_thread, this, 0, &id); //辅助线程assert(_thread_handle);if(!_thread_handle){CloseHandle(_com_handle);_com_handle = INVALID_HANDLE_VALUE;}elsereturn true;}return false;}public:_thread_com(){_notify_num = 0;_notify_hwnd = NULL;_thread_handle = NULL;_func = NULL;memset(&_wait_o, 0, sizeof(_wait_o));_wait_o.hEvent = CreateEvent(NULL, true, false, NULL); assert(_wait_o.hEvent != INVALID_HANDLE_VALUE); }~_thread_com(){close();if(_wait_o.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_wait_o.hEvent);}//设定发送通知, 接受字符最小值void set_notify_num(int num){_notify_num = num;}int get_notify_num(){return _notify_num;}//送消息的窗口句柄inline void set_hwnd(HWND hWnd) {_notify_hwnd = hWnd;}inline HWND get_hwnd(){return _notify_hwnd;}inline void set_func(void (*f)(int)) {_func = f;}//关闭线程及串口virtual void close(){if(is_open()){_run_flag = false;SetCommMask(_com_handle, 0);SetEvent(_wait_o.hEvent);if(WaitForSingleObject(_thread_handle, 100) != WAIT_OBJECT_0)TerminateThread(_thread_handle, 0);CloseHandle(_com_handle);CloseHandle(_thread_handle);_thread_handle = NULL;_com_handle = INVALID_HANDLE_VALUE;ResetEvent(_wait_o.hEvent);}}/*辅助线程控制*///获得线程句柄HANDLE get_thread(){return _thread_handle;}//暂停监视线程bool suspend(){return _thread_handle != NULL ? SuspendThread(_thread_handle) != 0xFFFFFFFF : false;}//恢复监视线程bool resume(){return _thread_handle != NULL ? ResumeThread(_thread_handle) != 0xFFFFFFFF : false;}//重建监视线程bool restart(){if(_thread_handle) /*只有已有存在线程时*/{_run_flag = false;SetCommMask(_com_handle, 0);SetEvent(_wait_o.hEvent);if(WaitForSingleObject(_thread_handle, 100) != WAIT_OBJECT_0)TerminateThread(_thread_handle, 0);CloseHandle(_thread_handle);_run_flag = true;_thread_handle = NULL;DWORD id;_thread_handle = CreateThread(NULL, 0, com_thread, this, 0, &id);return (_thread_handle != NULL); //辅助线程}return false;}private://监视线程static DWORD WINAPI com_thread(LPVOID para){_thread_com *pcom = (_thread_com *)para;if(!SetCommMask(pcom->_com_handle, EV_RXCHAR EV_ERR)) return 0;COMSTAT stat;DWORD error;for(DWORD length, mask = 0; pcom->_run_flag && pcom->is_open(); mask = 0){if(!WaitCommEvent(pcom->_com_handle, &mask, &pcom->_wait_o)){if(GetLastError() == ERROR_IO_PENDING){GetOverlappedResult(pcom->_com_handle, &pcom->_wait_o,&length, true);}}if(mask & EV_ERR) // == EV_ERRClearCommError(pcom->_com_handle, &error, &stat);if(mask & EV_RXCHAR) // == EV_RXCHAR{ClearCommError(pcom->_com_handle, &error, &stat);if(stat.cbInQue > pcom->_notify_num)pcom->on_receive();}}return 0;}};typedef _thread_com _com; //名称简化#endif //_COM_H_。