MFC Release下输出CallStack

本文介绍如何在Windows环境下使用Dbghelp库获取程序崩溃信息及调用堆栈(CallStack),包括错误代码、地址、标志等详细信息。
#pragma once
#include <windows.h>
#include < Dbghelp.h>
#include <iostream>  
#include <vector>  
using namespace std; 


#pragma comment(lib, "Dbghelp.lib")


namespace NSDumpCallStack
{
const int MAX_ADDRESS_LENGTH = 32;  
const int MAX_NAME_LENGTH = 1024;  
// 崩溃信息  
//   
struct CrashInfo  
{  
CHAR ErrorCode[MAX_ADDRESS_LENGTH];  
CHAR Address[MAX_ADDRESS_LENGTH];  
CHAR Flags[MAX_ADDRESS_LENGTH];  
};  


// CallStack信息  
//   
struct CallStackInfo  
{  
CHAR ModuleName[MAX_NAME_LENGTH];  
CHAR MethodName[MAX_NAME_LENGTH];  
CHAR FileName[MAX_NAME_LENGTH];  
CHAR LineNumber[MAX_NAME_LENGTH];  
};  


// 安全拷贝字符串函数  
//  
void SafeStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)  
{  
if (nMaxDestSize <= 0) return;  
if (strlen(szSrc) < nMaxDestSize)  
{  
strcpy_s(szDest, nMaxDestSize, szSrc);  
}  
else  
{  
strncpy_s(szDest, nMaxDestSize, szSrc, nMaxDestSize);  
szDest[nMaxDestSize-1] = '\0';  
}  
}    


// 得到程序崩溃信息  
CrashInfo GetCrashInfo(const EXCEPTION_RECORD *pRecord)  
{  
CrashInfo crashinfo;  
SafeStrCpy(crashinfo.Address, MAX_ADDRESS_LENGTH, "N/A");  
SafeStrCpy(crashinfo.ErrorCode, MAX_ADDRESS_LENGTH, "N/A");  
SafeStrCpy(crashinfo.Flags, MAX_ADDRESS_LENGTH, "N/A");  


sprintf_s(crashinfo.Address, "%08X", pRecord->ExceptionAddress);  
sprintf_s(crashinfo.ErrorCode, "%08X", pRecord->ExceptionCode);  
sprintf_s(crashinfo.Flags, "%08X", pRecord->ExceptionFlags);  
return crashinfo;  
}  


// 得到CallStack信息  
//  
vector<CallStackInfo> GetCallStack(const CONTEXT *pContext)  
{  
HANDLE hProcess = GetCurrentProcess();  


SymInitialize(hProcess, NULL, TRUE);  


vector<CallStackInfo> arrCallStackInfo;  


CONTEXT c = *pContext;  


STACKFRAME64 sf;  
memset(&sf, 0, sizeof(STACKFRAME64));  
DWORD dwImageType = IMAGE_FILE_MACHINE_I386;  


// 不同的CPU类型,具体信息可查询MSDN  
//  
#ifdef _M_IX86  
sf.AddrPC.Offset = c.Eip;  
sf.AddrPC.Mode = AddrModeFlat;  
sf.AddrStack.Offset = c.Esp;  
sf.AddrStack.Mode = AddrModeFlat;  
sf.AddrFrame.Offset = c.Ebp;  
sf.AddrFrame.Mode = AddrModeFlat;  
#elif _M_X64  
dwImageType = IMAGE_FILE_MACHINE_AMD64;  
sf.AddrPC.Offset = c.Rip;  
sf.AddrPC.Mode = AddrModeFlat;  
sf.AddrFrame.Offset = c.Rsp;  
sf.AddrFrame.Mode = AddrModeFlat;  
sf.AddrStack.Offset = c.Rsp;  
sf.AddrStack.Mode = AddrModeFlat;  
#elif _M_IA64  
dwImageType = IMAGE_FILE_MACHINE_IA64;  
sf.AddrPC.Offset = c.StIIP;  
sf.AddrPC.Mode = AddrModeFlat;  
sf.AddrFrame.Offset = c.IntSp;  
sf.AddrFrame.Mode = AddrModeFlat;  
sf.AddrBStore.Offset = c.RsBSP;  
sf.AddrBStore.Mode = AddrModeFlat;  
sf.AddrStack.Offset = c.IntSp;  
sf.AddrStack.Mode = AddrModeFlat;  
#else  
#error "Platform not supported!"  
#endif  


HANDLE hThread = GetCurrentThread();  


while (true)  
{  
// 该函数是实现这个功能的最重要的一个函数  
// 函数的用法以及参数和返回值的具体解释可以查询MSDN  
//  
if (!StackWalk64(dwImageType, hProcess, hThread, &sf, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))  
{  
break;  
}  


if (sf.AddrFrame.Offset == 0)  
{  
break;  
}  


CallStackInfo callstackinfo;  
SafeStrCpy(callstackinfo.MethodName, MAX_NAME_LENGTH, "N/A");  
SafeStrCpy(callstackinfo.FileName, MAX_NAME_LENGTH, "N/A");  
SafeStrCpy(callstackinfo.ModuleName, MAX_NAME_LENGTH, "N/A");  
SafeStrCpy(callstackinfo.LineNumber, MAX_NAME_LENGTH, "N/A");  


BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_NAME_LENGTH];  
IMAGEHLP_SYMBOL64 *pSymbol = (IMAGEHLP_SYMBOL64*)symbolBuffer;  
memset(pSymbol, 0, sizeof(IMAGEHLP_SYMBOL64) + MAX_NAME_LENGTH);  


pSymbol->SizeOfStruct = sizeof(symbolBuffer);  
pSymbol->MaxNameLength = MAX_NAME_LENGTH;  


DWORD symDisplacement = 0;  


// 得到函数名  
//  
if (SymGetSymFromAddr64(hProcess, sf.AddrPC.Offset, NULL, pSymbol))  
{  
SafeStrCpy(callstackinfo.MethodName, MAX_NAME_LENGTH, pSymbol->Name);  
}  


IMAGEHLP_LINE64 lineInfo;  
memset(&lineInfo, 0, sizeof(IMAGEHLP_LINE64));  


lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);  


