跨平台(Windows/Linux/Android)系统API错误处理技术咨询
程序背景
我正在开发一款跨平台程序,支持以下平台:
- Windows x64
- Linux x64
- Windows ARM
- Linux ARM
- Android ARM
该程序需对接最多20个串口,通过MQTT或ReST-JSON与服务器连接,应用中大量使用异步操作与线程。
技术问题
- 能否使用libc的
errno整型变量处理WIN API错误?还是必须在Windows平台使用GetLastError,在Linux和Android平台使用errno? GetLastError()是否线程安全?strerror_s是否线程安全?- 若编译时选择C11或更高版本,
strerror_s是否可在Linux和Android平台使用?
当前实现代码
//返回最后一次错误的字符串形式,无错误则返回空字符串。 std::string GetLastErrorAsString(void) { #ifdef _WIN64 //获取错误消息ID(若存在)。 DWORD errorMessageID = ::GetLastError(); if (errorMessageID == 0) { return std::string(); //未记录错误消息 } LPSTR message_buffer = nullptr; //请求Win64返回该消息ID对应的字符串版本。 //传入的参数指示Win64为我们创建存储消息的缓冲区(因为我们尚不清楚消息字符串的长度)。 size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&message_buffer, 0, NULL); //将错误消息复制到std::string中。 std::string error_message(message_buffer, size); //释放Win64分配的字符串缓冲区。 LocalFree(message_buffer); return error_message; #elif __linux__ || __ANDROID__ if (errno == 0) { return std::string(); //未记录错误消息 } size_t error_message_length = strerrorlen_s(errno) + 1; std::array<char, error_message_length> error_message; //char errmsg[errmsglen]; strerror_s(error_message.data(), error_message_length, errno); return std::string(error_message.data()); #endif }
问题解答
1. WIN API错误与errno的使用限制
不能用errno处理WIN API错误。WIN API调用失败时,只会通过GetLastError()返回错误码,不会主动设置errno;只有调用C标准库函数(如fopen、read)时,Windows才会同步更新errno。因此必须在Windows平台用GetLastError()获取WIN API的错误,Linux和Android平台用errno获取系统调用或标准库的错误。
2. GetLastError()的线程安全性
GetLastError()是线程安全的。每个线程都有独立的错误码存储区域,调用该函数只会返回当前线程的最后错误,不会与其他线程的错误码混淆。但要注意,必须在WIN API调用失败后立即调用GetLastError(),后续的API调用可能会覆盖当前线程的错误码。
3. strerror_s的线程安全性
strerror_s是线程安全的。作为C11引入的安全函数,它要求调用者提供私有缓冲区,避免了strerror使用共享静态缓冲区导致的线程竞争问题,只要传入的缓冲区是当前线程独有的,就不会有线程安全风险。
4. Linux和Android平台对C11标准strerror_s的支持
编译时指定C11或更高版本后,主流Linux发行版(如Ubuntu、CentOS)的GCC/Clang编译器均支持strerror_s;Android NDK从r15版本开始也支持C11标准的strerror_s,部分老版本NDK需要显式启用C11支持(比如在CMake中设置CMAKE_C_STANDARD 11)。
内容的提问来源于stack exchange,提问作者Dark Sorrow

