运行外部程序,可以用spy++得到该checklistbox的句柄 ,可是控件内还有不少子项的checkbox,这些东东没有句柄,如何选定其中某一项并设为enabled呢?万分感谢回答的朋友。

解决方案 »

  1.   

    参考"读取其他程序中StringGrid内容"一文,注入到目标进程中,创建一个隐形窗口,通过消息获取到该CheckListBox的实例指针,然后通过调用TCheckListBox的相应方法实现你的要求。去大富翁搜索。
      

  2.   

    应该和我问的问题差不多,有高人的话帮我的问题也一起解决了吧!http://community.csdn.net/Expert/TopicView1.asp?id=5308447
      

  3.   

    获取其他App中的StringGrid内容 
    作者:lichengbin 
    关键字:无 1.根据StringGrid组件的句柄,想直接通过消息如WM_GETTEXT等来获取StringGrid的内容,显然不可行,普通的Windows消息不能直接获取到某个Cell的内容,TStringGrid并不是Windows的组件,而是Delphi自己的组件,它的Cell内容是存放在TStrings列表中的。
    2.根据鼠标所在的位置,通过屏幕取词的技术,是可以Hook到鼠标下的某个Cell的内容,但在实际应用中也不可行,因为不是所有想Hook的Cell都是在显示区可见的,而ApiHook的屏幕取词技术要求Cell的文本重画,才能取得其内容。既然上面两种方法都不可行,那么我们就要寻找新的方法。
    1.思考
    由于StringGrid是Delphi自写的组件,而不是Windows的控件,因此要获取StringGrid的某个Cell的内容,只能是获取到StringGrid的对象实例,才能以StringGrid.Cell[X, Y]这样的方式取得任意Cell的内容。
    2.启发
    在Delphi中,在自己的程序中,用FindControl,是可以根据组件对象的句柄获得对象实例的。那么我们在Hook其他程序的时候能不能采用这种方法来获取StringGrid对象的实例呢?这有个问题,即使我们能够获取到StringGrid的对象实例,但是返回的实例地址是个在其他程序进程地址空间的私有地址(<2GB),这在我们的程序中是无法进行访问的。这个问题不难解决,屏幕取词技术不也是截获到了其他程序的屏幕输出文本吗!?
    3.实现
    有了上面的思考和启发,那我们就能整理出一个思路来啦。
    a.写一个DLL,将Hook StringGrid的代码都放在DLL中,在DLL中,还要包括GetMessage钩子的代码,目的是为了能通过全局钩子将DLL注入到目标程序,使Hook StringGrid的代码运行在目标进程中,这样就可以正确使用FindControl返回的StringGrid实例(也只有在目标进程内调用FindControl,才能够返回StringGrid实例地址)。
    b.注入到目标进程的Hook功能,我们怎么进行控制呢?我们怎么让Hook代码知道我们要Hook哪个Cell的内容?怎么询问得知目标StringGrid的行数、列数,以进行正确的遍历获取Cell?... 通过内存映射进行Hook DLL和主程序的数据共享,就可以啦。不过这样实用时不是很方便,因为需要在目标进程中执行Hook代码,显然不能直接调用Hook DLL中的函数,在GetMessage钩子回调函数中来做,也不是很妥当,控制不方便。最好的方法就是在目标进程中创建一个隐形窗口,在该隐形窗口的消息处理中作文章。我们可以通过发送消息的方式通知隐形窗口,执行Hook代码。OK!
    c.按照上面的思路,我编写了HookSG.dll和测试程序,不过并没有Hook得到想要的结果。是哪里出了问题呢?分析上面的流程,最大的可能就是FindControl并返回实际的StringGrid对象实例,否则,后面的StringGrid.Cells[X, Y]这样的获取Cell内容的代码是断不会有问题的,我也能够确认Hook的代码是在目标进程中执行的。查看FindControl的源代码:
    function FindControl(Handle: HWnd): TWinControl;
    var
      OwningProcess: DWORD;
    begin
      Result := nil;
      if (Handle <> 0) and (GetWindowThreadProcessID(Handle, OwningProcess) <> 0) and
         (OwningProcess = GetCurrentProcessId) then // 都进行了判断调用进程ID是否为Handle所在进程
      begin
        if GlobalFindAtom(PChar(ControlAtomString)) = ControlAtom then
          Result := Pointer(GetProp(Handle, MakeIntAtom(ControlAtom)))
        else
          Result := ObjectFromHWnd(Handle);
      end;
    end;
    function ObjectFromHWnd(Handle: HWnd): TWinControl;
    var
      OwningProcess: DWORD;
    begin
      if (GetWindowThreadProcessID(Handle, OwningProcess) <> 0) and
         (OwningProcess = GetCurrentProcessID) then
        Result := Pointer(SendMessage(Handle, RM_GetObjectInstance, 0, 0))
      else
        Result := nil;
    end;
    再看InitControls中:
    procedure InitControls;
    var
      UserHandle: HMODULE;
    begin
      WindowAtomString := Format('Delphi%.8X',[GetCurrentProcessID]);
      WindowAtom := GlobalAddAtom(PChar(WindowAtomString));
      ControlAtomString := Format('ControlOfs%.8X%.8X', [HInstance, GetCurrentThreadID]);
      ControlAtom := GlobalAddAtom(PChar(ControlAtomString));
      RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));
      ...
    end;
    问题就在这里啦。ControlAtomString是根据模块句柄(模块加载基地址)和线程ID动态生成的,目标进程的模块基地址就是EXE基地址,一般是0x00400000,但DLL的模块加载基地址就不是这个了,默认是0x10000000,而实际上可能因为这个地址已经被占用(有其他DLL被加载到这个地址)而进行重定位,所以初始化时添加的Atom和目标进程的ControlAtom就不一样,FindControl当然就不能找到StringGrid对象实例啦。
    清楚了这一点,解决起来就简单了,我们自己写个FindControl函数,以目标进程基地址来动态生成ControlAtomString,添加ControlAtom不就可以啦。在DLL中取EXE的基地址,用GetModuleHandle(nil)即可。
    OK。解决了这些问题,Hook其他程序的StringGrid的内容就水到渠成,没什么障碍啦。补充:
    上面提到的方法,不仅仅可以Hook其他程序的StringGrid的内容,实际上还可以获取更多的从TWinControl继承的窗口组件的内容、属性、...,拥有了对象的实例,你基本上就拥有了对对象的完全控制。
      

  4.   

    代码我上传这里了:http://cnc.ccrun.com/hookstringgrid.rar
      

  5.   

    procedure LBUTTON(h: THandle; x, y: integer);
    var
      r: Trect;
    begin
      if h <> 0 then
        begin
          GetWindowRect(h, r);     // postMessage(h, WM_MOUSEMOVE, 0, MakeLong(x, y));
         // postMessage(h, WM_MOUSEMOVE, 0, MakeLong(x, y));
          postMessage(h, WM_LBUTTONDOWN, MK_LBUTTON, MakeLong(x, y));
          postMessage(h, WM_LBUTTONUP, MK_LBUTTON, MakeLong(x, y));    end;
    end;
      

  6.   


    var
      ARect: TRect;
      h: THandle;
    begin
      h := StrToInt(Edit1.Text);
      SendMessage(h, LB_GETITEMRECT, 2, Integer(@ARect));  LBUTTON(h, ARect.Left + 3, ARect.Top + 3);
    end;
      

  7.   

    我以前做游戏外挂的时候用过,在同一进程里试试下面这个函数:
    ::EnableWindow( HWND , TRUE);如果不是同一进程先共享两线程输入队列
    otherID= GetWindowThreadProcessId(otherHwnd,&dwPID);   //取进程ID
    AttachThreadInput(GetCurrentThreadId(),otherID,TRUE); //共享两线程输入队列