今天在调试一个ICOP的操作的时候,发现连接被动关闭的时候老是会在一处断言处失败,跟了很久终于发现了问题。在此记录一下:
断言报错的代码如下:
HRESULT CIoCPWorker::UnregIoTask(HRESULT hIokey) { ENTER_CALL("CIoCPWorker_UnregIoTask"); ASSERT_(SUCCEEDED(hIokey)); _IoCallback* pIo = reinterpret_cast<_IoCallback*>(hIokey); if(pIo->pendingCount._GetCount() == 0) { TRACE2_L3("--UnregIoTask(), unreg iocp task, fd=%i, io map size=%i\n", pIo->fd, m_ioHandleMap.size() - 1); SOCKET temp = pIo->fd; m_ioHandleMap.erase(temp); return S_OK; } else { ASSERT_(pIo->pTask); pIo->pTask = NULL; TRACE2_L3("--UnregIoTask(), unreg iocp task, fd=%i, io doing=%i\n", pIo->fd, pIo->pendingCount._GetCount()); return E_FAIL; } }
调用的代码如下:
// 被动关闭/关闭超时 void CTcpPort::EndClose(HRESULT reason) { ENTER_CALL("CTcpPort_EndClose"); TRACE3_L4("--(%i,%i)EndClose(), reason=%x\n", m_s, m_s2, reason); if(m_pPortSink) { TRACE3_L4("--(%i,%i)EndClose(), report close, reason=%x\n", m_s, m_s2, reason); m_pPortSink->OnClose(reason); m_pPortSink = NULL; } if(SUCCEEDED(m_regKey) && reason != S_OK) { HRESULT hr = m_pIoWorker->UnregIoTask(m_regKey); ASSERT_(SUCCEEDED(hr)); m_regKey = E_FAIL; if(m_hClosingTimer) { m_pThreadsPool->UnregTimer(this,eClosingHandle); m_hClosingTimer = NULL; } Release(); } }
运行的时候在 ASSERT_(SUCCEEDED(hr)); 的地方失败。
看了一下S_OK和SUCCEEDED的声明:
#define E_FAIL _HRESULT_TYPEDEF_(0x80004005L) #define S_OK ((HRESULT)0x00000000L) #define S_FALSE ((HRESULT)0x00000001L) #define SUCCEEDED(hr) ((HRESULT)(hr) >= 0) #define FAILED(hr) ((HRESULT)(hr) < 0)
和S_OK放在一起的是S_FALSE,怀疑和这个有关,上网查了一下,果然如此!
S_OK是COM服务器返回正确
S_FALSE是COM服务器返回错误,不过这个错误是可以不处理的,不影响程序正常运行。只是结果不是想要的
E_FAIL是必须处理的错误。
返回E_FAIL,是告诉调用程序--某些地方出错,必须进行处理。否则,程序不能进行下去了。
返回S_FALSE,不是表示出现错误。
而S_OK和S_FALSE,则程序逻辑的不同表示。如果将它们表示成S_1和S_2可能更好理解些!
调用程序只需对返回的S_OK和S_FALSE进行判断,然后决定程序的走向。
最主要的误解是由S_FALSE的名称带来的。
所以在使用SUCCEEDED和FAILED来判断返回值的时候,需注意S_FALSE也表示成功,只是其结果并不是想要的,因此FAILED(S_FALSE)返回为"非",SUCCEEDED同理。
当自己写的函数接口返回值为HRESULT的时候,如果想要指明其出错,并且必须被处理时,应返回E_FAIL。