简介

  • windows环境下C++编程遇到的函数

windows C++ WSAStartup()函数 详解

WSAStartup() 是 Windows Sockets API(也称为 Winsock)中用于初始化 Windows Sockets 库的函数。该函数在使用任何其他 Windows Sockets 函数之前必须调用,用于设置程序对网络通信的支持。

函数原型

1
2
3
4
int WSAStartup(
    WORD wVersionRequested,  // 请求的 Winsock 版本
    LPWSADATA lpWSAData      // 指向 WSADATA 结构的指针,用于接收系统信息
);
  • 参数
    • wVersionRequested:指定应用程序请求的 Winsock 版本。该参数由高字节和低字节组成,例如,MAKEWORD(2, 2) 表示请求 Winsock 2.2 版本。
    • lpWSAData:指向一个 WSADATA 结构的指针,用于接收有关 Windows Sockets 实现的详细信息。
  • 返回值
    • 如果函数调用成功,返回值为零 (0)。
    • 如果函数调用失败,返回一个非零的错误代码。常见的错误代码包括 WSASYSNOTREADY(底层网络子系统不可用)和 WSAVERNOTSUPPORTED(请求的 Winsock 版本不受支持)。

使用说明

  1. 版本管理
    • 在调用 WSAStartup() 时,必须指定应用程序希望使用的 Winsock 版本。最常用的是 2.2 版本 (MAKEWORD(2, 2)),因为它支持大多数现代网络应用程序的需求。
    • 如果系统支持请求的版本,WSAStartup() 会返回该版本的详细信息。如果系统不支持请求的版本,则返回较低的版本信息,或者函数调用失败。
  2. WSADATA 结构
    • lpWSAData 参数指向的 WSADATA 结构用于接收 Winsock 的相关信息。该结构包含了 Winsock 版本、最大套接字数等重要信息。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    typedef struct WSAData {
        WORD           wVersion;       // Winsock 版本
        WORD           wHighVersion;   // 最高支持的 Winsock 版本
        char           szDescription[WSADESCRIPTION_LEN + 1];  // 实现描述
        char           szSystemStatus[WSASYSSTATUS_LEN + 1];   // 系统状态
        unsigned short iMaxSockets;    // 支持的最大套接字数
        unsigned short iMaxUdpDg;      // 最大 UDP 数据报长度
        char FAR*      lpVendorInfo;   // 供应商特定信息
    } WSADATA, *LPWSADATA;
    
  3. 清理
    • 当应用程序不再需要使用 Windows Sockets API 时,应该调用 WSACleanup() 函数来卸载 Winsock 库并释放相关资源。
  4. 错误处理
    • 如果 WSAStartup() 返回非零值,表明初始化失败,应用程序应检查返回值并通过 WSAGetLastError() 函数获取详细的错误信息。

使用示例

以下是一个简单的示例,展示如何正确使用 WSAStartup() 函数来初始化 Winsock 库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <winsock2.h>
#include <iostream>

int main() {
    WSADATA wsaData;

    // 初始化 Winsock 2.2 版本
    int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (result != 0) {
        std::cerr << "WSAStartup failed with error: " << result << std::endl;
        return 1;
    }

    std::cout << "Winsock initialized successfully." << std::endl;
    std::cout << "Winsock version: " << LOBYTE(wsaData.wVersion) << "." << HIBYTE(wsaData.wVersion) << std::endl;
    std::cout << "Description: " << wsaData.szDescription << std::endl;

    // 在此处可以编写网络通信相关代码...

    // 清理 Winsock
    WSACleanup();
    return 0;
}

在这个示例中:

  • WSAStartup() 函数用于初始化 Winsock 库,请求使用 Winsock 2.2 版本。
  • 如果初始化成功,程序会输出 Winsock 的版本信息和描述。
  • 当程序不再需要使用网络功能时,调用 WSACleanup() 函数来清理 Winsock 资源。

WSAStartup() 是任何使用 Windows Sockets API 进行网络编程的应用程序中的必备步骤。初始化成功后,您就可以使用其他 Winsock 函数来执行各种网络操作,例如创建套接字、连接到服务器、发送和接收数据等。

windows C++ GetCurrentThreadId()函数 详解

GetCurrentThreadId() 是 Windows API 中的一个函数,用于获取当前线程的唯一标识符(线程 ID)。在多线程编程中,每个线程都有一个唯一的 ID,可以通过该函数获取,便于在线程间进行标识和管理。

函数原型

1
DWORD GetCurrentThreadId(void);
  • 参数
    • void:该函数不接受任何参数。
  • 返回值
    • 返回当前调用线程的 DWORD 类型的线程 ID。线程 ID 是一个系统分配的数字,用于唯一标识当前线程。

使用说明

  1. 线程 ID 的唯一性
    • 每个线程在其生命周期内都有一个唯一的线程 ID。当线程终止时,该 ID 可能会被系统回收并分配给新的线程。
  2. 典型用法
    • GetCurrentThreadId() 常用于调试、日志记录、线程间通信或同步等场景中。例如,可以使用线程 ID 来标记日志消息,便于区分不同线程的输出。
    • 线程 ID 也可以用于将线程与某些特定的资源(如窗口、数据结构)关联起来。
  3. 与其他 API 的关系
    • 线程 ID 与线程句柄不同,线程句柄通过 CreateThreadOpenThread 等函数获取,而线程 ID 则是一个直接标识线程的数字。
    • 通过 OpenThread() 函数可以将线程 ID 转换为线程句柄,便于进行更复杂的线程操作。
  4. 注意事项
    • 线程 ID 是系统分配的,不应直接作为关键数据或资源的唯一标识,因为它们在特定条件下可能被重复使用。

使用示例

以下是一个简单的示例,演示如何使用 GetCurrentThreadId() 获取并打印当前线程的 ID。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <windows.h>
#include <iostream>

// 线程函数
DWORD WINAPI ThreadProc(LPVOID lpParam) {
    DWORD threadId = GetCurrentThreadId();
    std::cout << "Thread ID: " << threadId << " is running." << std::endl;
    Sleep(2000); // 模拟工作
    std::cout << "Thread ID: " << threadId << " is exiting." << std::endl;
    return 0;
}

int main() {
    // 创建两个线程
    HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

    // 等待线程完成
    WaitForSingleObject(hThread1, INFINITE);
    WaitForSingleObject(hThread2, INFINITE);

    // 关闭线程句柄
    CloseHandle(hThread1);
    CloseHandle(hThread2);

    return 0;
}