DWORD dwLineDisplacement;  


// 得到文件名和所在的代码行  
//  
if (SymGetLineFromAddr64(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo))  
{  
SafeStrCpy(callstackinfo.FileName, MAX_NAME_LENGTH, lineInfo.FileName);  
sprintf_s(callstackinfo.LineNumber, "%d", lineInfo.LineNumber);  
}  


IMAGEHLP_MODULE64 moduleInfo;  
memset(&moduleInfo, 0, sizeof(IMAGEHLP_MODULE64));  


moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);  


// 得到模块名  
//  
if (SymGetModuleInfo64(hProcess, sf.AddrPC.Offset, &moduleInfo))  
{  
SafeStrCpy(callstackinfo.ModuleName, MAX_NAME_LENGTH, moduleInfo.ModuleName);  
}  


arrCallStackInfo.push_back(callstackinfo);  
}  


SymCleanup(hProcess);  


return arrCallStackInfo;  
}  


LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
return NULL;
}


BOOL PreventSetUnhandledExceptionFilter()
{
HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
if (hKernel32 ==   NULL)
return FALSE;


void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
if(pOrgEntry == NULL)
return FALSE;


unsigned char newJump[ 100 ];
DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;
dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far


void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
DWORD dwNewEntryAddr = (DWORD) pNewFunc;
DWORD dwRelativeAddr = dwNewEntryAddr -  dwOrgEntryAddr;


newJump[ 0 ] = 0xE9;  // JMP absolute
memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
SIZE_T bytesWritten;
BOOL bRet = WriteProcessMemory(GetCurrentProcess(), pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
return bRet;
}


LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException)
{
// TODO: MiniDumpWriteDump
// Callstack
// 确保有足够的栈空间  
//  
#ifdef _M_IX86  
if (pException->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)  
{  
static char TempStack[1024 * 128];  
__asm mov eax,offset TempStack[1024 * 128];  
__asm mov esp,eax;  
}  
#endif    


CrashInfo crashinfo = GetCrashInfo(pException->ExceptionRecord);  


// 输出Crash信息  
//  
cout << "ErrorCode: " << crashinfo.ErrorCode << endl;  
cout << "Address: " << crashinfo.Address << endl;  
cout << "Flags: " << crashinfo.Flags << endl;  


vector<CallStackInfo> arrCallStackInfo = GetCallStack(pException->ContextRecord);  


// 输出CallStack  
//  
cout << "CallStack: " << endl;  
for (vector<CallStackInfo>::iterator i = arrCallStackInfo.begin(); i != arrCallStackInfo.end(); ++i)  
{  
CallStackInfo callstackinfo = (*i);  


cout << callstackinfo.MethodName << "() : [" << callstackinfo.ModuleName << "] (File: " << callstackinfo.FileName << " @Line " << callstackinfo.LineNumber << ")" << endl;  
}  
FatalAppExit(-1,  _T("ApplicationCrashHandler1"));
return EXCEPTION_CONTINUE_SEARCH;
}


void RunCrashHandler()
{
SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
PreventSetUnhandledExceptionFilter();
}
};


#define DeclareDumpCallStack() NSDumpCallStack::RunCrashHandler();

