【问题标题】:Passive Monitoring Serial Port in Windows using C使用 C 在 Windows 中被动监控串行端口
【发布时间】:2020-06-18 18:40:04
【问题描述】:

我是串行编程的菜鸟。我正在尝试用 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");
    }

}

【问题讨论】:

  • 如果您想监控其他程序正在生成的串行流量,您需要以非独占模式打开端口 - 即允许共享(并且在其他程序打开端口之前这样做因为它很可能在禁用共享的情况下打开它)。将 dwShareMode 参数的 FILE_SHARE_READ|FILE_SHARE_WRITE 传递给 CreateFile。
  • com0com 伴随着 hub4com,可以将流量“路由”到多个目的地。几年前,我将它与多个 com0com 设备一起使用来监控串行通信。先画一张图,这样你就知道你需要什么了。
  • @SornelHaetir,感谢您提供的信息。我试过了,但没有用。
  • @thebusybee,我使用 com0com 作为虚拟端口只是因为我没有硬件端口,但是我创建的代码本质上必须是“本机”的,因此没有来自 com0com 的库。

标签: c serial-port com-port


【解决方案1】:

您的代码应该可以正常工作(编辑:,只要您打算将它与 com0com 一起使用)。正如上面评论中的busybee所建议的那样,我认为您混淆了端口或误解了 com0com 应该如何工作。

您可能有两种不同的情况:

1)您正在使用您的 Windows PC 作为嗅探器来监控 另外两个方之间的 Modbus 交易。例如 PLC 和远程 Modbus 传感器。在这种情况下,您需要两个真实的硬件串口和两个 com0com 提供的虚拟端口。

2)如果您的计算机中的某物充当 Modbus 事务中的一方,那么您只需要一个硬件串行端口和几个虚拟端口。

既然你提到了被动,我猜你是在第 1 种情况下。如果是这样,你只需要正确选择你的端口。我写了一个关于如何做到这一点的完整示例,巧合的是,Modbus 也使用 Termite 和 com0com,看看here。您可能还想看看SerialPCAP,它与 Wireshark 结合使用甚至可以解码您的 Modbus 消息。

如果您更喜欢重新发明轮子,我想您可以放弃 com0com 并按照 cmets 中其他人的建议共享端口。如果您决定走这条路,可能需要阅读一些有趣的问题,请参阅here

编辑:你说你确实想重新发明轮子。这很好,但我认为你需要在开始编写代码之前考虑一些事情。我不是专业的串口开发人员;在 Windows 上要少得多,在最近的 Windows 版本上要少得多。但我早在很久以前就对这个话题做了一些研究,所以我可以给你我的看法:

-我们大多数非轮式改造者都非常乐意使用上面解释的虚拟串行端口技术监控我们的串行端口(我将再重复一次:对于 Modbus RTU 流量监控,请查看 Wireshark/SerialPCAP 和您会忘记其他任何事情)。我的第一印象是您想这样做(这就是您谈论 com0com 的原因)。阅读您的 cmets,我想这对您来说还不够好(我可以理解,我更喜欢干净的解决方案而不是肮脏的技巧)。

-现在,清楚了,有什么可以做的吗?从用户空间来看,我认为您现在无法共享串行端口。对您的问题的评论中提到dwShareMode 的技巧可能在 90 年代就起作用了,但恐怕它不再起作用了。更多详情请见here

-如果你去司机区,你可能会有一些机会。阅读here。其他有用的链接:1,2

我的结论是:你的代码没有修复,你想做的比你拥有的更复杂。

【讨论】:

  • 感谢您的信息。我正在尝试重新发明轮子。我正在考虑在完成后将其移植到具有实际硬件的设备上,以读取进出该端口的内容。到目前为止,我的建议仍然没有成功。非常感谢。
  • 但是……这个其他设备是什么?如果它运行 Windows,则可以按照您的路线进行操作。如果没有,你就走错了路。
  • 我完全不反对重新发明轮子,我想有时我们中的一些人会为此付出代价。无论如何,请注意,这个问题显然是针对 Windows 的(它提到了 com0com,并且包含一个 windows.h),这就是为什么我为 Windows 链接了特定的解决方案。特别是使用 SerialPCAP 的那个,在 Linux 上运行得非常好,一个小团队需要几个月的时间才能想出类似的东西(用所有信息解码 Modbus 帧)。毫无疑问,只要有时间和资源,你也能做到。
  • 我会看看我的笔记和图书馆,我想我可能有一些有用的链接。对于Linux,顺便说一句,解决这个问题的方法还有很多;例如,参见this one
猜你喜欢
  • 2014-09-24
  • 2011-07-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-13
  • 1970-01-01
  • 2016-10-26
  • 1970-01-01
  • 2010-10-17
相关资源
最近更新 更多