在这个示例中:

  • ThreadProc 是线程的执行函数,每个线程在启动后都会执行该函数。
  • 每个线程都会调用 GetCurrentThreadId() 来获取并打印其自身的线程 ID。
  • 主线程创建了两个子线程,并等待它们完成执行。
  • 通过线程 ID,您可以在日志或调试信息中区分不同线程的行为。

GetCurrentThreadId() 是多线程编程中的一个基本工具,能够帮助开发者识别和管理不同的线程。

windows C++ WaitForSingleObject()函数 详解

WaitForSingleObject() 是 Windows API 中用于同步操作的函数。它用于使调用线程等待一个内核对象(如线程、进程、信号量、事件等)变为有信号状态,或者等待超时。该函数经常用于多线程编程中,确保线程之间的协调与同步。

函数原型

1
2
3
4
DWORD WaitForSingleObject(
    HANDLE hHandle,   // 内核对象的句柄
    DWORD  dwMilliseconds // 等待的时间(毫秒)
);
  • 参数
    • hHandle:要等待的内核对象的句柄。这个句柄可以是由 CreateEventCreateMutexCreateSemaphoreCreateThread 等函数返回的句柄。
    • dwMilliseconds:指定等待的时间,单位为毫秒。可以是以下值之一:
      • INFINITE:表示无限等待,直到对象变为有信号状态。
      • 非零值:指定最大等待时间(毫秒)。如果在指定时间内对象没有变为有信号状态,函数会返回 WAIT_TIMEOUT
      • 0:表示立即返回,不等待。如果对象已经是有信号状态,函数立即返回;否则,函数立即返回 WAIT_TIMEOUT
  • 返回值
    • WAIT_OBJECT_0 (0x00000000L):指定的对象变为有信号状态。
    • WAIT_TIMEOUT (0x00000102L):等待超时,指定的对象未变为有信号状态。
    • WAIT_ABANDONED (0x00000080L):等待的对象是一个互斥体对象,且上一个拥有该互斥体的线程在没有释放互斥体的情况下终止。表示互斥体已被“放弃”。
    • WAIT_FAILED:函数调用失败。可以通过 GetLastError() 获取错误代码。

使用说明

  1. 同步操作
    • WaitForSingleObject() 主要用于线程同步,确保某个线程在执行某些操作之前等待另一个线程或进程完成其工作。
  2. 常见应用场景
    • 等待线程或进程终止:通过等待线程或进程的句柄,确保主线程在子线程或子进程完成后再继续执行。
    • 事件同步:通过等待事件对象,控制多个线程的执行顺序。
    • 互斥体和信号量:通过等待这些对象,控制对共享资源的访问。
  3. 注意事项
    • 如果使用 INFINITE 作为等待时间,线程将无限期地等待,直到对象变为有信号状态,这可能导致线程挂起,无法继续执行。
    • 对于互斥体,使用 WAIT_ABANDONED 返回值表示该互斥体对象被上一个线程错误地放弃,此时程序应小心处理共享资源的状态。

使用示例

下面是一个使用 WaitForSingleObject() 的简单示例,演示如何等待一个线程完成执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <windows.h>
#include <iostream>

// 线程函数
DWORD WINAPI ThreadProc(LPVOID lpParam) {
    std::cout << "Thread is running..." << std::endl;
    Sleep(3000); // 模拟线程工作 3 秒钟
    std::cout << "Thread is exiting..." << std::endl;
    return 0;
}

int main() {
    // 创建线程
    HANDLE hThread = CreateThread(
        NULL,        // 默认安全属性
        0,           // 默认堆栈大小
        ThreadProc,  // 线程函数
        NULL,        // 线程函数的参数
        0,           // 默认创建标志
        NULL         // 不接收线程 ID
    );

    if (hThread == NULL) {
        std::cerr << "Failed to create thread. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // 等待线程完成
    DWORD dwResult = WaitForSingleObject(hThread, INFINITE);
    switch (dwResult) {
        case WAIT_OBJECT_0:
            std::cout << "Thread has terminated." << std::endl;
            break;
        case WAIT_TIMEOUT:
            std::cerr << "Wait timed out." << std::endl;
            break;
        case WAIT_FAILED:
            std::cerr << "Wait failed. Error: " << GetLastError() << std::endl;
            break;
    }

    // 关闭线程句柄
    CloseHandle(hThread);

    return 0;
}

在这个示例中:

  • CreateThread() 函数创建了一个新线程,执行 ThreadProc 函数。
  • 主线程使用 WaitForSingleObject() 来等待子线程完成执行。因为等待时间设置为 INFINITE,主线程会一直等待,直到子线程终止。
  • 通过 dwResult 检查 WaitForSingleObject() 的返回值,决定接下来的操作。

WaitForSingleObject() 是多线程编程中非常重要的一个工具,能有效管理线程间的执行顺序和资源访问控制。

windows C++ GetLastError()函数 详解

GetLastError() 是 Windows API 中用于获取调用失败的函数返回的错误代码的函数。许多 Windows API 函数在执行失败时,不会直接返回错误信息,而是通过设置一个内部的线程局部变量来记录错误代码。调用 GetLastError() 函数可以检索到这个错误代码,用于诊断和处理错误情况。

函数原型

1
DWORD GetLastError(void);
  • 返回值
    • 返回一个 DWORD 类型的错误代码。这个错误代码是一个整数值,对应特定的错误类型。

使用说明

  1. 线程局部存储
    • GetLastError() 返回的错误代码与调用线程是关联的,即每个线程都有自己的错误代码存储区。因此,如果多线程程序中某个线程调用 GetLastError(),它获取到的错误代码仅适用于该线程的上下文。
  2. 使用场景
    • 当调用 Windows API 函数时,如果函数返回了一个失败的状态(例如返回 NULLINVALID_HANDLE_VALUE),通常应该紧接着调用 GetLastError() 来获取具体的错误代码。这有助于诊断失败的原因并采取相应措施。
  3. FormatMessage() 配合使用
    • 错误代码本身是一个数字,通常难以直接理解。可以使用 FormatMessage() 函数将错误代码转换为可读的错误消息字符串。
  4. 清除错误代码
    • GetLastError() 只会返回最近一次失败的函数调用的错误代码。对于成功的函数调用,错误代码不会被清除。因此,在进行新的操作之前,如果想确保没有残留的错误代码,可以先调用 SetLastError(0) 清除错误状态。

常见错误代码

以下是一些常见的错误代码及其含义:

  • ERROR_SUCCESS (0):操作成功。
  • ERROR_FILE_NOT_FOUND (2):系统找不到指定的文件。
  • ERROR_ACCESS_DENIED (5):拒绝访问。
  • ERROR_INVALID_HANDLE (6):句柄无效。
  • ERROR_NOT_ENOUGH_MEMORY (8):内存不足,无法完成此操作。
  • ERROR_INVALID_PARAMETER (87):参数错误。
  • ERROR_INSUFFICIENT_BUFFER (122):缓冲区大小不足。

完整的错误代码列表可以在微软文档中找到。

使用示例

以下是一个简单的示例,演示如何使用 GetLastError() 获取错误代码并显示相应的错误消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <windows.h>
#include <iostream>

int main() {
    // 尝试打开一个不存在的文件
    HANDLE hFile = CreateFile(
        L"nonexistent_file.txt", // 文件名
        GENERIC_READ,            // 访问模式
        0,                       // 共享模式
        NULL,                    // 安全属性
        OPEN_EXISTING,           // 如何创建
        FILE_ATTRIBUTE_NORMAL,   // 文件属性
        NULL                     // 模板文件句柄
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        DWORD dwError = GetLastError();  // 获取错误代码
        std::cerr << "Failed to open file. Error code: " << dwError << std::endl;

        // 使用 FormatMessage 获取错误信息
        LPVOID lpMsgBuf;
        FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            dwError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPWSTR)&lpMsgBuf,
            0,
            NULL
        );

        std::wcerr << "Error message: " << (LPWSTR)lpMsgBuf << std::endl;

        // 释放 FormatMessage 分配的缓冲区
        LocalFree(lpMsgBuf);
    } else {
        std::cout << "File opened successfully." << std::endl;
        CloseHandle(hFile);  // 关闭文件句柄
    }

    return 0;
}

