WEB开发网
开发学院软件开发VC 管理TM群 阅读

管理TM群

 2007-03-15 21:54:42 来源:WEB开发网   
核心提示: 二、问题的分析我在实现时使用的是TM2006新春版,在群聊天的窗口里点工具务栏上的"群设置",弹出了"群设置"窗口,在这个窗口里,选择"成员列表"这一项,右边有一个list,这个list就包含有所有的成员了,当选中了某个人后,就可

二、问题的分析

我在实现时使用的是TM2006新春版,在群聊天的窗口里点工具务栏上的"群设置",弹出了"群设置"窗口,在这个窗口里,选择"成员列表"这一项,右边有一个list,这个list就包含有所有的成员了,当选中了某个人后,就可把它T了.

图一 群设置窗口

现在的问题是要先把想T的人找出来.怎么样在list中把想T的人给找出来呢,我的想法是枚举这个list里所有人的QQ号,然后跟想要T的QQ号作比较,如果有相同的,就把list里的这一项选中,然后我们就可以进一步的操作了.那现在就可以把问题转化为,枚举list,获得list里的项的信息.我用spy++查看了一下那个"群设置"窗口,如图所示:

图二 用spy++查看的窗口关系

最顶层的就是那个"群设置"窗口了,那个显示成员的list原来是一个syslistview32类型的控件,包含在一个类型为"#32770"的dialog中,我们只要顺着最顶层的窗口中,一层层的窗口找下去就可以得到我个想要的那个list窗口的名柄了,呵呵,之后,就可以向这个list发出消息,让它干活了.想到这,偶心时狂喜了一阵子.但是在实现过程中却是不是那么的顺利的.在顶层窗口中找那个list的句柄,并不是什么困难的事.把桌面上的把有窗口都枚举一遍,就要以找到窗口名为”群设置”的窗口了.

