莫名的问题,疑为内存泄漏,但是查了几天也没查出头绪
最近写了个游戏辅助,其中有一个扫描游戏角色的功能.
子线程会遍历句柄,判定窗口标题,符合则角色放入列表框以及全局角色参数类数组中,如果角色下线就从列表框移除,并且调用类成员清除对应数组的数据.
以下是实现代码:
class EVECharacter{//角色参数类
public:
CString Name;
HWND Hwnd;
bool Threadrun;
int NumInSelection;
CString HostileMode;
CString BattleMode;
int HPMode;
int WarpKM;
int AntiBan;
int AtSafePoint;
CString RepairMode;
int Friendly_Corp;
int Friendly_Aillance;
int Friendly_Execellent;
int Friendly_Good;
CString ComplexSelect;
CString RepairType;
CString ChargeSetting;
CString SalvageBookmark;
CString DronesAttack;
CString KeepInRange;
int ComplexDelay;
CString AfterBurner;
CString BusyReturn;
CString Refouced;
CString EasyMode;
CString RiskSetting;
int SystemFont;
int beep;
int Gotosafe;
int SelfCheck;
EVECharacter()
{
AtSafePoint=1;
beep=0;
Gotosafe=0;
Hwnd=0;
Name="";
Threadrun=false;
NumInSelection=-1;
HostileMode="";
BattleMode="";
HPMode=0;
WarpKM=0;
AntiBan=0;
SelfCheck=0;
RepairMode="";
int Friendly_Corp=0;
int Friendly_Aillance=0;
int Friendly_Execellent=0;
int Friendly_Good=0;
ComplexSelect="";
RepairType="";
ChargeSetting="";
SalvageBookmark="";
DronesAttack="";
KeepInRange="";
ComplexDelay=0;
AfterBurner="";
BusyReturn="";
Refouced="";
EasyMode="";
SystemFont=0;
RiskSetting="";
}
int EmptyData()
{
AtSafePoint=1;
beep=0;
Gotosafe=0;
Threadrun=false;
NumInSelection=-1;
HostileMode="";
BattleMode="";
HPMode=0;
WarpKM=0;
AntiBan=0;
SelfCheck=0;
RepairMode="";
int Friendly_Corp=0;
int Friendly_Aillance=0;
int Friendly_Execellent=0;
int Friendly_Good=0;
ComplexSelect="";
RepairType="";
ChargeSetting="";
SalvageBookmark="";
DronesAttack="";
KeepInRange="";
ComplexDelay=0;
AfterBurner="";
BusyReturn="";
Refouced="";
EasyMode="";
SystemFont=0;
RiskSetting="";
return 0;
}
};
DWORD WINAPI TR_Char(PVOID lpParameter)
{
while(1)
{
Sleep(2000);
int *p = (int *)lpParameter;
HWND Hwnd;
Hwnd=GetDesktopWindow();
Hwnd=GetWindow(Hwnd,GW_CHILD);
char WindowTitle[MAX_PATH] = {0};
PSTR EVETitle = "EVE -";
HWND list;
int Arrlocation=0;
int WindowTitleLen=0;
int TimesOfWhile=0;
int userselection=-1;
int findstr;
int listcount=-1;
list=GetDlgItem(hDlgWnd,IDC_CHARLIST);
listcount=SendMessage(list,LB_GETCOUNT,NULL,NULL);//取得列表框条目数
CString Charactername;
for(int i=0;i<=100;i++)//刷新角色数组
EVECharHwndNow[i]=0;
while(Hwnd)//遍历句柄
{
/*WindowTitleLen=GetWindowTextLength(Hwnd);
WindowTitle = (PSTR) VirtualAlloc((LPVOID) NULL,
(DWORD) (WindowTitleLen + 1), MEM_COMMIT,
PAGE_READWRITE); */
GetWindowText(Hwnd,WindowTitle,MAX_PATH);
if(strstr(WindowTitle,EVETitle))
{
if(FindCharInArr(Hwnd)==101)//检查角色是否已经在数组中
{
Arrlocation=FindEmptyArrLocation();
Charactername=WindowTitle;
Charactername=Charactername.Mid(6);
EVEChar[Arrlocation].Name=Charactername;
EVEChar[Arrlocation].Hwnd=Hwnd;
SendMessage(list,LB_ADDSTRING ,NULL,(LPARAM)Charactername.GetBuffer());
}
EVECharHwndNow[TimesOfWhile++]=Hwnd;//将角色句柄添加到临时数组
}
Hwnd=GetWindow(Hwnd,GW_HWNDNEXT);
}
if(listcount==0)//如果列表框没有角色
{
findstr=SendMessage(list,LB_FINDSTRING,0,(LPARAM)"没有检测到角色..");//没有角色则显示没有角色
if(findstr!=-1)
{
}
else
{
SendMessage(list,LB_ADDSTRING,NULL,(LPARAM)("没有检测到角色.."));
}
}
else
{
findstr=SendMessage(list,LB_FINDSTRING,0,(LPARAM)"没有检测到角色..");
if((findstr!=-1 && listcount>1))
{
SendMessage(list,LB_DELETESTRING,findstr,NULL);
}
}
DelAllUnexistChar();//删除已经下线的角色
}
return 0;
}
int FindCharInArr(HWND Hwnd)
{
for(int i=0;i<=100;i++)
{
if(EVEChar[i].Hwnd==Hwnd)
return i;
}
return 101;
}
int FindEmptyArrLocation()
{
for(int i=0;i<=100;i++)
{
if(!EVEChar[i].Hwnd)
return i;
}
}
int DelAllUnexistChar(){
for(int i=0;i<=100;i++){
bool characterfind=false;
for(int j=0;j<=100;j++)
{
if(EVEChar[i].Hwnd==EVECharHwndNow[j])
{
characterfind=true;
break;
}
}
if(characterfind!=true)//标记该角色为无效
{
Addlog(EVEChar[i].Name,"角色已下线,清除角色.");
// EVEChar[i].Hwnd=0;
// EVEChar[i].Threadrun = false;
//EVEChar[i].EmptyData();
int findstr;
int listcount=-1;
HWND list;
list=GetDlgItem(hDlgWnd,IDC_CHARLIST);
findstr=SendMessage(list,LB_FINDSTRING,0,(LPARAM)EVEChar[i].Name.GetBuffer());
SendMessage(list,LB_DELETESTRING,findstr,NULL);
}
}
return 0;
}
但我发现程序执行了一段时间以后,没有下线的角色会被莫名的被程序判定为下线.
我屡次检查了几次代码,发现被判定为下线时,EVEChar[i].Hwnd内容正确,但EVECharHwndNow数组中整个数组都是空的(遍历筛选标题出错了?)
我后来就试着将DelAllUnexistChar中清空数据的代码注释了,程序恢复正常
但长期运行一段时间后程序却莫名崩溃.
我怀疑还是我贴出的窗口遍历这些代码的某一处出了问题,虽然多线程没锁,但是都是读操作没有写操作(只有写在同一全局变量类中其他数组中的,这个影响不影响吧?)
所以我怀疑还是某一处内存泄漏或者逻辑错了,可是自己却死活找不到问题的所在.
问题又非常难以重现,几个小时,十几个小时的运行才出现问题. 真心无解了
谁能帮帮我?
[解决办法]
运行后,你打开任务管理器查看对应进程的”句柄数“或”GDI对象“是否不断的增加?
[解决办法]
不是被TR_Char的while(1)包在里面么?你while(1)里开线程结束线程?
[解决办法]
>仔细看了看,修复了这个问题,只在TR_Char的DelAllUnexistChar把EVEChar[i].Threadrun = false,原子读写应该没有问题
>然后在另一个线程中while EVEChar[i].Threadrun = ture后面的语句加上了emptydata。
你没修复。你这就等于是一个用普通变量threadrun当做mutex保护这个object。这肯定是过不了压力测试的。
[解决办法]
检查是否资源泄漏的办法之一:
在任务管理器 进程 查看 选择列 里面选择:内存使用、虚拟内存大小、句柄数、线程数、USER对象、GDI对象
让你的程序(进程)不退出,循环执行主流程很多遍,越多越好,比如1000000次甚至无限循环,记录以上各数值,再隔至少一小时,越长越好,比如一个月,再记录以上各数值。如果以上两组数值的差较大或随时间流逝不断增加,则铁定有对应资源的资源泄漏!
崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处。
判断是否越界访问,可以在数组的最后一个元素之后对应的地址处设置数据读写断点。如果该地址对应其它变量干扰判断,可将数组多声明一个元素,并设置数据读写断点在该多出元素对应的地址上。