在这个示例中:

  • 尝试打开一个不存在的文件 nonexistent_file.txt
  • CreateFile() 调用失败并返回 INVALID_HANDLE_VALUE,表示操作失败。
  • 使用 GetLastError() 获取具体的错误代码,并使用 FormatMessage() 将错误代码转换为可读的错误消息。

这个函数对于调试和错误处理非常重要,可以帮助开发者准确定位问题所在。

windows C++ ClearCommError()函数 详解

ClearCommError() 是 Windows API 中用于处理通信端口(如串口)错误的函数。它用于获取通信设备的错误信息,并可以清除通信设备的错误状态。这个函数通常用于串口通信程序中,用来检查和处理通信异常情况。

函数原型

1
2
3
4
5
BOOL ClearCommError(
    HANDLE hFile,           // 通信设备句柄
    LPDWORD lpErrors,       // 指向一个变量,该变量接收设备错误信息
    LPCOMSTAT lpStat        // 指向 COMSTAT 结构,该结构接收通信状态信息
);
  • 参数
    • hFile:通信设备的句柄。通常通过 CreateFile() 函数获取,用于表示串口设备(如 COM1, COM2)。
    • lpErrors:指向一个 DWORD 变量的指针,用于接收通信设备的错误状态信息。这个参数可以为 NULL,如果不需要获取错误信息。
    • lpStat:指向一个 COMSTAT 结构的指针,该结构接收设备的通信状态信息。这个参数可以为 NULL,如果不需要获取通信状态信息。
  • 返回值
    • 如果函数调用成功,返回值为非零值 (TRUE)。
    • 如果函数调用失败,返回值为零 (FALSE)。可以通过调用 GetLastError() 函数获取详细的错误信息。

错误状态

lpErrors 参数接收的错误状态是一个或多个以下值的组合:

  • CE_BREAK:接收到中断信号。
  • CE_FRAME:硬件检测到帧错误。
  • CE_OVERRUN:输入缓冲区溢出。数据丢失。
  • CE_RXOVER:输入缓冲区溢出,字符被丢弃。
  • CE_RXPARITY:接收到的字符有奇偶校验错误。
  • CE_TXFULL:应用程序试图传输字符时,输出缓冲区已满。

COMSTAT 结构

lpStat 参数指向的 COMSTAT 结构,用于获取通信设备的状态信息。该结构包括如下成员:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct _COMSTAT {
  DWORD fCtsHold : 1;      // CTS (Clear To Send) 信号被保持
  DWORD fDsrHold : 1;      // DSR (Data Set Ready) 信号被保持
  DWORD fRlsdHold : 1;     // RLSD (Receive Line Signal Detect) 信号被保持
  DWORD fXoffHold : 1;     // XOFF 被保持
  DWORD fXoffSent : 1;     // 已发送 XOFF
  DWORD fEof : 1;          // 已接收到 EOF
  DWORD fTxim : 1;         // 传输缓冲区被空中断
  DWORD fReserved : 25;    // 保留
  DWORD cbInQue;           // 输入缓冲区中的字节数
  DWORD cbOutQue;          // 输出缓冲区中的字节数
} COMSTAT, *LPCOMSTAT;

使用示例

下面是一个使用 ClearCommError() 函数的简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <windows.h>
#include <iostream>

