WIN API ReadFile() 返回 GetLastError() ERROR_INVALID_PARAMETER。

3
我写了以下代码并在code::blocks中使用mingw gcc 4.7编译它,运行良好。后来我决定开始使用Visual Studio 2013 Express,现在当调用ReadFile()时出现了错误,显示参数无效。我无法找到错误,希望这里有人能发现。

这全部包含在一个名为Serial的类中。从IDE中可以看出,与CreateFile()返回的句柄相比,m_hSerial的内存引用是正确的。

m_hSerial = CreateFile(m_pchPort,
                       GENERIC_READ | GENERIC_WRITE,
                       0,
                       0,
                       OPEN_EXISTING,
                       FILE_FLAG_OVERLAPPED,
                       0);

我这样调用WorkThread
m_hThread = (HANDLE)_beginthreadex(0, 0, &WorkThread, (void*) this, 0, 0);

这里是WorkThread的代码

unsigned int __stdcall Serial::WorkThread(void* pvParam)
{
// This is a pointer to the 'this' serial class.
// Needed to be able to set members of the class in a static class function
Serial * cThis = (Serial*) pvParam;
// Set up the overlapped event
OVERLAPPED ov;
memset(&ov, 0, sizeof(ov));
ov.hEvent = CreateEvent(0, true, 0, 0);
DWORD dwEventMask = 0;
DWORD dwWait;
HANDLE aHandles[2];
aHandles[0] = cThis->m_hThreadTerminator;
aHandles[1] = ov.hEvent;

SetEvent(cThis->m_hThreadRunning);
while (true)
{
    if (!WaitCommEvent(cThis->m_hSerial, &dwEventMask, &ov))
    {
        assert(GetLastError() == ERROR_IO_PENDING);

    }

    dwWait = WaitForMultipleObjects(2, aHandles, FALSE, INFINITE);
    switch(dwWait)
    {
        case WAIT_OBJECT_0:
        {
            _endthreadex(1);
        }
        case WAIT_OBJECT_0 + 1:
        {
            if (dwEventMask & EV_TXEMPTY)
            {
                ResetEvent(ov.hEvent);
            }
            else if (dwEventMask & EV_RXCHAR)
            {
                // read data here
                DWORD dwBytesRead = 0;
                DWORD dwErrors;
                COMSTAT cStat;
                OVERLAPPED ovRead;
                ovRead.hEvent = CreateEvent(0, true, 0, 0);

                // Get the Bytes in queue
                ClearCommError(cThis->m_hSerial, &dwErrors, &cStat);
                DWORD nSize = cStat.cbInQue;
                // EM_REPLACESEL needs a LPARAM null terminated string, make room and set the CString NULL
                char *szBuf = new char[nSize+1];
                memset(szBuf, 0x00, sizeof(szBuf));

                if (!ReadFile(cThis->m_hSerial, &szBuf, nSize, &dwBytesRead, &ovRead))
                    DWORD err = GetLastError();
                if (dwBytesRead == nSize)
                    SendMessage(cThis->m_hHwnd, WM_SERIAL, 0, LPARAM(&szBuf));

                CloseHandle(ovRead.hEvent); // clean up!!!
                delete[] szBuf;
            }
            // Reset the overlapped event
            ResetEvent(ov.hEvent);
        }
        break;
    }//switch
}

return 0;

}


为什么在调用ReadFile()时要解引用szBuf?通过直接传递szBuf,你隐式地执行了&szBuf[0],这应该是调用此函数的正确方法。 - Vinz
你忘记正确初始化ovRead了,使用OVERLAPPED ovRead = {}; - Hans Passant
@HansPassant 您是正确的,那是我的错误。请将其发布为答案,我会接受它。 - user188757
你可以要求David更新他的回答。 - Hans Passant
1个回答

4
ReadFile(cThis->m_hSerial, &szBuf, nSize, &dwBytesRead, &ovRead)

你要求执行异步操作,但同时还要求函数告诉你读取了多少字节。你将 &dwBytesRead 作为倒数第二个参数传递。当执行重叠读取时,应将此参数传递为 NULL。根据文档所述:

如果这是一个异步操作,请将此参数设置为 NULL,以避免可能的错误结果。

在上面的代码中,将 &szBuf 传递是错误的。你应该传递 szBuf
你还没有初始化 OVERLAPPED 结构体。可以按照以下方式进行初始化:
OVERLAPPED ovRead = {};

一个更大的问题是,您要求异步访问,但在写代码时却像它是同步的一样。一旦ReadFile返回,您就尝试从dwBytesRead中获取有意义的信息,并关闭了放置在重叠结构中的事件。
如果您真的要异步编码,那么您需要重新编写代码以进行异步操作。乍一看,似乎您没有完全理解重叠I/O的影响,因此您应该考虑切换到非重叠的同步I/O。

你的意思是传递 &szBuf[0] * 吗? - Vinz
@Vinzenz 无意义的打字错误,我本来想说的是 szBuf,现在已经修复了,谢谢。 - David Heffernan
是的,但你写成了&szBuf而不是szBuf。这只是语言问题。最终szBuf&szBuf[0]是相同的 ;) - Vinz
没有“必须”,这只是一个建议。 - Hans Passant
我同意,我可能没有完全理解。但这是最终目的。感谢快速回复,我会研究如何更改代码。 - user188757
显示剩余2条评论

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