<think>嗯,用户在使用MFC的CExport2Excel时遇到了程序闪退的问题,需要找出原因和解决方案。首先,我得回忆一下常见的MFC程序崩溃原因。可能的原因包括内存泄漏、空指针访问、数组越界、COM组件未初始化等。 CExport2Excel可能涉及到文件操作和Excel的COM接口调用。首先考虑COM初始化是否正确。MFC中使用COM需要调用CoInitialize或AfxOleInit,特别是在非主线程中,如果没有正确初始化,会导致崩溃。比如,用户可能在某个工作线程中调用了导出功能,但没有初始化COM,这时候就会出问题。 接下来是文件路径的问题。如果用户提供的路径无效或没有写入权限,程序可能在尝试创建文件时崩溃。比如路径中包含非法字符,或者目录不存在,又没有错误处理,就会导致异常。这时候需要检查路径是否有效,并确保程序有权限写入目标目录。 然后是Excel对象释放的问题。使用COM对象后,必须正确释放资源,否则可能导致内存泄漏或崩溃。比如,每个CreateObject对应的对象是否调用了Release,或者是否有未关闭的工作簿或应用程序实例,导致Excel进程残留在内存中,进而引发问题。 另外,多线程操作也是一个可能的原因。如果用户在没有同步的情况下,多个线程同时操作Excel对象,可能会导致竞争条件,从而崩溃。需要确认是否在导出时有多线程调用,并考虑加锁或者改为单线程处理。 还有异常处理的问题。如果在导出过程中发生了异常,比如磁盘空间不足、文件被占用等,而代码中没有try-catch块来捕获处理,程序就会直接崩溃。建议用户添加异常处理机制,确保程序能捕获并处理这些错误,而不是直接退出。 还可能涉及Office版本兼容性问题。不同的Excel版本可能有不同的COM接口,如果代码是针对特定版本编写的,而在用户环境中安装了不同版本的Excel,可能会导致问题。需要检查ProgID是否正确,比如使用“Excel.Application”而不是特定版本号,或者安装兼容的Office组件。 此外,资源泄露也是常见的崩溃原因。比如没有正确释放BSTR字符串、VARIANT变量或其他COM资源,长时间运行后导致内存不足。使用智能指针如_com_ptr_t或CComBSTR可以帮助自动管理资源,避免泄露。 最后,日志记录和调试输出对于定位问题非常重要。用户可以在关键步骤添加日志,记录导出过程中的状态,或者使用调试器查看崩溃时的调用堆栈,确定具体出错的位置。例如,在调用CreateObject、打开工作簿、写入数据等步骤前后输出日志,帮助缩小问题范围。 综合这些可能的原因,需要建议用户逐一排查,比如检查COM初始化、验证文件路径、确保资源释放、添加异常处理、确认Office版本兼容性,以及使用调试工具定位问题。通过逐步排除,应该能找到导致闪退的具体原因并解决它。</think>### MFC CExport2Excel程序闪退原因及解决方案 #### 一、常见崩溃原因分析 1. **COM组件未初始化** Excel操作依赖COM组件,未调用`CoInitialize`或`AfxOleInit`会导致崩溃。例如: ```cpp if (!AfxOleInit()) { AfxMessageBox(_T("COM初始化失败")); return; } ``` 2. **无效文件路径** 路径包含非法字符(如`*`、`?`)或目标目录无写入权限时,创建Excel文件会失败[^1]。 **验证方法**:`PathFileExists`检查目录合法性。 3. **Excel对象未释放** 未正确释放`Workbook`、`Worksheet`等COM对象会导致内存泄漏或崩溃。典型错误: ```cpp _Application app; app.CreateDispatch(_T("Excel.Application")); // 缺少 app.ReleaseDispatch() ``` 4. **多线程冲突** 多个线程同时操作Excel对象时,可能因COM套间线程模型不匹配导致崩溃[^2]。 #### 二、解决方案步骤 1. **添加COM初始化代码** 在`InitInstance()`中初始化: ```cpp BOOL CYourApp::InitInstance() { if (!AfxOleInit()) { AfxMessageBox(_T("COM初始化失败")); return FALSE; } // 其他初始化代码 } ``` 2. **规范化路径处理** 使用`GetTempPath`生成临时路径或检查用户输入: ```cpp CString strPath = _T("C:\\ValidPath\\test.xlsx"); if (!PathIsDirectory(PathFindPath(strPath))) { AfxMessageBox(_T("路径无效")); return; } ``` 3. **强制释放COM对象** 使用`ReleaseDispatch()`和`COleVariant::Clear()`: ```cpp _Workbook workbook = app.GetWorkbooks().Add(COleVariant((long)1)); // 操作完成后 workbook.SaveAs(COleVariant(strPath)); workbook.Close(COleVariant((long)FALSE)); workbook.ReleaseDispatch(); ``` 4. **添加异常捕获** 使用`try-catch`捕获`COleException`: ```cpp try { // Excel操作代码 } catch (COleException* e) { TCHAR szError[256]; e->GetErrorMessage(szError, 255); AfxMessageBox(szError); e->Delete(); } ``` #### 三、高级调试技巧 1. **查看崩溃堆栈** 在Debug模式下运行程序,触发崩溃时通过`Call Stack`窗口定位错误代码行。 2. **检查Office版本兼容性** 修改ProgID为版本无关标识符: ```cpp app.CreateDispatch(_T("Excel.Application.16")); // 明确指定Office 2016 ``` 3. **使用智能指针** 通过`_com_ptr_t`自动管理COM对象: ```cpp Excel::_ApplicationPtr spApp; spApp.CreateInstance(__uuidof(Excel::Application)); ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值