使用 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.

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