我们先看下面的代码
void ui::wnd::CDesktopWnd::Exe2Shortcut( LPCWSTR strFullPath, LPCWSTR strFileName, LPCWSTR shelllink_path) { CString strDescName = strFileName; if(strDescName == _T("iexplore.exe")) { strDescName = _T("Internet Explorer"); } PathRenameExtension(strDescName.GetBuffer(0), _T("")); CString strFileNameTmp = strFileName; CString strLnk = GetLnkPath(); PathRenameExtension(strFileNameTmp.GetBuffer(0), DESKTOPWND_SHORTCUT_EXT); CString strLnkFileTxt = strLnk + _T("\\") + strFileNameTmp; strFileNameTmp = strFileName; PathRenameExtension(strFileNameTmp.GetBuffer(0), _T(".bmp")); //和上述同样问题 CString strIcoPath = strLnk + _T("\\") + strFileNameTmp; // CIconSnap is; // is.GetIcon(strFullPath); CIconSnap iconsnap; if(FALSE == iconsnap.Save(strFullPath, strIcoPath)) { SIMPLE_TRACE(L"提取图标失败!"); //::MessageBox(m_hWnd, _T("提取图标失败!"), _T("消息"), 0); } else { AppLogicalContainer iconContainer; BOOL bAddSuc = TRUE; if(shelllink_path==0) { if (strDescName != _T("Internet Explorer")) { bAddSuc = iconContainer.AddApp(strFullPath,strIcoPath,strDescName, shelllink_path); } if (bAddSuc) { CString strSkinPath = GetSkinPath(); m_IconListView.AddButtonEx( strDescName, strFullPath , m_hWnd, HandlerParam(this, OnBtnMessage), strIcoPath, strSkinPath + _T("\\tools\\desktop_icon_back3.png"), strSkinPath + _T("\\tools\\desktop_icon_back1.png")); } } else if (shelllink_path) { CString strSkinPath = GetSkinPath(); m_IconListView.AddButtonEx( strDescName, shelllink_path , m_hWnd, HandlerParam(this, OnBtnMessage), strIcoPath, strSkinPath + _T("\\tools\\desktop_icon_back3.png"), strSkinPath + _T("\\tools\\desktop_icon_back1.png")); } } }
这段代码是有问题的,跟踪调试出现在内存释放的时候,下面是栈
PathRenameExtension (str.GetBuffer
THREAD fffffa8002791b60 Cid 0ab0.0ad8 Teb: 000007fffff8e000 Win32Thread: fffff900c22cf600 WAIT: (UserRequest) UserMode Non-Alertable
fffffa800291c5b0 ProcessObject
fffffa80038d2e90 NotificationEvent
Not impersonating
DeviceMap fffff8a000f67550
Owning Process fffffa8002777060 Image: SandBoxMgr.exe
Attached Process N/A Image: N/A
Wait Start TickCount 36688 Ticks: 8719 (0:00:02:16.017)
Context Switch Count 1282 LargeStack
UserTime 00:00:00.124
KernelTime 00:00:00.374
Win32 Start Address SandboxMgr!_threadstartex (0x000000013f35a670)
Stack Init fffff88003c62d70 Current fffff88003c61f90
Base fffff88003c63000 Limit fffff88003c51000 Call 0
Priority 12 BasePriority 8 UnusualBoost 0 ForegroundBoost 2 IoPriority 2 PagePriority 5
Kernel stack not resident.
Child-SP RetAddr Call Site
fffff880`03c61fd0 fffff800`03ed0052 nt!KiSwapContext+0x7a
fffff880`03c62110 fffff800`03ecc54b nt!KiCommitThreadWait+0x1d2
fffff880`03c621a0 fffff800`041c1bcf nt!KeWaitForMultipleObjects+0x271
fffff880`03c62450 fffff800`041c24d6 nt!ObpWaitForMultipleObjects+0x294
fffff880`03c62920 fffff800`03ec8153 nt!NtWaitForMultipleObjects+0xe5
fffff880`03c62b70 00000000`7749046a nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`03c62be0)
00000000`04b9cac8 000007fe`fd6213a6 ntdll!NtWaitForMultipleObjects+0xa
00000000`04b9cad0 00000000`77243143 KERNELBASE!WaitForMultipleObjectsEx+0xe8
00000000`04b9cbd0 00000000`772b9025 kernel32!WaitForMultipleObjectsExImplementation+0xb3
00000000`04b9cc60 00000000`772b91a7 kernel32!WerpReportFaultInternal+0x215
00000000`04b9cd00 00000000`772b91ff kernel32!WerpReportFault+0x77
00000000`04b9cd30 00000000`772b941c kernel32!BasepReportFault+0x1f
00000000`04b9cd60 00000000`774d573c kernel32!UnhandledExceptionFilter+0x1fc
00000000`04b9ce40 00000000`77455148 ntdll! ?? ::FNODOBFM::`string‘+0x2365
00000000`04b9ce70 00000000`7747554d ntdll!_C_specific_handler+0x8c
00000000`04b9cee0 00000000`77455d1c ntdll!RtlpExecuteHandlerForException+0xd
00000000`04b9cf10 00000000`7748fe48 ntdll!RtlDispatchException+0x3cb
00000000`04b9d5f0 00000000`77491da0 ntdll!KiUserExceptionDispatcher+0x2e (TrapFrame @ 00000000`04b9da18)
00000000`04b9dbb0 000007fe`fbd6254a ntdll!RtlFreeHeap+0xd0
00000000`04b9dc30 000007fe`f6cd13ca FLTLIB!FilterConnectCommunicationPort+0x1da
00000000`04b9dd30 000007fe`f6cd4033 EstSbFile!EstSbFileManager+0x6a [e:\ronggf\work\branches\minsheng\client_windows\src\estsbfile08\devicectrol.cpp @ 345]
00000000`04b9dda0 00000001`3f1c43d1 EstSbFile!EstLogInformationPrint+0x103 [e:\ronggf\work\branches\minsheng\client_windows\src\estsbfile08\estsbfile.cpp @ 1684]
00000000`04b9de20 00000001`3f1c4545 SandboxMgr!myout::print+0x31 [e:\ronggf\work\branches\minsheng\client_windows\src\include\misc\myout.h @ 100]
00000000`04b9de50 00000001`3f1fa789 SandboxMgr!global::trace::output_trace+0x165 [e:\ronggf\work\branches\minsheng\client_windows\src\include\misc\trace.h @ 18]
00000000`04b9ef40 00000001`3f1f27c9 SandboxMgr!CIconSnap::Save+0x109 [e:\ronggf\work\branches\minsheng\client_windows\src\sandboxmgr\iconsnap.cpp @ 122]
00000000`04b9f2b0 00000001`3f1f6782 SandboxMgr!ui::wnd::CDesktopWnd::Exe2Shortcut+0x1c9 [e:\ronggf\work\branches\minsheng\client_windows\src\sandboxmgr\desktopwnd.cpp @ 579]
00000000`04b9f430 00000001`3f1f06e0 SandboxMgr!ui::wnd::CDesktopWnd::OnRecvOpenDialogData+0x42 [e:\ronggf\work\branches\minsheng\client_windows\src\sandboxmgr\desktopwnd.cpp @ 1574]
00000000`04b9f480 00000001`3f1f9f96 SandboxMgr!ui::w
nd::CDesktopWnd::ProcessWindowMessage+0x4c0 [e:\ronggf\work\branches\minsheng\client_windows\src\sandboxmgr\desktopwnd.h @ 130]
00000000`04b9f4d0 00000000`7735c3c1 SandboxMgr!ATL::CWindowImplBaseT<ATL::CWindow,ATL::CWinTraits<1442840576,0> >::WindowProc+0xc6 [c:\program files (x86)\microsoft visual studio 9.0\vc\atlmfc\include\atlwin.h @ 3081]
00000000`04b9f590 00000000`7735c60a USER32!UserCallWinProcCheckWow+0x1ad
00000000`04b9f650 00000001`3f208478 USER32!DispatchMessageWorker+0x3b5
00000000`04b9f6d0 00000001`3f35a65b SandboxMgr!CVirtualDesktop::NewDesktopRunLoop+0x478 [e:\ronggf\work\branches\minsheng\client_windows\src\sandboxmgr\virtualdesktop.cpp @ 328]
00000000`04b9fac0 00000001`3f35a70f SandboxMgr!_callthreadstartex+0x17 [f:\dd\vctools\crt_bld\self_64_amd64\crt\src\threadex.c @ 348]
00000000`04b9faf0 00000000`7723f56d SandboxMgr!_threadstartex+0x9f [f:\dd\vctools\crt_bld\self_64_amd64\crt\src\threadex.c @ 326]
00000000`04b9fb20 00000000`77473281 kernel32!BaseThreadInitThunk+0xd
00000000`04b9fb50 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
在RtlFreeHeap 中出现问题了。
这个时候怎么办呢?从现场来看一定是堆出问题胃,那FLB 或者ntdll 上查找是找不出头绪的。这个时候想到了AppVerifier 和gflags 都可以用来进行堆的分配验证,释放验证。AppVerifier 使用比较方便,因此就使用AppVerifier 了。
使用AppVerifier后,果然出现了问题,提示在PathRenameExtension处出现问题了。
直接说结果吧:
因为CString 的 GetBuffer 直接传0,没有分配多余的空间导致PathRenameExtension 在后面追加了.txt四个字节,导致写内存超出了分配的内存范围,然后在释放内存的时候,内存管理检查到堆结构边界被破坏了,检查了出来。而原来的崩溃,是因为写的几个字节破坏了堆分配表,而应声而崩。
再看看上面的代码有下面的问题(浅析了),还有什么问题请大家补充。
void ui::wnd::CDesktopWnd::Exe2Shortcut( LPCWSTR strFullPath, LPCWSTR strFileName, LPCWSTR shelllink_path) { CString strDescName = strFileName; if(strDescName == _T("iexplore.exe")) //不严谨 { strDescName = _T("Internet Explorer"); } PathRenameExtension(strDescName.GetBuffer(0), _T("")); //上述条件成立的时候不是必须的 CString strFileNameTmp = strFileName; CString strLnk = GetLnkPath(); PathRenameExtension(strFileNameTmp.GetBuffer(0), DESKTOPWND_SHORTCUT_EXT); //有问题代码,因为 //CString::GetBuffer 没有增大内存导致当strFileName 的扩展名没有或者比替换的扩展名长度小的时候,会出现向未分配的内存写数据 CString strLnkFileTxt = strLnk + _T("\\") + strFileNameTmp; //效率很低,两个临时变量,两次析构 strFileNameTmp = strFileName; PathRenameExtension(strFileNameTmp.GetBuffer(0), _T(".bmp")); //和上述同样问题 CString strIcoPath = strLnk + _T("\\") + strFileNameTmp; // CIconSnap is; // is.GetIcon(strFullPath); CIconSnap iconsnap; if(FALSE == iconsnap.Save(strFullPath, strIcoPath)) { SIMPLE_TRACE(L"提取图标失败!"); //::MessageBox(m_hWnd, _T("提取图标失败!"), _T("消息"), 0); } else { AppLogicalContainer iconContainer; BOOL bAddSuc = TRUE; if(shelllink_path==0) { if (strDescName != _T("Internet Explorer")) { bAddSuc = iconContainer.AddApp(strFullPath,strIcoPath,strDescName, shelllink_path); } if (bAddSuc) { CString strSkinPath = GetSkinPath(); m_IconListView.AddButtonEx( strDescName, strFullPath , m_hWnd, HandlerParam(this, OnBtnMessage), strIcoPath, strSkinPath + _T("\\tools\\desktop_icon_back3.png"), strSkinPath + _T("\\tools\\desktop_icon_back1.png")); } } else if (shelllink_path) { CString strSkinPath = GetSkinPath(); m_IconListView.AddButtonEx( strDescName, shelllink_path , m_hWnd, HandlerParam(this, OnBtnMessage), strIcoPath, strSkinPath + _T("\\tools\\desktop_icon_back3.png"), strSkinPath + _T("\\tools\\desktop_icon_back1.png")); } } }
经上分析,这个编写者没有掌握好CString 的用法和对API PathRenameExtension 的粗心大意。这段代码可以作为纠错的一个范例。