【问题标题】:Cannot access SystemListView contents using Managed Win32 API's无法使用托管 Win32 API 访问 SystemListView 内容
【发布时间】:2023-03-24 15:04:02
【问题描述】:

我在 C# 环境中使用 ManagedWindows API: http://mwinapi.sourceforge.net/

过去,我使用下面的代码成功地抓取了其他正在运行的程序的类似列表框的部分的内容,我在其中迭代键/值对以查找列表项。但是,对于这个特定的项目列表,我可以获得准确的项目数量,但该值始终为空!

使用这个:

TargetMidWindow.Content.ComponentType

我发现我遇到问题的列表是“listview”,而我成功使用的其他窗口是“detailslistview”,以防万一。下面是我用于查找所需数据的代码,除了更改我使用的搜索词之外,它几乎与我的其他成功代码相同。此外,如果相关,我试图从中提取数据的程序是 MetaTrader4,并且我已经能够成功地从程序的其他部分刮取数据。

    // Find the main window
        SystemWindow[] TopLevel = SystemWindow.AllToplevelWindows;
        SystemWindow TargetTopWindow = SystemWindow.ForegroundWindow;
        foreach (SystemWindow SearchWindow in TopLevel)
        {
            string Title = SearchWindow.Title;
            if (Title.Contains("MetaTrader"))
            {
                TargetTopWindow = SearchWindow;
                break;
            }
        }

        // Find the section where positions are contained
        SystemWindow[] MidLevel = TargetTopWindow.AllDescendantWindows;
        SystemWindow TargetMidWindow = SystemWindow.ForegroundWindow;
        foreach (SystemWindow SearchWindow in MidLevel)
        {
            string ClassName = SearchWindow.ClassName;
            if (ClassName.Contains("SysListView32"))
            {
                SystemWindow ParentWindow = SearchWindow.Parent;
                    if ((ParentWindow.Title.Contains("Terminal")))
                    {
                        TargetMidWindow = SearchWindow;

                    }
            }
        }

        // Get the positions
        Dictionary<string, string> RawValues = new Dictionary<string, string>();
        foreach (KeyValuePair<string, string> KVP in TargetMidWindow.Content.PropertyList)
        {
            string key = KVP.Key;
            string value = KVP.Value;
        }

我需要做一些特别的事情来避免每个列表项都得到“空”值吗?

谢谢! 比尔

