使用 C 在 Windows 中被动监控串口
Passive Monitoring Serial Port in Windows using C
我是串行编程的菜鸟。我正在尝试用 C 编写一个被动监视器,以显示屏幕上写入或从 COM 端口读取的任何内容。我看到的大多数代码实际上是从 COM 端口读取或写入。
我尝试从正在传输和接收 Modbus 流量的 COM 端口读取数据,但没有得到任何读数。我正在使用 com0com 串口模拟器。只有当我真正从与 COM 端口配对的另一个端口读取代码时,代码才能工作。
我正在尝试模仿串行端口监视器应用程序。到目前为止它不起作用。请协助。
谢谢。
下面是 COM 读取的代码:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void setupPort(HANDLE * handle, char * portName);
void readFromPort(HANDLE * handle);
int main()
{
HANDLE first_port;
char * first_port_name = "COM3";
setupPort(&first_port, first_port_name);
readFromPort(&first_port);
return 0;
}
void setupPort(HANDLE * handle, char * portName)
{
BOOL status;
*handle = CreateFile(portName, //port name
GENERIC_READ | GENERIC_WRITE, //Read/Write
0, // No Sharing
NULL, // No Security
OPEN_EXISTING,// Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
if (handle == INVALID_HANDLE_VALUE)
{
printf("\n%s could not be opened\n", portName);
}
else
{
printf("\n%s successfully opened.\n", portName);
}
DCB dcbSerialParams = { 0 }; // Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
status = GetCommState(*handle, &dcbSerialParams); //retreives the current settings
if (status == FALSE)
printf("\n Error! in GetCommState()");
dcbSerialParams.BaudRate = CBR_9600; // Setting BaudRate = 9600
dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
dcbSerialParams.StopBits = ONESTOPBIT; // Setting StopBits = 1
dcbSerialParams.Parity = NOPARITY; // Setting Parity = None
status = SetCommState(*handle, &dcbSerialParams); //Configuring the port according to settings in DCB
if (status == FALSE)
{
printf("\n Error! in Setting DCB Structure");
}
else //If Successful display the contents of the DCB Structure
{
printf("\n\n Setting DCB Structure Successful\n");
printf("\n Baudrate = %d", dcbSerialParams.BaudRate);
printf("\n ByteSize = %d", dcbSerialParams.ByteSize);
printf("\n StopBits = %d", dcbSerialParams.StopBits);
printf("\n Parity = %d", dcbSerialParams.Parity);
}
/*------------------------------------ Setting Timeouts --------------------------------------------------*/
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (SetCommTimeouts(*handle, &timeouts) == FALSE)
printf("\n\n Error! in Setting Time Outs");
else
printf("\n\n Setting Serial Port Timeouts Successful");
/*------------------------------------ Setting Receive Mask ----------------------------------------------*/
status = SetCommMask(*handle, EV_RXCHAR); //Configure Windows to Monitor the serial device for Character Reception
if (status == FALSE)
printf("\n\n Error! in Setting CommMask");
else
printf("\n\n Setting CommMask successful");
}
void readFromPort(HANDLE * handle)
{
BOOL status;
DWORD dwEventMask; // Event mask to trigger
char TempChar; // Temporary Character
char SerialBuffer[256]; // Buffer Containing Rxed Data
DWORD NoBytesRead; // Bytes read by ReadFile()
int i = 0;
/*------------------------------------ Setting WaitComm() Event ----------------------------------------*/
while(TRUE)
{
printf("\n\n Waiting for Data Reception");
status = TRUE; //Wait for the character to be received
/*-------------------------- Program will Wait here till a Character is received ------------------------*/
if (status == FALSE)
{
printf("\n Error! in Setting WaitCommEvent()");
}
else //If WaitCommEvent()==True Read the RXed data using ReadFile();
{
printf("\n\n Characters Received\n");
do
{
status = ReadFile(*handle, &TempChar, sizeof(TempChar), &NoBytesRead, NULL);
SerialBuffer[i] = TempChar;
i++;
}
while (NoBytesRead > 0);
/*------------Printing the RXed String to Console----------------------*/
printf("\n\n ");
int j =0;
for (j = 0; j < i-1; j++) // j < i-1 to remove the dupliated last character
{
printf("%02X", (unsigned int)(unsigned char)SerialBuffer[j]);
}
i=0;
}
//CloseHandle(*handle);//Closing the Serial Port
printf("\n +==========================================+\n");
}
}
您的代码应该可以正常工作(编辑: 只要您打算将它与 com0com 一起使用)。正如 busybee 在上面的评论中所建议的那样,我认为您混淆了您的端口或误解了 com0com 应该如何工作。
您可能有两种不同的情况:
1)您正在使用 Windows PC 作为嗅探器来监视 另外两个 方之间的 Modbus 事务。例如 PLC 和远程 Modbus 传感器。在这种情况下,您需要两个真正的硬件串口和几个由 com0com 提供的虚拟端口。
2)如果您计算机中的某些东西作为 Modbus 事务中的一方,那么您只需要一个硬件串行端口和几个虚拟端口。
既然你提到了 passive,我猜你是在场景 1 中。如果是这样,你只需要正确选择你的端口。我写了一个完整的例子来说明如何做到这一点,巧合的是 Modbus 也使用 Termite 和 com0com,看看 . You might also want to take a look to SerialPCAP,它与 Wireshark 结合甚至可以解码你的 Modbus 消息。
如果你喜欢重新发明轮子,我想你可以放弃 com0com 并按照评论中其他人的建议共享端口。如果您决定走这条路,您可能想阅读一些有趣的问题,请参阅 。
编辑: 你说你确实想重新发明轮子。这很好,但我认为在开始编写代码之前您需要考虑一些事情。我不是串口开发专家;在 Windows 上少得多,在最近的 Windows 版本上就更少了。但是我以前对这个话题做了一些研究,所以我可以给你我的观点:
-我们中的大多数非 wheelreinventors 会非常乐意使用上面解释的虚拟串口技术来监控我们的串口(我再重复一遍:对于 Modbus RTU 流量监控,请查看 Wireshark/SerialPCAP 你会忘记其他任何事情)。我的第一印象是您想这样做(这就是您谈论 com0com 的原因)。看了你的评论,我想这对你来说还不够好(我能理解,我更喜欢干净的解决方案而不是卑鄙的把戏)。
-现在,清楚了,有什么可以做的吗?从用户空间来看,我不认为你现在可以共享串行端口。你的问题的评论中提到 dwShareMode
的技巧可能在 90 年代就奏效了,但恐怕它不会再奏效了。有关详细信息,请参阅 。
-如果你去driverland,你可能会有一些机会。阅读 here. Other useful links: 1,2.
我的结论是:你的代码没有修复,你想做的比你拥有的更复杂。
我是串行编程的菜鸟。我正在尝试用 C 编写一个被动监视器,以显示屏幕上写入或从 COM 端口读取的任何内容。我看到的大多数代码实际上是从 COM 端口读取或写入。
我尝试从正在传输和接收 Modbus 流量的 COM 端口读取数据,但没有得到任何读数。我正在使用 com0com 串口模拟器。只有当我真正从与 COM 端口配对的另一个端口读取代码时,代码才能工作。
我正在尝试模仿串行端口监视器应用程序。到目前为止它不起作用。请协助。
谢谢。
下面是 COM 读取的代码:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void setupPort(HANDLE * handle, char * portName);
void readFromPort(HANDLE * handle);
int main()
{
HANDLE first_port;
char * first_port_name = "COM3";
setupPort(&first_port, first_port_name);
readFromPort(&first_port);
return 0;
}
void setupPort(HANDLE * handle, char * portName)
{
BOOL status;
*handle = CreateFile(portName, //port name
GENERIC_READ | GENERIC_WRITE, //Read/Write
0, // No Sharing
NULL, // No Security
OPEN_EXISTING,// Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
if (handle == INVALID_HANDLE_VALUE)
{
printf("\n%s could not be opened\n", portName);
}
else
{
printf("\n%s successfully opened.\n", portName);
}
DCB dcbSerialParams = { 0 }; // Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
status = GetCommState(*handle, &dcbSerialParams); //retreives the current settings
if (status == FALSE)
printf("\n Error! in GetCommState()");
dcbSerialParams.BaudRate = CBR_9600; // Setting BaudRate = 9600
dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
dcbSerialParams.StopBits = ONESTOPBIT; // Setting StopBits = 1
dcbSerialParams.Parity = NOPARITY; // Setting Parity = None
status = SetCommState(*handle, &dcbSerialParams); //Configuring the port according to settings in DCB
if (status == FALSE)
{
printf("\n Error! in Setting DCB Structure");
}
else //If Successful display the contents of the DCB Structure
{
printf("\n\n Setting DCB Structure Successful\n");
printf("\n Baudrate = %d", dcbSerialParams.BaudRate);
printf("\n ByteSize = %d", dcbSerialParams.ByteSize);
printf("\n StopBits = %d", dcbSerialParams.StopBits);
printf("\n Parity = %d", dcbSerialParams.Parity);
}
/*------------------------------------ Setting Timeouts --------------------------------------------------*/
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (SetCommTimeouts(*handle, &timeouts) == FALSE)
printf("\n\n Error! in Setting Time Outs");
else
printf("\n\n Setting Serial Port Timeouts Successful");
/*------------------------------------ Setting Receive Mask ----------------------------------------------*/
status = SetCommMask(*handle, EV_RXCHAR); //Configure Windows to Monitor the serial device for Character Reception
if (status == FALSE)
printf("\n\n Error! in Setting CommMask");
else
printf("\n\n Setting CommMask successful");
}
void readFromPort(HANDLE * handle)
{
BOOL status;
DWORD dwEventMask; // Event mask to trigger
char TempChar; // Temporary Character
char SerialBuffer[256]; // Buffer Containing Rxed Data
DWORD NoBytesRead; // Bytes read by ReadFile()
int i = 0;
/*------------------------------------ Setting WaitComm() Event ----------------------------------------*/
while(TRUE)
{
printf("\n\n Waiting for Data Reception");
status = TRUE; //Wait for the character to be received
/*-------------------------- Program will Wait here till a Character is received ------------------------*/
if (status == FALSE)
{
printf("\n Error! in Setting WaitCommEvent()");
}
else //If WaitCommEvent()==True Read the RXed data using ReadFile();
{
printf("\n\n Characters Received\n");
do
{
status = ReadFile(*handle, &TempChar, sizeof(TempChar), &NoBytesRead, NULL);
SerialBuffer[i] = TempChar;
i++;
}
while (NoBytesRead > 0);
/*------------Printing the RXed String to Console----------------------*/
printf("\n\n ");
int j =0;
for (j = 0; j < i-1; j++) // j < i-1 to remove the dupliated last character
{
printf("%02X", (unsigned int)(unsigned char)SerialBuffer[j]);
}
i=0;
}
//CloseHandle(*handle);//Closing the Serial Port
printf("\n +==========================================+\n");
}
}
您的代码应该可以正常工作(编辑: 只要您打算将它与 com0com 一起使用)。正如 busybee 在上面的评论中所建议的那样,我认为您混淆了您的端口或误解了 com0com 应该如何工作。
您可能有两种不同的情况:
1)您正在使用 Windows PC 作为嗅探器来监视 另外两个 方之间的 Modbus 事务。例如 PLC 和远程 Modbus 传感器。在这种情况下,您需要两个真正的硬件串口和几个由 com0com 提供的虚拟端口。
2)如果您计算机中的某些东西作为 Modbus 事务中的一方,那么您只需要一个硬件串行端口和几个虚拟端口。
既然你提到了 passive,我猜你是在场景 1 中。如果是这样,你只需要正确选择你的端口。我写了一个完整的例子来说明如何做到这一点,巧合的是 Modbus 也使用 Termite 和 com0com,看看
如果你喜欢重新发明轮子,我想你可以放弃 com0com 并按照评论中其他人的建议共享端口。如果您决定走这条路,您可能想阅读一些有趣的问题,请参阅
编辑: 你说你确实想重新发明轮子。这很好,但我认为在开始编写代码之前您需要考虑一些事情。我不是串口开发专家;在 Windows 上少得多,在最近的 Windows 版本上就更少了。但是我以前对这个话题做了一些研究,所以我可以给你我的观点:
-我们中的大多数非 wheelreinventors 会非常乐意使用上面解释的虚拟串口技术来监控我们的串口(我再重复一遍:对于 Modbus RTU 流量监控,请查看 Wireshark/SerialPCAP 你会忘记其他任何事情)。我的第一印象是您想这样做(这就是您谈论 com0com 的原因)。看了你的评论,我想这对你来说还不够好(我能理解,我更喜欢干净的解决方案而不是卑鄙的把戏)。
-现在,清楚了,有什么可以做的吗?从用户空间来看,我不认为你现在可以共享串行端口。你的问题的评论中提到 dwShareMode
的技巧可能在 90 年代就奏效了,但恐怕它不会再奏效了。有关详细信息,请参阅
-如果你去driverland,你可能会有一些机会。阅读 here. Other useful links: 1,2.
我的结论是:你的代码没有修复,你想做的比你拥有的更复杂。