3

ここに私のコードがあります:

   public static string ReadListViewItem(IntPtr lstview, int item)
    {
        const int dwBufferSize = 1024;

        int dwProcessID;
        LV_ITEM lvItem;
        string retval;
        bool bSuccess;
        IntPtr hProcess = IntPtr.Zero;
        IntPtr lpRemoteBuffer = IntPtr.Zero;
        IntPtr lpLocalBuffer = IntPtr.Zero;
        IntPtr threadId = IntPtr.Zero;

        try
        {
            lvItem = new LV_ITEM();
            lpLocalBuffer = Marshal.AllocHGlobal(dwBufferSize);
            // Get the process id owning the window
            threadId = GetWindowThreadProcessId(lstview, out dwProcessID);
            if ((threadId == IntPtr.Zero) || (dwProcessID == 0))
                throw new ArgumentException("hWnd");

            // Open the process with all access
            hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
            if (hProcess == IntPtr.Zero)
                throw new ApplicationException("Failed to access process");

            // Allocate a buffer in the remote process
            lpRemoteBuffer = VirtualAllocEx(hProcess, IntPtr.Zero, dwBufferSize, MEM_COMMIT,
              PAGE_READWRITE);
            if (lpRemoteBuffer == IntPtr.Zero)
                throw new SystemException("Failed to allocate memory in remote process");

            // Fill in the LVITEM struct, this is in your own process
            // Set the pszText member to somewhere in the remote buffer,
            // For the example I used the address imediately following the LVITEM stuct
            lvItem.mask = LVIF_TEXT;

            lvItem.iItem = item;
            lvItem.iSubItem = 2;
            lvItem.pszText = (IntPtr)(lpRemoteBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM)));
            lvItem.cchTextMax = 50;

            // Copy the local LVITEM to the remote buffer
            bSuccess = WriteProcessMemory(hProcess, lpRemoteBuffer, ref lvItem,
              Marshal.SizeOf(typeof(LV_ITEM)), IntPtr.Zero);
            if (!bSuccess)
                throw new SystemException("Failed to write to process memory");

            // Send the message to the remote window with the address of the remote buffer
            SendMessage(lstview, LVM_GETITEMText, 0, lpRemoteBuffer);

            // Read the struct back from the remote process into local buffer
            bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize,IntPtr.Zero);
            if (!bSuccess)
                throw new SystemException("Failed to read from process memory");

            // At this point the lpLocalBuffer contains the returned LV_ITEM structure
            // the next line extracts the text from the buffer into a managed string
            retval = Marshal.PtrToStringAnsi((IntPtr)(lpLocalBuffer +
              Marshal.SizeOf(typeof(LV_ITEM))));
        }
        finally
        {
            if (lpLocalBuffer != IntPtr.Zero)
                Marshal.FreeHGlobal(lpLocalBuffer);
            if (lpRemoteBuffer != IntPtr.Zero)
                VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE);
            if (hProcess != IntPtr.Zero)
                CloseHandle(hProcess);
        }
        return retval;
    }

何をしてもretvalは空を返しますが、lpLocalBufferはそうではありません。

ここに ListItem の定義があります:

   [StructLayout(LayoutKind.Sequential)]
    private struct LV_ITEM
    {
        public int mask;
        public int iItem;
        public int iSubItem;
        public int state;
        public int stateMask;
        public IntPtr pszText;
        public int cchTextMax;
        public int iImage;
        internal int lParam;
        internal int iIndent;
    }

86x、64ビット、任意のCPU用にコンパイルしようとしましたが、何も機能していないようです!

なぜこれが起こっているのでしょうか?

C# + .net4 、Windows 7 64 ビット。

4

5 に答える 5

13

これを行う別の方法を次に示します。 UI Automationを使用します。クロスプロセス、クロスビットの作業を行い、リストビュー、リストボックス、またはその他のほとんどすべての標準 Windows UI に対して機能します。マウス ポインターの下にあるリストビューから HWND を取得し、その中の項目をダンプするサンプル アプリを次に示します。各項目の名前だけをダンプします。リストビューを使用すると、必要に応じて各項目のフィールドに再帰できると思います。

// Compile using: csc ReadListView.cs /r:UIAutomationClient.dll

using System;
using System.Windows.Automation;
using System.Runtime.InteropServices;

