莫名的问题,疑为内存泄漏,但是查了几天也没查出头绪

kkkgho 2013-03-16 06:49:20
最近写了个游戏辅助,其中有一个扫描游戏角色的功能.

子线程会遍历句柄,判定窗口标题,符合则角色放入列表框以及全局角色参数类数组中,如果角色下线就从列表框移除,并且调用类成员清除对应数组的数据.

以下是实现代码:

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中清空数据的代码注释了,程序恢复正常

但长期运行一段时间后程序却莫名崩溃.

我怀疑还是我贴出的窗口遍历这些代码的某一处出了问题,虽然多线程没锁,但是都是读操作没有写操作(只有写在同一全局变量类中其他数组中的,这个影响不影响吧?)

所以我怀疑还是某一处内存泄漏或者逻辑错了,可是自己却死活找不到问题的所在.

问题又非常难以重现,几个小时,十几个小时的运行才出现问题. 真心无解了

谁能帮帮我?
...全文
316 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2013-03-29
  • 打赏
  • 举报
回复
引用 10 楼 kkkgho 的回复:
引用 9 楼 zhao4zhong1 的回复:检查是否资源泄漏的办法之一: 在任务管理器 进程 查看 选择列 里面选择:内存使用、虚拟内存大小、句柄数、线程数、USER对象、GDI对象 让你的程序(进程)不退出,循环执行主流程很多遍,越多越好,比如1000000次甚至无限循环,记录以上各数值,再隔至少一小时,越长越好,比如一个月,再记录以上各数值。如果以上两组数值的差……
判断是否越界访问,可以在数组的最后一个元素之后对应的地址处设置数据读写断点。如果该地址对应其它变量干扰判断,可将数组多声明一个元素,并设置数据读写断点在该多出元素对应的地址上。
kkkgho 2013-03-29
  • 打赏
  • 举报
回复
引用 9 楼 zhao4zhong1 的回复:
检查是否资源泄漏的办法之一: 在任务管理器 进程 查看 选择列 里面选择:内存使用、虚拟内存大小、句柄数、线程数、USER对象、GDI对象 让你的程序(进程)不退出,循环执行主流程很多遍,越多越好,比如1000000次甚至无限循环,记录以上各数值,再隔至少一小时,越长越好,比如一个月,再记录以上各数值。如果以上两组数值的差较大或随时间流逝不断增加,则铁定有对应资源的资……
最终查了很久,发现原来是有一个地方访问HTTP,返回HTML的数组划分的小了 在极少数情况下会造成下标越界,哎,真是白瞎了这么久!
赵4老师 2013-03-17
  • 打赏
  • 举报
回复
检查是否资源泄漏的办法之一: 在任务管理器 进程 查看 选择列 里面选择:内存使用、虚拟内存大小、句柄数、线程数、USER对象、GDI对象 让你的程序(进程)不退出,循环执行主流程很多遍,越多越好,比如1000000次甚至无限循环,记录以上各数值,再隔至少一小时,越长越好,比如一个月,再记录以上各数值。如果以上两组数值的差较大或随时间流逝不断增加,则铁定有对应资源的资源泄漏! 崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处。 判断是否越界访问,可以在数组的最后一个元素之后对应的地址处设置数据读写断点。如果该地址对应其它变量干扰判断,可将数组多声明一个元素,并设置数据读写断点在该多出元素对应的地址上。
FancyMouse 2013-03-17
  • 打赏
  • 举报
回复
引用 6 楼 kkkgho 的回复:
引用 5 楼 FancyMouse 的回复:引用 3 楼 kkkgho 的回复:引用 1 楼 FancyMouse 的回复:你emptydata不就是写操作了么。而且是对整个object写了一遍。什么叫没有写操作。 哪个是线程结束的时候才执行的,执行完线程就没了,应该不影响吧? 不是被TR_Char的while(1)包在里面么?你while(1)里开线程结束线程?……
>仔细看了看,修复了这个问题,只在TR_Char的DelAllUnexistChar把EVEChar[i].Threadrun = false,原子读写应该没有问题 >然后在另一个线程中while EVEChar[i].Threadrun = ture后面的语句加上了emptydata。 你没修复。你这就等于是一个用普通变量threadrun当做mutex保护这个object。这肯定是过不了压力测试的。
kkkgho 2013-03-17
  • 打赏
  • 举报
