如何实现:
当鼠标停留在网页上时,读取鼠标停留位置的文字,例如网页显示的表格,停留在某个单元格,显示该单元格的数值。

解决方案 »

  1.   

    如果是软件,应该可以用API HOOK实现屏幕取词,Delphi下深入windows核心编程这本书提到过
      

  2.   

    要取的是浏览器中表格里面显示的数字串,以前做过是取的类似textbox控件里面的文字,不知道浏览器表格里面文字,和控件文字是不是一回事?
      

  3.   

    原理就是 
    hook  textout ,exttextout 这类函数
    根据鼠标指向窗口的位置,使那个位置invalid,
    当窗口重绘调用textout这些函数的时候,就可知道那个位置的内容
      

  4.   

    做过类似的东东。
    比较好的处理方式是使用OleAcc
      

  5.   

    我贴段我程序中的代码,你可以参考下。
    unit yAccUtils;interfaceuses
      SysUtils, Classes, Windows, oleacc;const
      csOleAccLibraryName = 'OleAcc.dll';function AccessibleObjectFromPoint(Pt: TPoint;
      var Acc: IAccessible; var Child: Variant): HRESULT; stdcall;
    function WindowFromAccessibleObject(pAcc:IACCESSIBLE;
      var phwnd:HWND): HRESULT; stdcall;implementationvar
      FOleAccLibrary: Integer = 0;
      FAccPresent: Boolean = False;type
      TyaAccessibleObjectFromPoint = function(Pt: TPoint;
        var Acc: IAccessible; var Child: Variant): HRESULT; stdcall;
      TyaWindowFromAccessibleObject = function(pAcc:IACCESSIBLE;
        var phwnd:HWND):HRESULT; stdcall;procedure AccLibLoad;
    begin
      if FAccPresent then
        Exit;
      FOleAccLibrary := SafeLoadLibrary(csOleAccLibraryName);
      FAccPresent := FOleAccLibrary <> 0;
    end;procedure AccLibUnLoad;
    begin
      if FOleAccLibrary <> 0 then
        FreeLibrary(FOleAccLibrary);
      FAccPresent := False;
    end;procedure CheckAccLib;
    begin
      if not FAccPresent then
        AccLibLoad;
    end;function AccessibleObjectFromPoint(Pt: TPoint;
      var Acc: IAccessible; var Child: Variant): HRESULT; stdcall;
    var
      AProc: TyaAccessibleObjectFromPoint;
    begin
      CheckAccLib;
      if FOleAccLibrary <> 0 then
      begin
        @AProc := Windows.GetProcAddress(FOleAccLibrary, 'AccessibleObjectFromPoint');
        if @AProc <> nil then
        begin
          Result := AProc(Pt, Acc, Child);
          Exit;
        end;
      end;  Result := E_FAIL;
    end;function WindowFromAccessibleObject(pAcc: IACCESSIBLE;
      var phwnd: HWND): HRESULT; stdcall;
    var
      AProc: TyaWindowFromAccessibleObject;
    begin
      CheckAccLib;
      if FOleAccLibrary <> 0 then
      begin
        @AProc := Windows.GetProcAddress(FOleAccLibrary, 'WindowFromAccessibleObject');
        if @AProc <> nil then
        begin
          Result := AProc(pAcc, phwnd);
          Exit;
        end;
      end;  Result := E_FAIL;
    end;initialization
    finalization
      AccLibUnLoad;
      

  6.   

    调用:
    var
     P: TPoint;
     A: IAccessible;
     V: Variant;
     pszName: WideString;
     pszValue: WideString;
    begin
      if GetCursorPos(P) then
      begin
        FOldPoint := P;
        if Succeeded(AccessibleObjectFromPoint(P, A, V)) then
        begin
          A.Get_accName(V, pszName);
          A.Get_accValue(V, pszValue);
        end;

    pszValue即你所要的网页上的文字
      

  7.   

    類似屏幕取詞的程序,直接用AIP HOOK;必要時會用到勾子,以下是示例代碼“ 螢幕取詞”的實現:
    //-----------------------------------------------------------------
    1 用SetWindowsHookEx()安裝滑鼠鉤子MouseProc; 
    2 在螢幕上移動滑鼠時,系統就會調用滑鼠鉤子MouseProc; 
    3 進入MouseProc,獲得滑鼠的座標(x,y), 
    設置對TextOut()、ExtTextOut()等的跟蹤程式, 
    用invalidateRect()告訴系統該點(x,y)“失效”; 
    4 
    系統發出WM_PAINT消息,指示該點(x,y)處的應用程式重繪“失效”的區域。 
    5 負責繪製該點()的應用程式在受到 WM_PAINT 消息後, 就有機會調用
    TextOut()、 ExtTextOut()等函數。 
    6 調用的函數被攔截進入跟蹤程式:設置好了的跟蹤程式截獲了該次調用, 
    從應用程式的堆疊中取出 該點(x,y)“文字”的指標; 
    7 從應用程式的資料段中將“文字”指標的內容取出,即完成了一次“螢幕 
    抓字”; 
    8 退出跟蹤程式,返回到滑鼠鉤子MouseProc; 
    9 在MouseProc中解除對TextOut() ExtTextOut()的跟蹤; 
    10 退出MouseProc滑鼠鉤副程式,控制權交給系統。 
    11 在螢幕上移動滑鼠,開始下一次“螢幕抓字”,返回步驟2。
    //-----------------------------------------------------------------
    Dll工程.
    GetWordDll.dpr
    //-----------------------------------------------------------------------------------
    library GetWordDll;
    uses
        Windows,
        SysUtils,
        Classes,
        UnitHookDll in 'UnitHookDll.pas',
        UnitNt2000Hook in 'UnitNt2000Hook.pas',
        UnitHookType in 'UnitHookType.pas';
    exports
          StartHook,
          StopHook,
    //      MouseWndProc,
          {以下匯出列表都是必須的,
          不能少,因為程式要取其位址}
          NewBeginPaint,
          NewCreateCompatibleDC,
          NewTextOutA,
          NewTextOutW,
          NewExtTextOutA,
          NewExtTextOutW,
          NewDrawTextA,
          NewDrawTextW; 
    begin
    end.
    UnitHookType.pas
    unit UnitHookType;
    interface
    uses windows, messages;
    const
          MaxStringLen = 100;
          WM_MOUSEPT = WM_USER + 1138;
          MappingFileName = 'GetWord32 for 9x NT 2000';
          fBeginPaint=0;
          fGetWindowDC=1;
          fGetDC=2;
          fCreateCompatibleDC=3;
          fTextOutA=4;
          fTextOutW=5;
          fExtTextOutA=6;
          fExtTextOutW=7;
          fDrawTextA=8;
          fDrawTextW=9;
    type
          PPointer = ^Pointer;
          TShareMem = packed record
              hProcWnd: HWND; {主應用視窗控制碼}
              hHookWnd: HWND; {滑鼠所在視窗}
              pMouse: TPoint; {滑鼠資訊}
              DCMouse,DCCompatible: HDC;
              fTimerID: integer;
              fStrMouseQueue: array[0..MaxStringLen] of Char; {滑鼠資訊串}
              nTimePassed: integer; {滑鼠停留的時間}
              bCanSpyNow: Boolean; {開始取詞}
              Text: array[0..MaxStringLen] of Char; {字串}
          end;
          PShareMem = ^TShareMem;
    implementation
    end.
    UnitNt2000Hook.pas
    //-----------------------------------------------------------------------------------
    unit UnitNt2000Hook;
    interface
    uses classes, Windows,SysUtils, messages,dialogs;
    type
        TImportCode = packed record
           JumpInstruction: Word;
           AddressOfPointerToFunction: PPointer;
        end;
        PImportCode = ^TImportCode;
        PImage_Import_Entry = ^Image_Import_Entry;
        Image_Import_Entry = record
          Characteristics: DWORD;
          TimeDateStamp: DWORD;
          MajorVersion: Word;
          MinorVersion: Word;
          Name: DWORD;
          LookupTable: DWORD;
        end;
        TLongJmp = packed record
           JmpCode: ShortInt; {指令,用$E9來代替系統的指令}
           FuncAddr: DWORD; {函數位址}
        end;
        THookClass = class
        private
           Trap:boolean; {調用方式:True陷阱式,False改引入表式}
           hProcess: Cardinal; {進程控制碼,只用於陷阱式}
           AlreadyHook:boolean; {是否已安裝Hook,只用於陷阱式}
           AllowChange:boolean; {是否允許安裝、卸載Hook,只用於改引入表式}
           Oldcode: array[0..4]of byte; {系統函數原來的前5個位元組}
           Newcode: TLongJmp; {將要寫在系統函數的前5個位元組}
        private
        public
           OldFunction,NewFunction:Pointer;{被截函數、自訂函數}
           constructor Create(IsTrap:boolean;OldFun,NewFun:pointer);
           constructor Destroy;
           procedure Restore;
           procedure Change;
        published
        end;
    implementation
    {取函數的實際位址。如果函數的第一個指令是Jmp,則取出它的跳轉位址(實際位址),這往往是由於程式中含有Debug調試資訊引起的}
    function FinalFunctionAddress(Code: Pointer): Pointer;
    Var
        func: PImportCode;
    begin
        Result:=Code;
        if Code=nil then exit;
        try
          func:=code;
          if (func.JumpInstruction=$25FF) then
            {指令二進位碼FF 25    彙編指令jmp [...]}
            Func:=func.AddressOfPointerToFunction^;
          result:=Func;
        except
          Result:=nil;
        end;
    end;{更改引入表中指定函數的位址,只用於改引入表式}
    function PatchAddressInModule(BeenDone:Tlist;hModule: THandle; OldFunc,NewFunc: Pointer):integer;
    const
         SIZE=4;
    Var
         Dos: PImageDosHeader; //DOS頭
         NT: PImageNTHeaders;    //PE頭
         ImportDesc: PImage_Import_Entry;//輸入表
         rva: DWORD;     //RVA
         Func: PPointer;    //
         DLL: String;
         f: Pointer;
         written: DWORD;
         mbi_thunk:TMemoryBasicInformation;
         dwOldProtect:DWORD;
    begin
        Result:=0;
        if hModule=0 then exit;
        Dos:=Pointer(hModule);
        {如果這個DLL模組已經處理過,則退出。BeenDone包含已處理的DLL模組}
        if BeenDone.IndexOf(Dos)>=0 then exit;
        BeenDone.Add(Dos);{把DLL模組名加入BeenDone}
        OldFunc:=FinalFunctionAddress(OldFunc);{取函數的實際位址}
        {如果這個DLL模組的位址不能訪問,則退出}
        if IsBadReadPtr(Dos,SizeOf(TImageDosHeader)) then exit;
        {如果這個模組不是以'MZ'開頭,表明不是DLL,則退出}
        if Dos.e_magic<>IMAGE_DOS_SIGNATURE then exit;{IMAGE_DOS_SIGNATURE='MZ'}//檢查數位簽章,最好再檢查一下PE
        {定位至NT Header}
        NT :=Pointer(Integer(Dos) + dos._lfanew);
        {定位至引入函數表}
        RVA:=NT^.OptionalHeader.
           DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;//導入表
        if RVA=0 then exit;{如果引入函數表為空,則退出}
        {把函數引入表的相對位址RVA轉換為絕對位址}
        ImportDesc := pointer(DWORD(Dos)+RVA);{Dos是此DLL模組的首位址}//RVA->VA
        {遍歷所有被引入的下級DLL模組}
        While (ImportDesc^.Name<>0) do
        begin
          {被引入的下級DLL模組名字}
          DLL:=PChar(DWORD(Dos)+ImportDesc^.Name);
          {把被導入的下級DLL模組當做當前模組,進行遞迴呼叫}
          PatchAddressInModule(BeenDone,GetModuleHandle(PChar(DLL)),OldFunc,NewFunc);
          {定位至被引入的下級DLL模組的函數表}
          Func:=Pointer(DWORD(DOS)+ImportDesc.LookupTable);
          {遍歷被引入的下級DLL模組的所有函數}
          While Func^<>nil do
          begin
            f:=FinalFunctionAddress(Func^);{取實際地址}
            if f=OldFunc then {如果函數實際位址就是所要找的地址}
            begin
               VirtualQuery(Func,mbi_thunk, sizeof(TMemoryBasicInformation));
               VirtualProtect(Func,SIZE,PAGE_EXECUTE_WRITECOPY,mbi_thunk.Protect);{更改記憶體屬性}
               WriteProcessMemory(GetCurrentProcess,Func,@NewFunc,SIZE,written);{把新函數位址覆蓋它}
               VirtualProtect(Func, SIZE, mbi_thunk.Protect,dwOldProtect);{恢復記憶體屬性}
            end;
            If Written=4 then Inc(Result);
    //        else showmessagefmt('error:%d',[Written]);
            Inc(Func);{下一個功能函數}
          end;
          Inc(ImportDesc);{下一個被引入的下級DLL模組}
        end;
    end;{HOOK的入口,其中IsTrap表示是否採用陷阱式}
    constructor THookClass.Create(IsTrap:boolean;OldFun,NewFun:pointer);
    begin
         {求被截函數、自訂函數的實際位址}
         OldFunction:=FinalFunctionAddress(OldFun);
         NewFunction:=FinalFunctionAddress(NewFun);
         Trap:=IsTrap;
         if Trap then{如果是陷阱式}
         begin
            {以特權的方式來打開當前進程}
            hProcess := OpenProcess(PROCESS_ALL_ACCESS,FALSE, GetCurrentProcessID);
            {生成jmp xxxx的代碼,共5位元組}
            Newcode.JmpCode := ShortInt($E9); {jmp指令的十六進位代碼是E9}
            NewCode.FuncAddr := DWORD(NewFunction) - DWORD(OldFunction) - 5;
            {保存被截函數的前5個位元組}
            move(OldFunction^,OldCode,5);
            {設置為還沒有開始HOOK}
            AlreadyHook:=false;
         end;
         {如果是改引入表式,將允許HOOK}
         if not Trap then AllowChange:=true;
         Change; {開始HOOK}
         {如果是改引入表式,將暫時不允許HOOK}
         if not Trap then AllowChange:=false;
    end;
    {HOOK的出口}
    constructor THookClass.Destroy;
    begin
         {如果是改引入表式,將允許HOOK}
         if not Trap then AllowChange:=true;
         Restore; {停止HOOK}
         if Trap then{如果是陷阱式}
            CloseHandle(hProcess);
    end;
    {開始HOOK}
    procedure THookClass.Change;
    var
         nCount: DWORD;
         BeenDone: TList;
    begin
        if Trap then{如果是陷阱式}
        begin
          if (AlreadyHook)or (hProcess = 0) or (OldFunction = nil) or (NewFunction = nil) then
              exit;
          AlreadyHook:=true;{表示已經HOOK}
          WriteProcessMemory(hProcess, OldFunction, @(Newcode), 5, nCount);
        end
        else begin{如果是改引入表式}
             if (not AllowChange)or(OldFunction=nil)or(NewFunction=nil)then exit;
             BeenDone:=TList.Create; {用於存放當前進程所有DLL模組的名字}
             try
               PatchAddressInModule(BeenDone,GetModuleHandle(nil),OldFunction,NewFunction);
             finally
               BeenDone.Free;
             end;
        end;
    end;
    {恢復系統函數的調用}
    procedure THookClass.Restore;
    var
         nCount: DWORD;
         BeenDone: TList;
    begin
        if Trap then{如果是陷阱式}
        begin
          if (not AlreadyHook) or (hProcess = 0) or (OldFunction = nil) or (NewFunction = nil) then
              exit;
          WriteProcessMemory(hProcess, OldFunction, @(Oldcode), 5, nCount);
          AlreadyHook:=false;{表示退出HOOK}
        end
        else begin{如果是改引入表式}
          if (not AllowChange)or(OldFunction=nil)or(NewFunction=nil)then exit;
          BeenDone:=TList.Create;{用於存放當前進程所有DLL模組的名字}
          try
            PatchAddressInModule(BeenDone,GetModuleHandle(nil),NewFunction,OldFunction);
          finally
            BeenDone.Free;
          end;
        end;
    end;
    end.
      

  8.   

    楼上的代码怎么用?yAccUtils.pas并不是以“End.”结尾的,而且uses里不存在oleacc也没法编译来验证。我知道的方法代码不超过三行,哪有你这么麻烦啊。你这到底是什么代码?你QQ多少?
      

  9.   

    我少拷贝了一个End. 自己加上啊:-)olecc.pas单元Delphi 2010中有这个文件,所以我直接使用了,
    如果是Delphi 7或者更早版本好像没有这个单元,其他版本不大清楚。如果要在Delphi 7 中使用,加上IAccessible的定义就好了,具体如下:  
      {$EXTERNALSYM IAccessible}
      IAccessible = interface(IDispatch)
        ['{618736E0-3C3D-11CF-810C-00AA00389B71}']
        function Get_accParent(out ppdispParent: IDispatch): HResult; stdcall;
        function Get_accChildCount(out pcountChildren: Integer): HResult; stdcall;
        function Get_accChild(varChild: OleVariant; out ppdispChild: IDispatch): HResult; stdcall;
        function Get_accName(varChild: OleVariant; out pszName: WideString): HResult; stdcall;
        function Get_accValue(varChild: OleVariant; out pszValue: WideString): HResult; stdcall;
        function Get_accDescription(varChild: OleVariant; out pszDescription: WideString): HResult; stdcall;
        function Get_accRole(varChild: OleVariant; out pvarRole: OleVariant): HResult; stdcall;
        function Get_accState(varChild: OleVariant; out pvarState: OleVariant): HResult; stdcall;
        function Get_accHelp(varChild: OleVariant; out pszHelp: WideString): HResult; stdcall;
        function Get_accHelpTopic(out pszHelpFile: WideString; varChild: OleVariant;
                                  out pidTopic: Integer): HResult; stdcall;
        function Get_accKeyboardShortcut(varChild: OleVariant; out pszKeyboardShortcut: WideString): HResult; stdcall;
        function Get_accFocus(out pvarChild: OleVariant): HResult; stdcall;
        function Get_accSelection(out pvarChildren: OleVariant): HResult; stdcall;
        function Get_accDefaultAction(varChild: OleVariant; out pszDefaultAction: WideString): HResult; stdcall;
        function accSelect(flagsSelect: Integer; varChild: OleVariant): HResult; stdcall;
        function accLocation(out pxLeft: Integer; out pyTop: Integer; out pcxWidth: Integer;
                             out pcyHeight: Integer; varChild: OleVariant): HResult; stdcall;
        function accNavigate(navDir: Integer; varStart: OleVariant; out pvarEndUpAt: OleVariant): HResult; stdcall;
        function accHitTest(xLeft: Integer; yTop: Integer; out pvarChild: OleVariant): HResult; stdcall;
        function accDoDefaultAction(varChild: OleVariant): HResult; stdcall;
        function Set_accName(varChild: OleVariant; const pszName: WideString): HResult; stdcall;
        function Set_accValue(varChild: OleVariant; const pszValue: WideString): HResult; stdcall;
      end;
      

  10.   

    另外你说的三行代码实现是什么?不会是GetWindowText吧。。
    要注意看楼主要的是取得网页上的某些文本。