CreateFile()返回INVALID_HANDLE_VALUE但GetLastError()返回ERROR_SUCCESS

8
我正在使用CreateFile()打开一个串口。我有一个测试用例(过于复杂,无法重新分发),该测试用例始终导致CreateFile()返回INVALID_HANDLE_VALUEGetLastError()返回ERROR_SUCCESS。看起来,只有当一个线程在另一个端口关闭它的同时打开端口时,才会出现此错误。打开端口的线程遇到了这个问题。
我不知道这是否有区别,但稍后在代码中,我使用CreateIoCompletionPort将端口与CompletionPort相关联。
以下是我的代码:
HANDLE port = CreateFile(L"\\\\.\\COM1",
                         GENERIC_READ | GENERIC_WRITE,
                         0,                    // must be opened with exclusive-access
                         0,                    // default security attributes
                         OPEN_EXISTING,        // must use OPEN_EXISTING
                         FILE_FLAG_OVERLAPPED, // overlapped I/O
                         0);                   // hTemplate must be NULL for comm devices
if (port == INVALID_HANDLE_VALUE)
{
    DWORD errorCode = GetLastError();
    cerr << L"CreateFile() failed with error: " << errorCode << endl;
}

我相信这种情况不应该发生。我做错了什么?如何让API返回正确的结果?
更多细节: 这段代码来自我开发的串口库: JPeripheral 下面是实际的(未经过处理的)源代码:
JLong SerialChannel::nativeOpen(String name)
{
    cerr << "nativeOpen(" << name << ")" << endl;
    wstring nameWstring = name;
    HANDLE port = CreateFile((L"\\\\.\\" + nameWstring).c_str(),
        GENERIC_READ | GENERIC_WRITE,
        0,                                          // must be opened with exclusive-access
        0,                                          // default security attributes
        OPEN_EXISTING,                  // must use OPEN_EXISTING
        FILE_FLAG_OVERLAPPED,       // overlapped I/O
        0);                                         // hTemplate must be NULL for comm devices
    cerr << "nativeOpen.afterCreateFile(" << name << ")" << endl;
    cerr << "port: " << port << ", errorCode: " << GetLastError() << endl;
    if (port == INVALID_HANDLE_VALUE)
    {
        DWORD errorCode = GetLastError();

        switch (errorCode)
        {
            case ERROR_FILE_NOT_FOUND:
                throw PeripheralNotFoundException(jace::java_new<PeripheralNotFoundException>(name, Throwable()));
            case ERROR_ACCESS_DENIED:
            case ERROR_SHARING_VIOLATION:
                throw PeripheralInUseException(jace::java_new<PeripheralInUseException>(name, Throwable()));
            default:
            {
                throw IOException(jace::java_new<IOException>(L"CreateFile() failed with error: " +
                    getErrorMessage(GetLastError())));
            }
        }
    }

    // Associate the file handle with the existing completion port
    HANDLE completionPort = CreateIoCompletionPort(port, ::jperipheral::worker->completionPort, Task::COMPLETION, 0);
    if (completionPort==0)
    {
        throw AssertionError(jace::java_new<AssertionError>(L"CreateIoCompletionPort() failed with error: " +
            getErrorMessage(GetLastError())));
    }
    cerr << "nativeOpen.afterCompletionPort(" << name << ")" << endl;

    // Bind the native serial port to Java serial port
    SerialPortContext* result = new SerialPortContext(port);
    cerr << "nativeOpen.afterContext(" << name << ")" << endl;
    return reinterpret_cast<intptr_t>(result);
}

这是我得到的实际输出结果:
nativeOpen(COM1)
nativeOpen.afterCreateFile(COM1)
port: 00000374, errorCode: 0
nativeOpen.afterCompletionPort(COM1)
nativeOpen.afterContext(COM1)
[...]
nativeOpen(COM1)
nativeOpen.afterCreateFile(COM1)
port: FFFFFFFF, errorCode: 0
java.io.IOException: CreateFile() failed with error: The operation completed successfully.

你要访问的硬件是什么? - Gabe
@Gabe:我正在访问我们内部开发的一个嵌入式设备。它有一个标准的DB9串口连接,我已将其连接到我的电脑上(这里没有USB-RS232适配器)。 - Gili
@David:我已经发布了完整的源代码供您审查。 - Gili
5
CreateFile失败后,必须立即调用GetLastError,中间不要加入<< - Roman R.
@Gili:是的,你的简化隐藏了问题。你在调用“<<”之后调用了“GetLastError”。你需要在调用“CreateFile”之后调用“GetLastError”。 - David Schwartz
显示剩余2条评论
1个回答

9
 HANDLE port = CreateFile(...);
 cerr << "nativeOpen.afterCreateFile(" << name << ")" << endl;
 cerr << "port: " << port << ", errorCode: " << GetLastError() << endl;
 if (port == INVALID_HANDLE_VALUE)
 {
    DWORD errorCode = GetLastError();

输出到cerr会在底层调用winapi函数,这会重置由GetLastError()返回的线程错误值。修复方法:

 HANDLE port = CreateFile(...);
 int err = GetLastError();
 // etc, use err instead...

我还不确定。原本存在问题的代码中没有包含任何cerr指令。我最近才添加了它。你看到上面的代码(减去cerr)有什么问题吗? - Gili
1
你展示的输出跟踪只能由破坏GetLastError值的代码生成。我不怀疑它可能会失败,因为打开COM端口失败的原因有很多。 - Hans Passant
你是对的。原始代码调用了throw IOException(jace::java_new<IOException>(L"CreateFile() failed with error: " + getErrorMessage(GetLastError()))。注意在故障点和读取GetLastError()之间存在多少代码。更早地读取该值解决了问题。谢谢! - Gili
我曾经遇到过类似的问题,即使在CreateFile2和GetLastError()之间只有“new wchar_t[messageLength]”,这已足以重置由GetLastError()返回的错误消息。 - VeeTheSecond

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接