【问题标题】:Win32- Getting ListView Control content from another applicationWin32-从另一个应用程序获取 ListView 控件内容
【发布时间】:2012-10-01 19:13:37
【问题描述】:

我正在尝试访问另一个应用程序中的 ListView 控件(位于对话框中),并从该控件中获取数据。这是我正在编写的 Win32 代码(带有适当的 cmets):

        HWND hListView32 = hRoot;   //HANDLE to the ListView control within the Dialog, having class name - "SysListView32"
        int cnt = (int) ::SendMessage(hListView32, LVM_GETITEMCOUNT, 0, 0L); //returns CORRECT item count of the ListView Control
        int nItem=0,nRes;

        for(int nItem=0;nItem<cnt;nItem++)
        {
            LVITEM LvItem;  // ListView Item struct
              char Text[255]={0};  
              char Temp[255]={0};
              char Temp1[255]={0};

                memset(&LvItem,0,sizeof(LvItem));
          LvItem.mask=LVIF_TEXT;
              LvItem.iSubItem=1;    //Trying to get the 2nd Colomn text

              LvItem.pszText=Text;  //Does not returns any Text, after the below SendMessage is executed???

              LvItem.cchTextMax=256;
              LvItem.iItem=nItem;
              nRes  = (int)::SendMessage(hListView32,LVM_GETITEMTEXT, nItem, (LPARAM)&LvItem);
              DWORD dd = ::GetLastError();  //returns 0
        }

虽然代码正在执行,但我没有从控件中获取任何数据。但是,我能够从控件中检索正确的项目计数,但没有数据。

另一种方法可能是使用 MSAA 挂钩来获取数据。但这将是一个非常漫长和繁琐的过程。这里的想法用完了。请帮忙。

谢谢,

【问题讨论】:

  • LVM_GETITEMTEXT 只能在同一个进程中工作。参见例如David Heffernans 转至this SO question

标签: winapi win32-process


【解决方案1】:

有几种可能性。

  1. DLL Injection 使用 windows 挂钩。优点:简单直接。缺点:很多进程都会加载这个 dll。

  2. DLL Injection 通过打开库进行调试来使进程加载库,在该进程的上下文中使用 VallocEx 分配一块虚拟内存,使用 WriteProcessMemory 写入它的内存并使用创建远程线程LoadLibrary 函数的起始地址。优点:单个进程受到影响。缺点:比钩子解决方案复杂一点。

  3. 读取进程内存。与选项 2 相同,但不是写入此内存并远程执行代码,而是将消息 LVM_GETITEMTEXT 发送到相关窗口,提供有效的已知内存位置,然后使用 ReadProcessMemory 读取该位置。

【讨论】:

  • DLL 注入不是唯一的方法。也可以在目标应用程序中分配内存并让它处理LVM_GETITEMTEXT 消息。
  • @user786653 但就您设法打开分配和读取内存的进程而言 - 加载 dll 的一步。
  • 我的主要反对意见是你的第一句话,这是错误的。我不声称知道最好的方法。如果你编辑你的答案,我很乐意收回我的反对票。
  • @user786653 我理解你。否决票本身不是问题。缺失的原因和过多的家庭作业问题让我怀疑我是否正确地度过了自己的时间。我会在您允许的情况下修正答案。
【解决方案2】:

传递缓冲区的ListView 消息仅在拥有ListView 的进程的地址空间内工作。您必须使用VirtualAllocEx() 在同一个进程中分配内存块,然后您可以使用WriteProcessMemory() 写入它并让ListView 根据需要填充它,然后您可以使用ReadProcessMemory() 读取它并使用释放它VirtualFreeEx().

试试这个(为简洁起见省略了错误处理):

HWND hListView32 = hRoot;

int cnt = (int) ::SendMessage(hListView32, LVM_GETITEMCOUNT, 0, 0);
if (cnt > 0)
{
    DWORD dwProcessId;
    GetWindowThreadProcessId(hListView32, &dwProcessId);

    HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId);

    LVITEM *pLvItem = (LVITEM*) VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
    LPTSTR pText = (LPTSTR) VirtualAllocEx(hProcess, NULL, sizeof(TCHAR)*256, MEM_COMMIT, PAGE_READWRITE);

    for(int nItem = 0; nItem < cnt; ++nItem) 
    { 
        TCHAR Text[256] = {0};

        LVITEM LvItem = {0}; 
        LvItem.mask = LVIF_TEXT; 
        LvItem.iSubItem = 1;
        LvItem.pszText = pText;
        LvItem.cchTextMax = 256; 
        LvItem.iItem = nItem; 

        WriteProcessMemory(hProcess, pLvItem, &LvItem, sizeof(LVITEM), NULL);

        int nRes = (int) ::SendMessage(hListView32, LVM_GETITEMTEXT, nItem, (LPARAM)pLvItem);
        if (nRes > 0)
            ReadProcessMemory(hProcess, pText, &Text[0], sizeof(TCHAR)*nRes, NULL);

        // use Text as needed...
    }

    VirtualFreeEx(hProcess, pText, 0, MEM_RELEASE);
    VirtualFreeEx(hProcess, pLvItem, 0, MEM_RELEASE);
    CloseHandle(hProcess);
}

【讨论】:

    【解决方案3】:

    我复制粘贴了 Remy Lebeau 的代码,但在我的特殊情况下它的工作方式非常奇怪。通过 SendMessage(listview, LVM_GETITEMCOUNT, 0, 0) 正确检索元素的数量 ,但循环每次都读取相同的元素!它不是第一个或最后一个元素,不是被选中的,但似乎是随机的。这是我的代码:

    HWND win=FindWindowEx(NULL, NULL, _("TEventLogView"), NULL);
    HWND listview=FindWindowEx(win, NULL, _("TListView"), NULL);
    int cnt = (int) ::SendMessage(listview, LVM_GETITEMCOUNT, 0, 0);
    if (cnt > 0)
    {
        DWORD dwProcessId;
        GetWindowThreadProcessId(listview, &dwProcessId);
        int n = grdEvents->GetNumberRows();
        HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId);
        LVITEM *pLvItem = (LVITEM*) VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
        LPTSTR pText = (LPTSTR) VirtualAllocEx(hProcess, NULL, sizeof(TCHAR)*255, MEM_COMMIT, PAGE_READWRITE);
       for(int nItem = 0; nItem < cnt; ++nItem)
       {
          // need to read 1 - 3 subitems
          for (int j = 1; j < 4; j++)
          {
             TCHAR Text[255] = {0};
             LVITEM LvItem = {0};
             LvItem.mask = LVIF_STATE | LVIF_TEXT;
             LvItem.pszText = pText;
             LvItem.cchTextMax = sizeof(TCHAR)*255;
             LvItem.iItem = nItem;
             LvItem.iSubItem = j;
             int nRes1 = WriteProcessMemory(hProcess, pLvItem, &LvItem, sizeof(LVITEM), NULL);
             int nRes2 = (int) ::SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)pLvItem);
             if (nRes2 > 0)
             {
                 ReadProcessMemory(hProcess, pText, &Text[0], sizeof(TCHAR)*nRes2, NULL);
                 // insert into wxWidgets grid
                 grdEvents->SetCellValue(nItem, j - 1, Text);
            }
          }
          VirtualFreeEx(hProcess, pText, 0, MEM_RELEASE);
          VirtualFreeEx(hProcess, pLvItem, 0, MEM_RELEASE);
          CloseHandle(hProcess);
       }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-27
      • 2016-03-27
      相关资源
      最近更新 更多