EnumWindows(EnumWindowsProc,0);//枚举所有的窗口
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
  TCHAR buff[1000];
  int buffsize(100),nPosition(-1);
  HWND hQQWnd=NULL;
  ::GetWindowText(hwnd,buff,buffsize);
  if (strlen(buff)<1)
    return TRUE;
  CString str(buff);
  nPosition=str.Find(_T("群设置")); //这里设置要找的窗口名
  if(nPosition!=-1)
    EnumChildWindows(hwnd,ChildWndProc,0);//继续找子窗口
  return TRUE;
}
//枚举包含有syslistview32的类型为#32770的窗口的句柄
BOOL CALLBACK ChildWndProc(HWND hwnd, LPARAM lParam)
{
  LPTSTR lptstr;
  HGLOBAL hglb=NULL;
  char className[CLASS_SIZE];
  if (GetClassName(hwnd,className,CLASS_SIZE)==0)
    return TRUE;
  CString str(className);
  HWND hChild = GetWindow(hwnd, GW_CHILD);
  if (GetClassName(hChild,className,CLASS_SIZE)==0)
    return TRUE;
  CString strChildName(className);
  //顶层窗口下有四个类型都为”#32770”的dialog,其中只有其中一个
  //才是包括有成员列表的
  while (str != _T("#32770") || strChildName != _T("SysListView32"))
  {
    HWND h1= GetNextWindow(hwnd, GW_HWNDNEXT);
    GetClassName(h1,className,CLASS_SIZE);
    str = className;
    hwnd = h1;
    
    hChild = GetWindow(hwnd, GW_CHILD);
    if (GetClassName(hChild,className,CLASS_SIZE)==0)
      return TRUE;
    strChildName =className;
  }
  EnumChildWindows(hwnd,DeleWndProc,0);//在包含syslistview的dialog中继续找
}
找到了包含有syslistview的窗口后,就继续找syslistview了,然后可以向它发送命令消息了.这是整个程序的关键部分,先把代码给出来,我再进行解释.#define CLASS_SIZE 4096
BOOL CALLBACK DeleWndProc(HWND hwnd, LPARAM lParam)
{
  LPTSTR lptstr;
  HGLOBAL hglb=NULL;
  char className[CLASS_SIZE];
  if (GetClassName(hwnd,className,CLASS_SIZE)==0)
    return TRUE;
  CString str(className);
  char sz[254] ="";
  if (_T("SysListView32") == str)
  {
    int iItem=0;
    int iFoundFlag = 0;//if find the qq number, iFoundFlag = 1;else 0;
    LVITEM lvitem,lvitem1, *plvitem,*plvitem1;
    DWORD PID;
    HANDLE hProcess;
    char ItemBuf[512],*pItem;
    GetWindowThreadProcessId(hwnd, &PID);  
    hProcess=OpenProcess(PROCESS_ALL_ACCESS,false,PID);
    if (!hProcess)
    {
      MessageBox(NULL,"获取进程句柄操作失败!","错误!",NULL);
    }
    else
    {
      plvitem=(LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
      plvitem1=(LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
      
      pItem=(char*)VirtualAllocEx(hProcess, NULL, 5120, MEM_COMMIT, PAGE_READWRITE);
      
      if (!plvitem)
      {
        MessageBox(NULL,"无法分配内存!","错误!",NULL);
      }      
      else
      {
        int nItemCount = ::SendMessage(hwnd, LVM_GETITEMCOUNT, 0 ,0);
        lvitem.mask=LVIF_TEXT;
        lvitem.cchTextMax=512;
        lvitem.iSubItem=1; //ProcessName
        lvitem.pszText=pItem;
        WriteProcessMemory(hProcess, plvitem, &lvitem, sizeof(LVITEM), NULL);
        lvitem1.state=LVIS_SELECTED;
        lvitem1.stateMask=LVIS_SELECTED;
        WriteProcessMemory(hProcess, plvitem1, &lvitem1, sizeof(LVITEM), NULL);
                for(; iItem<nItemCount; iItem++)
        {
          SendMessage(hwnd, LVM_GETITEMTEXT, (WPARAM)iItem, (LPARAM)plvitem);
          
          ReadProcessMemory(hProcess, pItem, ItemBuf, 512, NULL);
          
          CString strItem(ItemBuf);
                    //strQQNum就是要找的QQ号码了
          if(strQQNum == strItem)
          {
            SendMessage(hwnd, LVM_SETITEMSTATE, (WPARAM)iItem, (LPARAM)plvitem1);
            iFoundFlag = 1;
            break;
          }        
        }
        if(0 == iFoundFlag)
        {
          CString str;
          str = "没有找到QQ号:
";
          str += strQQNum;
          MessageBox(NULL, str, "提醒", NULL);
        }
        
        
      }
    }
    CloseHandle(hProcess);
    VirtualFreeEx(hProcess, pItem, 0, MEM_RELEASE);
    VirtualFreeEx(hProcess, plvitem1,0, MEM_RELEASE);
    VirtualFreeEx(hProcess, plvitem, 0, MEM_RELEASE);
  }
  return TRUE;
}
DeleWndProc函数主要是把枚举syslistview32的项,查找出我们想要找的QQ号,并选中. 最初时我是尝试用以下的代码去得到list的item的内容的.TCHAR szText[100];
LV_ITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = nIndex;
lvi.iSubItem = 0;
lvi.pszText = szText;
lvi.cchTextMax = 100;
ListView_GetItem(hwndLV, &lvi);

但却会报错误,存取错误,也就是说内存方面的问题了.问题定位到了ListView_GetItem(hwndLV, &lvi);这一句了.后来我查找了很多资料才知道为什么会有错误.因为我的程序与TM的程序是分别属于不同的Progress,我在自己的程序的进程中申请了lvi的内存空间,却希望把TM进程往这个内存空间去写入数据,当然是会有错误啦. Windows用到了虚存这个概念,它让每个程序都觉得自己占有2G的内存,每个程序都把自己用到的数据放在这2G的内存中去运行.每个程序间的内存空间是互不相干的,这样,如果某个程序出现了问题,也不会影响到其它程序的运行了.ListView_GetItem要往TM的程序里写数据,当然这样的数据只能保存在TM这个程序的内存空间里了.我们可以用VirtualAllocEx这个函数在TM这个程序运行的内存片中申请内存空间,这样ListView_GetItem就可以向这个新申请的空间中写入数据了.然后,我们再用ReadProcessMemory函数把新申请的空间中的数据读到自己程序进程里的缓冲区中去,采用了一个曲折的办法,实现了不同进程的数据交换.最后当然要把申请的空间用VirtualFreeEx释放,要不就会有内存泄漏了.

三、问题的解决

Tags:管理 TM

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接