起因
前些天,查找一个ActiveX项目的问题。问题比较难定位,并且源码不怎么易读。表现:在绝大多数电脑上运行没有问题,但在个别电脑上100%会出现IE崩溃,并且在一些其他电脑上操作多次也会出现IE崩溃。经过多番调试,总算找到了原因。原来罪魁祸首是写文件日志 。
stream != NULL
首先先看这个报错图:
再看一下项目的代码(已经经过略微修改):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <afx.h> #include "stdafx.h"
#include <iostream> #include <fstream> #include <afxdisp.h> using namespace std; void write(CString str,CString sErrorMsg) { fstream _file; _file.open(str+L"/logs", ios::in); if(!_file) { ::CreateDirectory(str+L"/logs/",NULL); } CStdioFile* pTxtFile = new CStdioFile(); try{
COleDateTime time=COleDateTime::GetCurrentTime(); CString logname=L"/"+time.Format(L"%Y-%m-%d")+L".log";
CStdioFile* pTxtFile = new CStdioFile();
if(!pTxtFile->Open(str+L"/logs/"+logname,CFile::modeReadWrite | CFile::typeText)) { pTxtFile->Open(str+L"/logs/"+logname,CFile::modeWrite | CFile::modeCreate); } try { if(pTxtFile->GetLength()>50*1024*1024) { pTxtFile->SetLength(0); } CString str=time.Format(L"%Y-%m-%d %H:%M:%S") + L"------" + sErrorMsg;
pTxtFile->SeekToEnd(); pTxtFile->WriteString(str); pTxtFile->WriteString(L"\n"); pTxtFile->Close(); }catch (...) { pTxtFile->Close(); } delete pTxtFile; }catch(...){ delete pTxtFile; } } int _tmain(int argc, _TCHAR* argv[]) { for(int i=0;i<1000;i++) { write("C:\\","err2"); } system("pause"); return 0; }
|
项目里是固定写C盘,这点很尴尬,因为C盘你不一定有权限写文件,而且用户电脑有可能就没有C盘这个盘符(是的,Windows并没有规定必须有C盘)。考虑到用户安装系统可能装在其他盘,以及权限问题,解决这个问题很简单,判断一下当前系统的盘符,同时申请管理员权限,保证能有写权限即可。
下面来看为什么有的电脑偶尔会出现IE崩溃的问题:
首先要知道IE标签页是多线程的,意味着每开一个标签页都需要多出几个线程(至于为什么不是多出来一个,你认为一个线程能支撑起一个标签页的所有活动吗?)。这样就会涉及到多线程写文件的问题,本着严谨追求的态度,特意去模拟了下该场景,以下代码多运行几次,就会报错:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| #include <afx.h> #include "stdafx.h"
#include <iostream> #include <fstream> #include <afxdisp.h> using namespace std; void write(CString str,CString sErrorMsg) { fstream _file; _file.open(str+L"/logs", ios::in); if(!_file) { ::CreateDirectory(str+L"/logs/",NULL); } CStdioFile* pTxtFile = new CStdioFile(); try{
COleDateTime time=COleDateTime::GetCurrentTime(); CString logname=L"/"+time.Format(L"%Y-%m-%d")+L".log";
CStdioFile* pTxtFile = new CStdioFile();
if(!pTxtFile->Open(str+L"/logs/"+logname,CFile::modeReadWrite | CFile::typeText)) { pTxtFile->Open(str+L"/logs/"+logname,CFile::modeWrite | CFile::modeCreate); } try { if(pTxtFile->GetLength()>50*1024*1024) { pTxtFile->SetLength(0); } CString str=time.Format(L"%Y-%m-%d %H:%M:%S") + L"------" + sErrorMsg;
pTxtFile->SeekToEnd(); pTxtFile->WriteString(str); pTxtFile->WriteString(L"\n"); pTxtFile->Close(); }catch (...) { pTxtFile->Close(); } delete pTxtFile; }catch(...){ delete pTxtFile; } } unsigned long WINAPI Fun(LPVOID lpParamter) { int iRunTime = 0; while(++iRunTime<1000) { write("C:\\","err1"); } ExitThread(-1); } void Work() { unsigned long ulThreadId = 0; HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, &ulThreadId); } int _tmain(int argc, _TCHAR* argv[]) { Work(); for(int i=0;i<1000;i++) { write("C:\\","err2"); } system("pause"); return 0; }
|
虽然在项目中我们并没有写多线程,但是IE是多线程的。实际项目中ActiveX需要调用硬件,那么就会出现线程竞争一个文件的问题。这个问题主要出现写代码不严谨,很多时候一点细微的地方会影响整个项目的运行。解决方案:在写日志的时候加锁即可。