class ReadListView
{
    public static void Main()
    {
        Console.WriteLine("Place pointer over listview and hit return...");
        Console.ReadLine();

        // Get cursor position, then the window handle at that point...
        POINT pt;
        GetCursorPos(out pt);
        IntPtr hwnd = WindowFromPoint(pt);

        // Get the AutomationElement that represents the window handle...
        AutomationElement el = AutomationElement.FromHandle(hwnd);

        // Walk the automation element tree using content view, so we only see
        // list items, not scrollbars and headers. (Use ControlViewWalker if you
        // want to traverse those also.)
        TreeWalker walker = TreeWalker.ContentViewWalker;
        int i = 0;
        for( AutomationElement child = walker.GetFirstChild(el) ;
            child != null; 
            child = walker.GetNextSibling(child) )
        {
            // Print out the type of the item and its name
            Console.WriteLine("item {0} is a \"{1}\" with name \"{2}\"", i++, child.Current.LocalizedControlType, child.Current.Name);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    };

    [DllImport("user32.dll")]
    private static extern IntPtr WindowFromPoint(POINT pt);

    [DllImport("user32.dll")]
    private static extern int GetCursorPos(out POINT pt);
}
于 2011-03-21T09:06:11.643 に答える
4

これが古いことは知っていますが、問題を解決しようとしているときに見つけました。これが他の人の助けになることを願っています。

私はこの質問の推奨事項をC++で使用し、LV_ITEM構造をわずかに変更して、VB.NETで64ビットで動作するようにしました(C#でテストしていませんが、解決策は非常に似ていると思います.)

Public Structure LV_ITEM64

    Public mask As Integer
    Public iItem As Integer
    Public iSubItem As Integer
    Public state As Integer
    Public stateMask As Integer
    Public placeholder1 As Integer
    Public pszText As Integer
    Public placeholder2 As Integer
    Public cchTextMax As Integer
    Public iImage As Integer

End Structure

次に、構造体のインスタンスを宣言するときに、次のコードを使用して 64 ビット構造と 32 ビット構造のどちらかを選択しました。

Dim lvi As Object

If IntPtr.Size = 4 Then
    lvi = New LV_ITEM
Else
    lvi = New LV_ITEM64
End If
于 2012-08-28T22:43:18.037 に答える
1

32ビットプロセスのリストビューコントロールから別の64ビットプロセスにアイテムを読み込もうとしていることを明確にしました。

私はさまざまなフォーラムでこのトピックに関する多くの質問を見てきましたが、成功した結果を達成したとは思えませんでした。

他のプログラムのリストビューから読み取ることができる32ビットの実行可能ファイルを作成するのが最善の選択肢だと思います。

于 2011-03-20T15:05:35.160 に答える
1

プログラムが 32 ビットで、ターゲット プログラムが 64 ビットである場合、克服すべき障害が少なくとも 1 つあります。またはその逆です。LVITEM 宣言が間違っています。IntPtr のビット数が間違っています。これにより、 Marshal.SizeOf() が間違った値を返します。たまたま、アライメントは大丈夫だと思います。フィールドを int または long に変更すると、ターゲット プログラムのビット数に応じて問題を解決できます。Taskmgr.exe の [プロセス] タブで確認できます。32 ビット プロセスの場合、プロセス名の末尾に「*32」が付きます。または、プロジェクトのターゲット プラットフォーム設定をターゲット プロセス (x86 または AnyCPU) と一致するように設定することで、問題を回避できます。

Debug + Windows + Memory + Memory1 を使用してこれをデバッグします。「lpLocalBuffer」を [アドレス] ボックスに入力し、表示される内容とコードが読み取る内容を観察します。文字列を適切に取得したことは、16進ビューから確実にわかるはずです。文字列の間にゼロがある場合、ターゲット プロセスはリスト ビューの Unicode バージョンを使用していることに注意してください。次に、それを読み取るために Marshal.PtrToStringUnicode が必要です。

于 2011-03-20T15:35:22.010 に答える
0

返信が遅くなり申し訳ありませんが、同じ問題に遭遇しました。これは、32 ビット システムと 64 ビット システムの両方で動作する VB.NET に使用した構造です。

<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure LV_ITEM
    Public Mask As UInteger
    Public Index As Integer
    Public SubIndex As Integer
    Public State As Integer
    Public StateMask As IntPtr
    Public Text As String
    Public TextLength As Integer
    Public ImageIndex As Integer
    Public LParam As IntPtr
End Structure
于 2013-12-12T12:20:44.527 に答える