int main() {
    // 打开串口 (COM1)
    HANDLE hComm = CreateFile(
        L"COM1",                     // 设备名
        GENERIC_READ | GENERIC_WRITE, // 访问模式
        0,                            // 共享模式
        NULL,                         // 安全属性
        OPEN_EXISTING,                // 打开已存在的设备
        0,                            // 文件属性
        NULL                          // 模板文件句柄
    );

    if (hComm == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open COM1. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // 检查并清除错误
    DWORD dwErrors;
    COMSTAT comStat;
    if (ClearCommError(hComm, &dwErrors, &comStat)) {
        if (dwErrors != 0) {
            std::cerr << "Communication error occurred: " << dwErrors << std::endl;
        } else {
            std::cout << "No errors. Bytes in queue: " << comStat.cbInQue << std::endl;
        }
    } else {
        std::cerr << "Failed to clear communication error. Error: " << GetLastError() << std::endl;
    }

    // 关闭串口
    CloseHandle(hComm);
    return 0;
}

在这个示例中:

  • CreateFile() 用于打开串口设备 COM1
  • ClearCommError() 用于检查通信错误,并获取当前通信状态。
  • 如果有错误发生,dwErrors 变量将包含具体的错误代码。
  • comStat 结构提供有关输入和输出缓冲区状态的信息。

这个函数在串口通信程序中非常有用,可以帮助开发者处理通信中的异常情况,如数据丢失、缓冲区溢出等问题。

windows C++ CloseHandle()函数 详解

CloseHandle() 是 Windows API 中用于关闭内核对象句柄的函数。它是 Windows 操作系统中资源管理的一部分,用于释放进程中占用的系统资源。

函数原型

1
BOOL CloseHandle(HANDLE hObject);
  • 参数
    • hObject:需要关闭的句柄。这个句柄可以是打开的文件、线程、进程、信号量、文件映射对象、互斥体等内核对象。
  • 返回值
    • 如果函数调用成功,返回值为非零值 (TRUE)。
    • 如果函数调用失败,返回值为零 (FALSE)。可以通过调用 GetLastError() 函数获取详细的错误信息。

使用说明

  1. 资源管理
    • 在 Windows 操作系统中,许多资源(如文件、进程、线程等)都是通过句柄来管理的。每当你创建或打开这些资源时,系统都会分配一个句柄。当不再需要这些资源时,必须调用 CloseHandle() 来释放句柄,否则会导致资源泄漏。
  2. 句柄类型
    • CloseHandle() 可以用于关闭多种类型的句柄,例如文件句柄、线程句柄、进程句柄、互斥体句柄、事件对象句柄等。需要确保关闭正确的句柄类型,以避免程序异常。
  3. 多次调用
    • 对同一个句柄多次调用 CloseHandle() 是错误的行为。这将导致未定义的行为,可能会引发程序崩溃或其他严重的错误。因此,调用 CloseHandle() 后,不应再使用这个句柄。
  4. 系统资源的自动释放
    • 当进程终止时,系统会自动关闭该进程中所有打开的句柄。但依赖于系统自动关闭句柄通常不是一个好的实践,程序应该显式地调用 CloseHandle() 来关闭不再需要的句柄。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <windows.h>
#include <iostream>

int main() {
    // 打开一个文件
    HANDLE hFile = CreateFile(
        L"example.txt",            // 文件名
        GENERIC_READ,              // 访问模式
        0,                         // 共享模式
        NULL,                      // 安全属性
        OPEN_EXISTING,             // 如何创建
        FILE_ATTRIBUTE_NORMAL,     // 文件属性
        NULL);                     // 模板文件句柄
    
    if (hFile == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open file. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // 执行文件操作...

    // 关闭文件句柄
    if (CloseHandle(hFile)) {
        std::cout << "File handle closed successfully." << std::endl;
    } else {
        std::cerr << "Failed to close file handle. Error: " << GetLastError() << std::endl;
    }

    return 0;
}

在这个示例中,CreateFile() 函数用于打开一个文件,并返回一个文件句柄。然后,使用 CloseHandle() 函数关闭这个文件句柄,释放相关资源。

windows C++ PurgeComm()函数 详解

PurgeComm 函数是 Windows API 中用于清除串口通信设备的输入或输出缓冲区的函数。它可以有效地清除缓冲区中的数据以及挂起的输入或输出请求,确保串口通信处于已知状态。这在处理通信错误或重置串口设备时非常有用。

函数原型

1
2
3
4
BOOL PurgeComm(
  HANDLE hFile,
  DWORD  dwFlags
);

参数详解

  1. hFile
    • 类型:HANDLE
    • 描述:这是一个串口设备的句柄,通常由 CreateFile 函数获得,代表一个打开的串口通信端口(如 "COM1")。
  2. dwFlags
    • 类型:DWORD
    • 描述:指定要清除的缓冲区或挂起的操作的标志。可以是以下值的组合:

    • PURGE_RXABORT (0x0002): 终止所有挂起的读取操作。未完成的读取操作将失败。
    • PURGE_RXCLEAR (0x0008): 清除接收缓冲区中的数据。
    • PURGE_TXABORT (0x0001): 终止所有挂起的写入操作。未完成的写入操作将失败。
    • PURGE_TXCLEAR (0x0004): 清除发送缓冲区中的数据。

    这些标志可以通过按位或 (|) 组合使用,例如 PURGE_RXABORT | PURGE_TXCLEAR

返回值

  • 成功:如果函数执行成功,返回 TRUE,表示缓冲区已被成功清除。
  • 失败:如果函数执行失败,返回 FALSE,可以通过调用 GetLastError() 来获取更多错误信息。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <windows.h>
#include <iostream>

int main() {
    // 打开串口
    HANDLE hSerial = CreateFile(
        "COM1",                        // 串口名称
        GENERIC_READ | GENERIC_WRITE,  // 读写权限
        0,                             // 独占访问
        NULL,                          // 默认安全属性
        OPEN_EXISTING,                 // 打开现有串口
        0,                             // 属性标志
        NULL                           // 不使用模板文件
    );

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open COM port. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // 清除接收缓冲区和终止所有挂起的读取操作
    if (!PurgeComm(hSerial, PURGE_RXCLEAR | PURGE_RXABORT)) {
        std::cerr << "Failed to purge COM port. Error: " << GetLastError() << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    std::cout << "COM port purged successfully." << std::endl;

    // 进行其他串口通信操作...
    
    // 关闭串口
    CloseHandle(hSerial);
    return 0;
}

解释示例中 PurgeComm 的使用

在上面的例子中,我们首先打开了 COM1 串口。接着,我们使用 PurgeComm 函数清除了接收缓冲区 (PURGE_RXCLEAR) 并终止了所有挂起的读取操作 (PURGE_RXABORT)。这有助于在进行进一步的串口操作之前,确保没有未处理的旧数据或挂起的操作。

典型用法场景

  • 处理通信错误:在检测到通信错误后,可以使用 PurgeComm 清除串口缓冲区,以便重新开始通信。
  • 重置串口状态:当需要重置串口状态时,可以清除所有挂起的操作和缓冲区内容,确保通信的稳定性。
  • 同步操作:当程序需要与设备重新同步时,可以通过清除接收缓冲区来忽略不完整或意外的输入。

注意事项

  • 挂起操作的影响:使用 PURGE_RXABORTPURGE_TXABORT 标志会导致挂起的读取或写入操作失败,并返回错误。使用这些标志时需要确保程序能够正确处理这些失败的操作。
  • 数据丢失:清除缓冲区(使用 PURGE_RXCLEARPURGE_TXCLEAR)会导致缓冲区中的数据丢失。因此,调用 PurgeComm 函数之前应确保缓冲区中的数据已被处理或不再需要。

常见错误

  • ERROR_INVALID_HANDLE:无效的句柄,可能是因为串口未成功打开或句柄已关闭。
  • ERROR_IO_PENDING:有未完成的 I/O 操作。这通常表示在尝试清除缓冲区时,有未处理完的操作。

PurgeComm 是串口通信中一个重要的维护工具,特别是在需要处理错误、重置通信状态或确保系统处于已知状态时。通过正确使用该函数,可以提高串口通信的稳定性和可靠性。

windows C++ SetCommTimeouts()函数 详解

SetCommTimeouts 函数是 Windows API 中用于设置串口通信设备的超时时间的函数。它允许你定义串口设备在读取和写入操作时的超时行为,这是确保串口通信可靠性的重要一步。

函数原型

1
2
3
4
BOOL SetCommTimeouts(
  HANDLE hFile,
  LPCOMMTIMEOUTS lpCommTimeouts
);

参数详解

  1. hFile
    • 类型:HANDLE
    • 描述:这是一个串口设备的句柄,通常由 CreateFile 函数获得,代表一个打开的串口通信端口(如 "COM1")。
  2. lpCommTimeouts
    • 类型:LPCOMMTIMEOUTS
    • 描述:指向 COMMTIMEOUTS 结构的指针,该结构包含了设备输入输出操作的超时设置。

返回值

  • 成功:如果函数执行成功,返回 TRUE,表示串口设备的超时设置已被成功应用。
  • 失败:如果函数执行失败,返回 FALSE,可以通过调用 GetLastError() 来获取更多错误信息。

COMMTIMEOUTS 结构体

COMMTIMEOUTS 结构体定义了串口设备读写操作的超时设置。结构体定义如下:

1
2
3
4
5
6
7
typedef struct _COMMTIMEOUTS {
  DWORD ReadIntervalTimeout;         // 读取字符间隔超时(毫秒)
  DWORD ReadTotalTimeoutMultiplier;  // 总读取超时乘子
  DWORD ReadTotalTimeoutConstant;    // 总读取超时常量(毫秒)
  DWORD WriteTotalTimeoutMultiplier; // 总写入超时乘子
  DWORD WriteTotalTimeoutConstant;   // 总写入超时常量(毫秒)
} COMMTIMEOUTS, *LPCOMMTIMEOUTS;

结构体字段详解

  1. ReadIntervalTimeout
    • 描述:指定两次字符读取之间的最大间隔时间。如果超出此时间,读取操作将完成。以毫秒为单位。
    • 特殊值:
      • MAXDWORD:表示非零值的超时时间无效,系统返回立即可用的数据,而不等待进一步的数据输入。
  2. ReadTotalTimeoutMultiplier
    • 描述:指定读取操作的超时乘子。实际的超时为乘子乘以读取的字符数。
  3. ReadTotalTimeoutConstant
    • 描述:指定读取操作的总超时常量。该值加上 ReadTotalTimeoutMultiplier 的结果为总读取超时时间。
  4. WriteTotalTimeoutMultiplier
    • 描述:指定写入操作的超时乘子。实际的超时为乘子乘以写入的字符数。
  5. WriteTotalTimeoutConstant
    • 描述:指定写入操作的总超时常量。该值加上 WriteTotalTimeoutMultiplier 的结果为总写入超时时间。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <windows.h>
#include <iostream>

int main() {
    // 打开串口
    HANDLE hSerial = CreateFile(
        "COM1",                        // 串口名称
        GENERIC_READ | GENERIC_WRITE,  // 读写权限
        0,                             // 独占访问
        NULL,                          // 默认安全属性
        OPEN_EXISTING,                 // 打开现有串口
        0,                             // 属性标志
        NULL                           // 不使用模板文件
    );

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open COM port. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // 设置串口超时参数
    COMMTIMEOUTS timeouts = { 0 };
    timeouts.ReadIntervalTimeout = 50;          // 50ms 的字符间隔超时
    timeouts.ReadTotalTimeoutMultiplier = 10;   // 每个字符的读取时间为 10ms
    timeouts.ReadTotalTimeoutConstant = 100;    // 总读取操作的附加时间为 100ms
    timeouts.WriteTotalTimeoutMultiplier = 10;  // 每个字符的写入时间为 10ms
    timeouts.WriteTotalTimeoutConstant = 100;   // 总写入操作的附加时间为 100ms

    if (!SetCommTimeouts(hSerial, &timeouts)) {
        std::cerr << "Failed to set COM port timeouts. Error: " << GetLastError() << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    std::cout << "COM port timeouts configured successfully." << std::endl;

    // 进行其他串口通信操作...
    
    // 关闭串口
    CloseHandle(hSerial);
    return 0;
}

解释示例中超时设置的逻辑

  • 读取超时
    • ReadIntervalTimeout = 50:如果两次字符读取之间的间隔超过 50 毫秒,读取操作将结束。
    • ReadTotalTimeoutMultiplier = 10:对于每个要读取的字符,设置 10 毫秒的超时。
    • ReadTotalTimeoutConstant = 100:总读取超时常量为 100 毫秒。

    例如,如果要读取 5 个字符,总读取超时时间为:(5 * 10) + 100 = 150 毫秒。

  • 写入超时
    • WriteTotalTimeoutMultiplier = 10:对于每个要写入的字符,设置 10 毫秒的超时。
    • WriteTotalTimeoutConstant = 100:总写入超时常量为 100 毫秒。

    例如,如果要写入 5 个字符,总写入超时时间为:(5 * 10) + 100 = 150 毫秒。

注意事项

  • 超时的适应性:设置的超时应根据实际应用的需要进行调整。如果超时设置得过短,可能会导致读取或写入操作过早地结束;而如果超时设置得过长,则可能会导致应用程序响应迟缓。
  • 特殊情况:如果串口通信中需要实时处理(如工业控制),则超时设置要特别小心,确保在通信故障时系统能够快速响应。

常见错误

  • ERROR_INVALID_HANDLE:无效的句柄,可能是因为串口未成功打开或句柄已关闭。
  • ERROR_INVALID_PARAMETER:传递给 SetCommTimeouts 的参数无效,可能是 COMMTIMEOUTS 结构体中的字段值不合理。

SetCommTimeouts 函数是配置串口通信设备超时的关键函数,通过合理设置,可以确保串口通信的有效性和可靠性,避免因超时问题导致的通信失败。

windows C++ SetCommState()函数 详解

SetCommState 函数是 Windows API 中用于设置串口设备通信参数的一个函数。它可以修改串口设备的配置,如波特率、数据位、停止位和奇偶校验等。这对于串口通信非常重要,因为需要确保串口设备的设置与通信双方的要求一致。

函数原型

1
2
3
4
BOOL SetCommState(
  HANDLE hFile,
  LPDCB  lpDCB
);

参数详解

  1. hFile
    • 类型:HANDLE
    • 描述:这是一个串口设备的句柄,通常由 CreateFile 函数获得,代表一个打开的串口通信端口(如 "COM1")。
  2. lpDCB
    • 类型:LPDCB
    • 描述:指向 DCB(Device Control Block)结构的指针,该结构包含了串口设备的通信设置。通过 SetCommState 函数,你可以将这些设置应用到串口设备上。

返回值

  • 成功:如果函数执行成功,返回 TRUE,表示串口设备的配置已被成功修改。
  • 失败:如果函数执行失败,返回 FALSE,可以通过调用 GetLastError() 来获取更多错误信息。

DCB 结构体

DCB 结构体保存了串口设备的详细设置,如波特率、数据位、停止位、奇偶校验等。该结构体的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
typedef struct _DCB {
  DWORD DCBlength;       // DCB结构体大小
  DWORD BaudRate;        // 波特率
  DWORD fBinary : 1;     // 二进制模式,必须为 TRUE
  DWORD fParity : 1;     // 启用奇偶校验
  DWORD fOutxCtsFlow : 1;// CTS(清除发送)流控制
  DWORD fOutxDsrFlow : 1;// DSR(数据设置就绪)流控制
  DWORD fDtrControl : 2; // DTR(数据终端就绪)流控制
  DWORD fDsrSensitivity : 1; // DSR敏感性
  DWORD fTXContinueOnXoff : 1; // 在接收到XOFF时继续发送
  DWORD fOutX : 1;       // 启用XON/XOFF发送控制
  DWORD fInX : 1;        // 启用XON/XOFF接收控制
  DWORD fErrorChar : 1;  // 启用错误字符替换
  DWORD fNull : 1;       // 启用空字节丢弃
  DWORD fRtsControl : 2; // RTS(请求发送)流控制
  DWORD fAbortOnError : 1; // 发生错误时中止所有读写操作
  DWORD fDummy2 : 17;    // 保留
  WORD  wReserved;       // 保留
  WORD  XonLim;          // 传输XON字符之前输入缓冲区中最少的字节数
  WORD  XoffLim;         // 传输XOFF字符之前输入缓冲区中最多的字节数
  BYTE  ByteSize;        // 数据位数(4-8)
  BYTE  Parity;          // 奇偶校验设置(0-4 = 无,奇,偶,标记,空格)
  BYTE  StopBits;        // 停止位数(0,1,2 = 1位,1.5位,2位)
  char  XonChar;         // XON字符
  char  XoffChar;        // XOFF字符
  char  ErrorChar;       // 错误字符(如果fErrorChar为TRUE)
  char  EofChar;         // 文件结束字符
  char  EvtChar;         // 事件字符
  WORD  wReserved1;      // 保留
} DCB, *LPDCB;

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <windows.h>
#include <iostream>

int main() {
    // 打开串口
    HANDLE hSerial = CreateFile(
        "COM1",                        // 串口名称
        GENERIC_READ | GENERIC_WRITE,  // 读写权限
        0,                             // 独占访问
        NULL,                          // 默认安全属性
        OPEN_EXISTING,                 // 打开现有串口
        0,                             // 属性标志
        NULL                           // 不使用模板文件
    );

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open COM port. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // 获取当前串口状态
    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

    if (!GetCommState(hSerial, &dcbSerialParams)) {
        std::cerr << "Failed to get COM port state. Error: " << GetLastError() << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    // 配置串口参数
    dcbSerialParams.BaudRate = CBR_9600;   // 设置波特率为9600
    dcbSerialParams.ByteSize = 8;          // 设置数据位为8
    dcbSerialParams.StopBits = ONESTOPBIT; // 设置停止位为1
    dcbSerialParams.Parity   = NOPARITY;   // 设置无奇偶校验

    // 设置串口状态
    if (!SetCommState(hSerial, &dcbSerialParams)) {
        std::cerr << "Failed to set COM port state. Error: " << GetLastError() << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    std::cout << "COM port configured successfully." << std::endl;

    // 进行其他串口通信操作...
    
    // 关闭串口
    CloseHandle(hSerial);
    return 0;
}

主要字段详解

  • BaudRate:设置串口的波特率(例如 CBR_9600 表示 9600 bps)。
  • ByteSize:设置每个数据包的数据位数,可以是 4 到 8 位。
  • Parity:设置奇偶校验位,常用值包括 NOPARITY (0),ODDPARITY (1),EVENPARITY (2)。
  • StopBits:设置停止位数,常用值为 ONESTOPBIT (0),ONE5STOPBITS (1),TWOSTOPBITS (2)。
  • fBinary:必须设置为 TRUE,表示串口以二进制模式工作。
  • fParity:是否启用奇偶校验。

注意事项

  • 结构体初始化:在调用 SetCommState 之前,确保 DCB 结构体的所有字段都已正确设置,特别是 DCBlength 字段应被设置为 sizeof(DCB)
  • 获取和设置状态:通常在调用 SetCommState 之前,先使用 GetCommState 获取当前串口配置,然后对 DCB 结构体进行修改,并再调用 SetCommState 进行设置。
  • 波特率一致性:确保通信双方使用相同的波特率和其他通信参数,否则会导致通信失败或数据错误。

常见错误

  • ERROR_INVALID_HANDLE:无效的句柄,可能是因为串口未成功打开或句柄已关闭。
  • ERROR_BAD_COMMAND:请求的操作不能被串口设备执行,可能是由于串口不支持特定的配置。
  • ERROR_INVALID_PARAMETER:传递给 SetCommState 的参数无效,可能是 DCB 结构体中的字段值不合理。

SetCommState 是配置串口通信的核心函数,它允许你设置各种串口通信参数,以确保串口设备按照期望的方式工作。

windows C++ GetCommState()函数 详解

GetCommState 函数是 Windows API 中用于获取串口通信设备当前配置的一个函数。它可以获取串口设备的通信参数,包括波特率、数据位、停止位和奇偶校验设置等。

函数原型

1
2
3
4
BOOL GetCommState(
  HANDLE hFile,
  LPDCB  lpDCB
);

参数详解

  1. hFile
    • 类型:HANDLE
    • 描述:这是一个串口设备的句柄,通常由 CreateFile 函数获得,代表一个打开的串口通信端口(如 "COM1")。
  2. lpDCB
    • 类型:LPDCB
    • 描述:指向 DCB 结构的指针,该结构用于存储串口设备的当前配置。DCB 结构保存了串口的详细设置,包括波特率、数据位、停止位、奇偶校验等。

返回值

  • 成功:如果函数执行成功,返回 TRUE,并且 lpDCB 指向的结构体被填充为当前的串口配置。
  • 失败:如果函数执行失败,返回 FALSE,可以通过调用 GetLastError() 来获取更多错误信息。

DCB 结构体

DCB(Device Control Block)结构体包含了串口设备的配置信息。结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
typedef struct _DCB {
  DWORD DCBlength;       // DCB结构体大小
  DWORD BaudRate;        // 波特率
  DWORD fBinary : 1;     // 二进制模式,必须为 TRUE
  DWORD fParity : 1;     // 启用奇偶校验
  DWORD fOutxCtsFlow : 1;// CTS(清除发送)流控制
  DWORD fOutxDsrFlow : 1;// DSR(数据设置就绪)流控制
  DWORD fDtrControl : 2; // DTR(数据终端就绪)流控制
  DWORD fDsrSensitivity : 1; // DSR敏感性
  DWORD fTXContinueOnXoff : 1; // 在接收到XOFF时继续发送
  DWORD fOutX : 1;       // 启用XON/XOFF发送控制
  DWORD fInX : 1;        // 启用XON/XOFF接收控制
  DWORD fErrorChar : 1;  // 启用错误字符替换
  DWORD fNull : 1;       // 启用空字节丢弃
  DWORD fRtsControl : 2; // RTS(请求发送)流控制
  DWORD fAbortOnError : 1; // 发生错误时中止所有读写操作
  DWORD fDummy2 : 17;    // 保留
  WORD  wReserved;       // 保留
  WORD  XonLim;          // 传输XON字符之前输入缓冲区中最少的字节数
  WORD  XoffLim;         // 传输XOFF字符之前输入缓冲区中最多的字节数
  BYTE  ByteSize;        // 数据位数(4-8)
  BYTE  Parity;          // 奇偶校验设置(0-4 = 无,奇,偶,标记,空格)
  BYTE  StopBits;        // 停止位数(0,1,2 = 1位,1.5位,2位)
  char  XonChar;         // XON字符
  char  XoffChar;        // XOFF字符
  char  ErrorChar;       // 错误字符(如果fErrorChar为TRUE)
  char  EofChar;         // 文件结束字符
  char  EvtChar;         // 事件字符
  WORD  wReserved1;      // 保留
} DCB, *LPDCB;

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <windows.h>
#include <iostream>

int main() {
    // 打开串口
    HANDLE hSerial = CreateFile(
        "COM1",                        // 串口名称
        GENERIC_READ | GENERIC_WRITE,  // 读写权限
        0,                             // 独占访问
        NULL,                          // 默认安全属性
        OPEN_EXISTING,                 // 打开现有串口
        0,                             // 属性标志
        NULL                           // 不使用模板文件
    );

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open COM port. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // 获取串口状态
    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

    if (!GetCommState(hSerial, &dcbSerialParams)) {
        std::cerr << "Failed to get COM port state. Error: " << GetLastError() << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    // 输出当前串口配置
    std::cout << "Baud Rate: " << dcbSerialParams.BaudRate << std::endl;
    std::cout << "Byte Size: " << static_cast<int>(dcbSerialParams.ByteSize) << std::endl;
    std::cout << "Parity: " << static_cast<int>(dcbSerialParams.Parity) << std::endl;
    std::cout << "Stop Bits: " << static_cast<int>(dcbSerialParams.StopBits) << std::endl;

    // 关闭串口
    CloseHandle(hSerial);
    return 0;
}

主要字段详解

  • BaudRate:波特率,例如 9600、19200 等。
  • ByteSize:每个字节的数据位数,可以是 4 到 8。
  • Parity:奇偶校验位设置,常用值包括 NOPARITY (0),ODDPARITY (1),EVENPARITY (2)。
  • StopBits:停止位数,常用值为 ONESTOPBIT (0),ONE5STOPBITS (1),TWOSTOPBITS (2)。

注意事项

  • 结构体初始化:在调用 GetCommState 之前,确保 DCB 结构体的 DCBlength 字段已被正确设置为 sizeof(DCB)
  • 获取和设置状态:通常在调用 GetCommState 获取当前配置后,可以使用 SetCommState 修改配置并应用到串口设备上。
  • 设备句柄:确保传递给 GetCommState 的句柄是有效的,通常是通过 CreateFile 成功打开串口设备获得的句柄。

常见错误

  • ERROR_INVALID_HANDLE:无效的句柄,可能是因为串口未成功打开或句柄已关闭。
  • ERROR_BAD_COMMAND:请求的操作不能被串口设备执行,可能是由于串口不支持特定的配置。

GetCommState 函数在串口通信中非常重要,它让你能够读取和理解当前的串口配置,从而确保通信的正确性和稳定性。

windows C++ SetupComm()函数 详解

SetupComm 函数是 Windows API 中用于配置串口设备缓冲区大小的一个函数。它主要用于设置串口通信时的输入和输出缓冲区的大小。这在处理串口通信时非常重要,因为适当配置的缓冲区可以避免数据丢失或溢出。

函数原型

1
2
3
4
5
BOOL SetupComm(
  HANDLE hFile,
  DWORD  dwInQueue,
  DWORD  dwOutQueue
);

参数详解

  1. hFile
    • 类型:HANDLE
    • 描述:这是一个串口设备的句柄。通常,该句柄由 CreateFile 函数获得,代表一个打开的串口通信端口(如 "COM1")。
  2. dwInQueue
    • 类型:DWORD
    • 描述:指定输入缓冲区的大小(以字节为单位)。这个缓冲区用于存储从串口接收到的数据。
  3. dwOutQueue
    • 类型:DWORD
    • 描述:指定输出缓冲区的大小(以字节为单位)。这个缓冲区用于存储将要通过串口发送的数据。

返回值

  • 成功:如果函数执行成功,返回 TRUE
  • 失败:如果函数执行失败,返回 FALSE,可以通过调用 GetLastError() 来获取更多错误信息。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <windows.h>
#include <iostream>

int main() {
    // 打开串口
    HANDLE hSerial = CreateFile(
        "COM1",                        // 串口名称
        GENERIC_READ | GENERIC_WRITE,  // 读写权限
        0,                             // 独占访问
        NULL,                          // 默认安全属性
        OPEN_EXISTING,                 // 打开现有串口
        0,                             // 属性标志
        NULL                           // 不使用模板文件
    );

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open COM port. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // 设置输入缓冲区为 1024 字节,输出缓冲区为 1024 字节
    if (!SetupComm(hSerial, 1024, 1024)) {
        std::cerr << "Failed to setup COM port buffers. Error: " << GetLastError() << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    std::cout << "COM port buffers setup successfully." << std::endl;

    // 进行其他串口通信操作...
    
    // 关闭串口
    CloseHandle(hSerial);
    return 0;
}

注意事项

  • 缓冲区大小的设置:通常,输入和输出缓冲区的大小应根据应用程序的需求进行设置。较大的缓冲区可以容纳更多的数据,减少数据丢失的可能性,但也会占用更多的内存。
  • 句柄有效性:确保在调用 SetupComm 前,串口设备句柄是有效的。这意味着 CreateFile 成功打开了一个串口设备。
  • 缓冲区重设:如果需要更改缓冲区的大小,可以在打开串口设备后立即调用 SetupComm,以确保在任何数据传输之前正确配置缓冲区。

常见错误

  • ERROR_INVALID_HANDLE: 提供的句柄无效,可能是因为串口未成功打开。
  • ERROR_IO_PENDING: 该错误通常与重叠 I/O 操作有关,但在使用 SetupComm 时并不常见。

SetupComm 是串口通信设置中的一个基础函数,正确配置它可以确保串口数据通信的稳定性和效率。

windows C++ CreateFileA()函数 详解

CreateFileA 函数是 Windows API 中用于打开或创建文件、文件夹、符号链接、命名管道、通信设备等的一种函数。CreateFileA 是其 ANSI 版本,对应的 Unicode 版本为 CreateFileW。以下是 CreateFileA 函数的详解。

函数原型

1
2
3
4
5
6
7
8
9
HANDLE CreateFileA(
  LPCSTR               lpFileName,
  DWORD                dwDesiredAccess,
  DWORD                dwShareMode,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  DWORD                dwCreationDisposition,
  DWORD                dwFlagsAndAttributes,
  HANDLE               hTemplateFile
);

参数详解

  1. lpFileName
    • 类型:LPCSTR
    • 描述:指向要打开或创建的对象的名称的指针。对于文件,这通常是文件的路径。如果是设备文件,则使用设备名称(例如 "\\\\.\\COM1")。
  2. dwDesiredAccess
    • 类型:DWORD
    • 描述:指定所需的访问权限。可以是以下常量的组合:
      • GENERIC_READ:读取访问。
      • GENERIC_WRITE:写入访问。
      • GENERIC_EXECUTE:执行访问。
      • GENERIC_ALL:所有访问权限。
  3. dwShareMode
    • 类型:DWORD
    • 描述:指定文件的共享模式,决定其他进程如何访问该文件。可以是以下常量的组合:
      • FILE_SHARE_READ:允许其他进程读取文件。
      • FILE_SHARE_WRITE:允许其他进程写入文件。
      • FILE_SHARE_DELETE:允许其他进程删除文件。
    • 如果此参数为 0,文件将被独占使用。
  4. lpSecurityAttributes
    • 类型:LPSECURITY_ATTRIBUTES
    • 描述:指向 SECURITY_ATTRIBUTES 结构的指针,该结构指定返回的句柄是否可被子进程继承以及文件或对象的安全描述符。如果为 NULL,句柄不可继承,且对象没有指定的安全描述符。
  5. dwCreationDisposition
    • 类型:DWORD
    • 描述:指定如何创建或打开文件,以下是常用的选项:
      • CREATE_NEW:创建新文件。如果文件已存在,函数将失败。
      • CREATE_ALWAYS:创建新文件。如果文件已存在,将覆盖该文件。
      • OPEN_EXISTING:打开现有文件。如果文件不存在,函数将失败。
      • OPEN_ALWAYS:打开文件,如果文件不存在则创建新文件。
      • TRUNCATE_EXISTING:打开现有文件并截断(清空)文件内容。该文件必须有写入权限。
  6. dwFlagsAndAttributes
    • 类型:DWORD
    • 描述:指定文件或设备的标志和属性。常用的标志包括:
      • FILE_ATTRIBUTE_ARCHIVE:文件归档属性。
      • FILE_ATTRIBUTE_HIDDEN:文件为隐藏文件。
      • FILE_ATTRIBUTE_NORMAL:无特殊属性集的文件。
      • FILE_ATTRIBUTE_READONLY:只读文件。
      • FILE_FLAG_DELETE_ON_CLOSE:文件在关闭时自动删除。
      • FILE_FLAG_SEQUENTIAL_SCAN:访问模式为顺序扫描。
      • FILE_FLAG_RANDOM_ACCESS:访问模式为随机访问。
  7. hTemplateFile
    • 类型:HANDLE
    • 描述:用于指定一个有效的模板文件句柄,模板文件的属性将复制到新创建的文件中。该参数通常用于创建新文件时设置与模板文件相同的属性。如果不需要模板文件,设置为 NULL

返回值

  • 成功:返回一个指向新打开文件、设备、管道等的句柄 (HANDLE)。你可以使用此句柄进行读写操作。
  • 失败:返回 INVALID_HANDLE_VALUE,可以调用 GetLastError() 获取详细的错误信息。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <windows.h>
#include <iostream>

int main() {
    HANDLE hFile = CreateFileA(
        "example.txt",                 // 文件名
        GENERIC_READ | GENERIC_WRITE,  // 读写权限
        0,                             // 独占模式,不共享
        NULL,                          // 默认安全属性
        CREATE_ALWAYS,                 // 总是创建新文件
        FILE_ATTRIBUTE_NORMAL,         // 普通文件
        NULL                           // 不使用模板文件
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        std::cout << "Failed to create or open file. Error: " << GetLastError() << std::endl;
        return 1;
    }

    std::cout << "File created/opened successfully." << std::endl;
    
    // 进行文件操作...
    
    CloseHandle(hFile);  // 关闭文件句柄
    return 0;
}

注意事项

  • 打开现有文件时,确保使用正确的权限设置(dwDesiredAccess),否则可能会导致访问失败。
  • 如果文件被其他进程占用且未使用共享模式,你可能会遇到无法访问文件的情况。
  • 在使用 CreateFileA 打开设备(如串口或并口)时,lpFileName 参数需要使用特定的格式(如 "\\\\.\\COM1")。

常见错误

  • ERROR_FILE_NOT_FOUND: 文件不存在,且未指定创建新文件。
  • ERROR_ACCESS_DENIED: 权限不足,无法访问文件。

这个函数的灵活性和多功能性使它在 Windows 编程中非常重要。