我所遇见的坑(二)

起因

前些天,查找一个ActiveX项目的问题。问题比较难定位,并且源码不怎么易读。表现:在绝大多数电脑上运行没有问题,但在个别电脑上100%会出现IE崩溃,并且在一些其他电脑上操作多次也会出现IE崩溃。经过多番调试,总算找到了原因。原来罪魁祸首是写文件日志 。

stream != NULL

首先先看这个报错图:

stream

再看一下项目的代码(已经经过略微修改):

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)//执行100次跳出
{
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需要调用硬件,那么就会出现线程竞争一个文件的问题。这个问题主要出现写代码不严谨,很多时候一点细微的地方会影响整个项目的运行。解决方案:在写日志的时候加锁即可。

评论