回复
引用 2 楼 fthislife 的回复:
运行后,你打开任务管理器查看对应进程的”句柄数“或”GDI对象“是否不断的增加?
我查了一下,有些不太对劲 程序刚打开就有三个线程和27个GDI对象(可是程序这时候是单线程的) 通过注册码验证机制登录以后,心跳包线程启动,这时候应该是二个线程 可是任务管理器中显示有六个。 然后再激活一个角色的脚本线程以后,线程为七个,GDI稳定在36,没有变化,停止脚本线程后也正常恢复成了六个线程。 究竟是哪里出问题了呢?
kkkgho 2013-03-17
  • 打赏
  • 举报
回复
引用 5 楼 FancyMouse 的回复:
引用 3 楼 kkkgho 的回复:引用 1 楼 FancyMouse 的回复:你emptydata不就是写操作了么。而且是对整个object写了一遍。什么叫没有写操作。 哪个是线程结束的时候才执行的,执行完线程就没了,应该不影响吧? 不是被TR_Char的while(1)包在里面么?你while(1)里开线程结束线程?
仔细看了看,修复了这个问题,只在TR_Char的DelAllUnexistChar把EVEChar[i].Threadrun = false,原子读写应该没有问题 然后在另一个线程中while EVEChar[i].Threadrun = ture后面的语句加上了emptydata。 emptydata完了线程就结束了,这方面应该没有问题了 但是,我现在长时间的测试以后,发现BUG来自于EVECharHwndNow[] 这是一个我通过窗口标题过滤出角色名的列表的对照用句柄数组 每次遍历一次句柄,这个数组就会被清空后重新被符合筛选的角色的游戏窗口句柄赋值以确保这个数组中的角色(句柄)都是最新的,有效的,然后用这个最新的数组和角色参数数组类进行对比。 但我发现,似乎句柄遍历有时候会失败。 我在DelAllUnexistChar()中加上调试输出语句以后,发现长期运行一段时间程序后 即使角色的窗口没有关闭时,DelAllUnexistChar中的if(characterfind!=true)也会生效。 导致角色明明没有下线,却被判定成角色下线,释放了相关内存。 通过断点,我发现EVECharHwndNow数组中没有任何句柄,而按照我的遍历逻辑来看,这是无论如何都不应该发生的 为什么长时间运行以后,遍历出来的窗口标题,经过筛选以后没有被赋给EVECharHwndNow数组呢? 真是让我百思不得其解
fthislife 2013-03-16
  • 打赏
  • 举报
回复
运行后,你打开任务管理器查看对应进程的”句柄数“或”GDI对象“是否不断的增加?
FancyMouse 2013-03-16
  • 打赏
  • 举报
回复
你emptydata不就是写操作了么。而且是对整个object写了一遍。什么叫没有写操作。
FancyMouse 2013-03-16
  • 打赏
  • 举报
回复
引用 3 楼 kkkgho 的回复:
引用 1 楼 FancyMouse 的回复:你emptydata不就是写操作了么。而且是对整个object写了一遍。什么叫没有写操作。 哪个是线程结束的时候才执行的,执行完线程就没了,应该不影响吧?
不是被TR_Char的while(1)包在里面么?你while(1)里开线程结束线程?
kkkgho 2013-03-16
  • 打赏
  • 举报
回复
引用 2 楼 fthislife 的回复:
运行后,你打开任务管理器查看对应进程的”句柄数“或”GDI对象“是否不断的增加?
稍等,我马上看一下
kkkgho 2013-03-16
  • 打赏
  • 举报
回复
引用 1 楼 FancyMouse 的回复:
你emptydata不就是写操作了么。而且是对整个object写了一遍。什么叫没有写操作。
哪个是线程结束的时候才执行的,执行完线程就没了,应该不影响吧?

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