【问题讨论】:

    标签: c# listview screen-scraping winapi


    【解决方案1】:

    嗯,使用友好的 API 包装 Windows 消息并不难。 Windows 窗体就是一个很好的例子。但是,一旦您开始使用另一个进程执行此操作,它就会遇到非常坚固的墙壁。

    为了读取 ListView 项目,您需要的特定消息是 LVM_GETITEM。那是那些坚固的墙壁信息之一。您传递给 SendMessage() 的 LPARAM 参数需要是指向 LVITEM 结构的指针。控件填充该结构中的字段。问题是,您传递的指针仅在 您的 进程中有效,而不是拥有该窗口的进程。

    解决这个问题需要大量的技巧。您必须分配在该进程内有效的内存。这需要 VirtualAllocEx() 和 ReadProcessMemory()。加上完成这些工作所需的所有胶水调用。我假设您正在使用的这个库没有处理这个问题。很容易找到,grep这些API函数名的源代码文件。

    【讨论】:

    • 天啊,我希望它不会采用史诗般的骇客。又放屁了一阵子,看来如果我决心这样做的话,我必须采取你的方法。
    【解决方案2】:

    如果您想找到特定 SysListView32 窗口的正确句柄,您需要从正确的窗口层次结构开始。从代码 sn-p 看来,您实际上并没有找到正确的句柄来从 SysListView32 窗口中检索报价。这就是您收到空值的原因。您最好运行 spy++ 并为您的特定经纪人和构建确定 Metatrader 终端的正确窗口结构。我发现在某些窗口的构建之间以及在某些代理之间,类是不同的,尽管程度较小。

    您正在寻找这样的特定报价窗口层次结构:

    Metatrader -> Market Watch -> Market Watch -> SysListView32
    

    相比之下,目前您正在查看您的代码:

    Metatrader -> Terminal -> (many sub-windows with SysListView32 class)
    

    右边的每一层都是左边窗口的一个子窗口。

    找到父“Metatrader”窗口,然后向下查找子窗口,直到到达 SysListView32。如果您使用 spy++,您可以读取 SysListView32 父窗口(市场观察)的类,并使用它来枚举窗口以找到正确的 SysListView32 窗口。仅供参考,构建 419 的正确市场观察类名称是:

    Afx:00400000:b:00010003:00000000:00000000
    

    一旦找到正确的窗口,您就可以使用当前组件提取其内容。我还没有尝试过,并且正在寻找从实际上涉及史诗骇客的 ListView 模块中移植一些来自 VB6 的代码。 ;) 我可能会看看 .NET Managed Windows API,看看这是否有助于简化流程。

    但与此同时,如果您确实需要了解底层,以下 VB6 源代码应该可以帮助您了解所涉及的内容。这是相当先进的材料,祝你好运!

    Public Function GetListviewItem(ByVal hWindow As Long, ByVal pColumn As Long, ByVal pRow As Long) As String
    Dim result              As Long
    Dim myItem              As LV_ITEMA
    Dim pHandle             As Long
    Dim pStrBufferMemory    As Long
    Dim pMyItemMemory       As Long
    Dim strBuffer()         As Byte
    Dim index               As Long
    Dim tmpString           As String
    Dim strLength           As Long
    
    Dim ProcessID As Long, ThreadID As Long
    ThreadID = GetWindowThreadProcessId(hWindow, ProcessID)
    '**********************
    'init the string buffer
    '**********************
    ReDim strBuffer(MAX_LVMSTRING)
    
    '***********************************************************
    'open a handle to the process and allocate the string buffer
    '***********************************************************
    pHandle = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, False, ProcessID)
    pStrBufferMemory = VirtualAllocEx(pHandle, 0, MAX_LVMSTRING, MEM_COMMIT, PAGE_READWRITE)
    
    '************************************************************************************
    'initialize the local LV_ITEM structure
    'The myItem.iSubItem member is set to the index of the column that is being retrieved
    '************************************************************************************
    myItem.mask = LVIF_TEXT
    myItem.iSubItem = pColumn
    myItem.pszText = pStrBufferMemory
    myItem.cchTextMax = MAX_LVMSTRING
    
    '**********************************************************
    'write the structure into the remote process's memory space
    '**********************************************************
    pMyItemMemory = VirtualAllocEx(pHandle, 0, Len(myItem), MEM_COMMIT, PAGE_READWRITE)
    result = WriteProcessMemory(pHandle, pMyItemMemory, myItem, Len(myItem), 0)
    
    '*************************************************************
    'send the get the item message and write back the memory space
    '*************************************************************
    result = SendMessage(hWindow, LVM_GETITEMTEXT, pRow, ByVal pMyItemMemory)
    result = ReadProcessMemory(pHandle, pStrBufferMemory, strBuffer(0), MAX_LVMSTRING, 0)
    result = ReadProcessMemory(pHandle, pMyItemMemory, myItem, Len(myItem), 0)
    
    '**************************************************
    'turn the byte array into a string and send it back
    '**************************************************
    For index = LBound(strBuffer) To UBound(strBuffer)
        If Chr(strBuffer(index)) = vbNullChar Then Exit For
        tmpString = tmpString & Chr(strBuffer(index))
    Next index
    
    tmpString = Trim(tmpString)
    
    '**************************************************
    'deallocate the memory and close the process handle
    '**************************************************
    result = VirtualFreeEx(pHandle, pStrBufferMemory, 0, MEM_RELEASE)
    result = VirtualFreeEx(pHandle, pMyItemMemory, 0, MEM_RELEASE)
    
    result = CloseHandle(pHandle)
    
    If Len(tmpString) > 0 Then GetListviewItem = tmpString
    
    End Function
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-02
      • 2018-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-01
      • 2020-06-05
      • 1970-01-01
      相关资源
      最近更新 更多