0xc0000008 invalid handle 错误原因纪实

本文记录了在调试过程中遇到的0xC00000008: Invalid Handle 错误的解决过程。问题源于全局变量的mutex对象在局部变量拷贝后,析构导致的无效句柄。在另一处代码中,由于MapViewOfFile返回的句柄类型错误,导致CloseHandle时触发异常。

 关于调试时程序报 0xC00000008: Invalid Handle 错误的原因。

前几天在调试一个产品时,进入到某一个函数调试,在函数返回时, IDE开始报 "叉叉叉...0xC00000008: Invalid Handle " 的调试错误,此时按F10, 不能继续往下执行,只能转到汇编模式调试……这不明白着流氓吗!?我怎么能看得懂汇编……
来劲了,非要弄明白到底是什么原因导致了报这个叉叉叉错误。
捣鼓了好久好久,折腾了很长很长,但没有找到错误的原因……看来非的请我的偶像赵阳了,结果他来调了好久也是没找到根本原因,最后留下几句鸟语铩羽而归。我挺高兴,偶像竟然没搞定。
又请来了我的部门经理ZhangSY同志,他在我心目中的地位那绝对是“手中无剑,心中无剑”的N系列。
过然,他坐下来,还是那么的思路清晰,身手敏捷,而且凸显了超好的记忆力,N年前的代码,他都能记得请当时为什么要屏蔽,为什么要修改……经过一番望闻问切,把原因锁定在了xMutex这个类上,类的析构函数上!
其实这里最终的结果已经不远了,但还不是真正的原因所在。
我跟你铆上了!
世上从来就没有救世主,也没有神仙&皇帝……
看,瞅,吧嗒吧嗒整了整整一下午+ 一个晚上,额滴神啊,找到原因了:邱YL说的也很对。
class xMutex a;
struct var
{ int a;
  xMutex mtx;
};
var list; //此为全局变量
void fun1()
{
var temp;
memcpy(&temp, &list, sizeof(var); //问题出在此
.....叉叉叉操作....
return fool;// 跟踪到此处,报0xc00000008错误!
}
看明白了吗?
在函数fun1的作用域内,已经初始化过的全局变量list的值通过拷贝付给了局部变量 temp,
在函数出栈前,temp的声明周期结束,系统调用了内部mtx的析构函数将原本属于list的mtx对象给析构了!

据我发现,这个0xc00000008是一级异常(first chance exception!)。调试中出现一级异常只能终止。

------
今天写一个关于pe的程序,调试时又出现了这个008,出现异常附近的代码如下:
CJWPckDlg::~CJWPckDlg()
{
 if (m_hMapFl)
 {
  UnmapViewOfFile(m_hMapFl);
  CloseHandle(m_hMapFl); //执行此语句时报008错误。
  m_hMapFl = NULL;
 }
 
 为啥?
 我把m_hMapFl定义为HANDLEL类型,而事实上m_hMapfl = MapViewOfFile();本来MapViewOfFile应该返回一个DWORD类型的地址。
 当执行CloseHandle(m_hMapFl)时,非法句柄!

<think> 我们在前面的代码中,当关闭串口时,在ClosePort函数中调用了CloseHandle(m_hThread)。但是,在MFC中,使用AfxBeginThread创建的线程,系统会自动管理线程句柄的关闭。因此,我们不应该手动关闭这个句柄,否则会导致重复关闭而出现无效句柄的错误。 具体来说,AfxBeginThread返回的线程句柄在线程结束时会被系统自动关闭,因此我们不需要(也不应该)再调用CloseHandle来关闭它。如果我们保存了这个句柄并试图关闭它,当线程已经结束时,这个句柄就已经被系统关闭了,再次关闭就会报错。 修改建议: 1. 不再保存m_hThread,而是使用一个事件或标志来控制线程退出。 2. 或者,如果我们确实需要保存句柄,那么应该确保只关闭一次。 根据MSDN文档,对于使用AfxBeginThread创建的线程,当线程结束时,线程句柄会被自动关闭,所以我们不应该再调用CloseHandle。 因此,修改ClosePort函数,去掉CloseHandle(m_hThread)这一行,只需要等待线程退出即可。 修改后的ClosePort函数: ```cpp void CSerialPortTool::ClosePort() { if (!m_bThreadRunning) return; m_bThreadRunning = FALSE; m_eventExitThread.SetEvent(); if (m_hThread) { // 等待线程结束,但不要关闭句柄,因为它是系统自动管理的 WaitForSingleObject(m_hThread, 3000); // 将句柄置为NULL,不需要关闭 m_hThread = NULL; } if (m_hComm != INVALID_HANDLE_VALUE) { CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; } } ``` 注意:在析构函数中调用ClosePort(),所以析构函数不需要额外处理。 这样修改后,就不会再出现关闭无效句柄的错误了。 另外,在ReadThreadProc线程函数中,我们使用了重叠I/O,要注意在退出线程时,应该取消未完成的重叠I/O操作。我们在等待线程退出前设置了m_bThreadRunning为FALSE并设置事件,线程中的WaitForMultipleObjects会等待到退出事件,然后跳出循环,线程自然结束。 同时,在退出线程之前,我们通过CancelIo来取消挂起的I/O操作(在WaitForMultipleObjects中如果等待到退出事件,则调用CancelIo)。但是注意,在WaitForMultipleObjects中等待到退出事件(即第二个事件)时,我们调用了CancelIo,然后继续循环,由于m_bThreadRunning为FALSE,循环会退出。所以不会再有新的读操作。 但是,在循环退出后,我们关闭了重叠结构中的事件句柄(ov.hEvent),这是正确的。 所以,总结:去掉对m_hThread的CloseHandle调用。 因此,错误0xC0000008是因为我们关闭了一个已经被系统关闭的句柄(或者无效的句柄)。 修改后,问题应该解决。 §§相关问题§§ 1. 为什么使用AfxBeginThread创建的线程句柄不需要手动关闭? 2. 在MFC多线程编程中,如何正确终止一个工作线程? 3. 什么是无效句柄错误0xC0000008)?通常由哪些操作引起? 4. 在关闭串口资源时,除了线程句柄还有哪些需要特别注意的资源释放? 5. 如何确保在串口工具类析构时,所有资源都被正确释放? </think> ### 解决 `CloseHandle(m_hThread)` 报错 0xC0000008 **错误原因分析:** `0xC0000008` 错误表示尝试关闭无效句柄。在您的代码中,主要原因可能是: 1. 线程已自然退出,句柄自动失效 2. 重复关闭同一句柄 3. 线程创建失败导致初始句柄无效 4. 多线程竞争条件导致句柄状态不一致 **解决方案:** ```cpp void CSerialPortTool::ClosePort() { if (!m_bThreadRunning) return; m_bThreadRunning = FALSE; m_eventExitThread.SetEvent(); // 通知线程退出 // 正确等待线程结束 if (m_hThread && m_hThread != INVALID_HANDLE_VALUE) { WaitForSingleObject(m_hThread, 3000); // 等待线程结束 // 检查线程状态后再关闭句柄 DWORD exitCode = STILL_ACTIVE; GetExitCodeThread(m_hThread, &exitCode); if (exitCode != STILL_ACTIVE) { CloseHandle(m_hThread); // 只有线程已退出时才关闭 m_hThread = NULL; } else { // 强制终止异常线程 TerminateThread(m_hThread, 0); CloseHandle(m_hThread); m_hThread = NULL; } } // 关闭串口句柄(保留原有逻辑) if (m_hComm != INVALID_HANDLE_VALUE) { CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; } } ``` **关键修改点:** 1. **双重验证机制**: ```cpp if (m_hThread && m_hThread != INVALID_HANDLE_VALUE) ``` 2. **线程状态检测**: ```cpp DWORD exitCode = STILL_ACTIVE; GetExitCodeThread(m_hThread, &exitCode); ``` 3. **安全关闭策略**: - 正常退出时优雅关闭 - 异常线程强制终止 ```cpp if (exitCode != STILL_ACTIVE) { CloseHandle(m_hThread); } else { TerminateThread(m_hThread, 0); CloseHandle(m_hThread); } ``` 4. **句柄重置**: ```cpp m_hThread = NULL; // 避免重复关闭 ``` **补充建议:** 1. 在 `OpenPort()` 中初始化句柄: ```cpp m_hThread = NULL; // 构造函数中已初始化 ``` 2. 线程创建后检查有效性: ```cpp m_hThread = AfxBeginThread(ReadThreadProc, this); if (!m_hThread || m_hThread == INVALID_HANDLE_VALUE) { // 错误处理 } ``` 3. 在线程退出函数中重置状态: ```cpp UINT CSerialPortTool::ReadThreadProc(LPVOID pParam) { // ...线程逻辑... pThis->m_hThread = NULL; // 线程结束时重置 return 0; } ``` **根本原因预防:** - 使用 `CMutex` 或 `CCriticalSection` 保护句柄操作 - 采用 RAII 模式管理资源生命周期 - 使用智能指针管理线程对象(需 MFC 扩展)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值