Normal Windows GUI applications work with messages that are sent to a window or control and the control reacts on these messages. This technology can easily be used to control other applications. A quite nice start can give the CWindow article on CodeProject.

Whenever we want to control some other applications, I would suggest to use spy+ (or any other tool like that) which hooks up into the application to show us all messages that are processed. That way we can quickly find out, what messages are used and I think in most cases we will find, that the common dialogs are used. This makes it quite easy for developers, because we can always look up the messages, that the controls understand.

In my example, the control also knows the LVM_GETITEMW message. We can look up the available messages and some more informations inside the c/c++ header files. The information we need can be found inside the commctrl.h file.

When we have a closer look, we will find, that a structure LV_ITEM is required. So we first translate it to c#:

[StructLayout(LayoutKind.Sequential)]
unsafe struct LV_ITEM
{
public Int32 mask;
public Int32 iItem;
 public Int32 iSubItem;
  public Int32 state;
  public Int32 stateMask;
  public char* pszText;
  public Int32 cchTextMax;
 public Int32 iImage;
  public Int32 lParam;
  public Int32 iIndent;
}

Important: Inside this structure, we need to use pointers. Pointers in C# are unsafe so we have to allow unsafe code inside our project settings.

Next we need to check out the core message. The message itself is just a number. Inside the commctrl.h we find:

#define LVM_GETITEMW            (LVM_FIRST + 75)

So we check the header files for the definition of LVM_FIRST (0×1000) so we can define the values:

const int LVM_FIRST = 0x1000;
const int LVM_GETITEMW = LVM_FIRST + 75;

And now we have to check, what kind of Message we have to send to the control:

The mask entry of LV_ITEM must be set. LVIF_TEXT makes sense for us because we want to get the text. So we have to define that number, too:

const int LVIF_TEXT = 0x00000001;

We set the LV_ITEM.iItem to the item number we want to read and the iSubItem to the sub item to read. The next important step is to set the pointer to a memory area where the ListView should write the content to.

After this preparation we can send the message now – and we will be surprised: We get an error inside the application we wanted to control! What is going wrong?

Windows protects the memory of the different processes. One process is not allowed to simply write to memory allocated by some other process. So we have to solve this, too.

The solution is again quite straight forward:

  • First we get a handle to the process owning the listview. We can use OpenProcess for this.
  • Next we allocate memory which should be accessable by that process. This can be done with VirtualAllocEx. We allocate enough memory so that the LV_ITEM and the returned string fit inside the area.
  • Now we can create create a LV_ITEM inside our memory area. The string points to the allocated buffer + size of LV_ITEM – so the string begins directly behind the LV_ITEM.
  • Next we copy the LV_ITEM we created in our process memory to the memory block we allocated. This can be done with WriteProcessMemory.
  • Now we can send the message – this time there should be no error.
  • To get the string that was written to that memory area we have to copy it back to some memory of our process using ReadProcessMemory.
  • The last step is to use Marshal.PtrToStringUri to get the string we are interested in.

So let us have a look at some working code (Just Copy & Paste the code so you can see the long lines, too):

using System;
using System.Runtime.InteropServices; namespace ListViewTest
{
[StructLayout(LayoutKind.Sequential)]
unsafe struct LV_ITEM {
public Int32 mask;
public Int32 iItem;
public Int32 iSubItem;
public Int32 state;
public Int32 stateMask;
public char* pszText;
public Int32 cchTextMax;
public Int32 iImage;
public Int32 lParam;
public Int32 iIndent;
} [Flags()]
public enum Win32ProcessAccessType {
AllAccess = CreateThread | DuplicateHandle | QueryInformation | SetInformation | Terminate | VMOperation | VMRead | VMWrite | Synchronize,
CreateThread = 0x2,
DuplicateHandle = 0x40,
QueryInformation = 0x400,
SetInformation = 0x200,
Terminate = 0x1,
VMOperation = 0x8,
VMRead = 0x10,
VMWrite = 0x20,
Synchronize = 0x100000
} [Flags]
public enum Win32AllocationTypes {
MEM_COMMIT = 0x1000,
MEM_RESERVE = 0x2000,
MEM_DECOMMIT = 0x4000,
MEM_RELEASE = 0x8000,
MEM_RESET = 0x80000,
MEM_PHYSICAL = 0x400000,
MEM_TOP_DOWN = 0x100000,
WriteWatch = 0x200000,
MEM_LARGE_PAGES = 0x20000000
} [Flags]
public enum Win32MemoryProtection {
PAGE_EXECUTE = 0x10,
PAGE_EXECUTE_READ = 0x20,
PAGE_EXECUTE_READWRITE = 0x40,
PAGE_EXECUTE_WRITECOPY = 0x80,
PAGE_NOACCESS = 0x01,
PAGE_READONLY = 0x02,
PAGE_READWRITE = 0x04,
PAGE_WRITECOPY = 0x08,
PAGE_GUARD = 0x100,
PAGE_NOCACHE = 0x200,
PAGE_WRITECOMBINE = 0x400
} public class ListViewItem {
[DllImport("kernel32.dll")]
internal static extern IntPtr OpenProcess(Win32ProcessAccessType dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
internal static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, Win32AllocationTypes flWin32AllocationType, Win32MemoryProtection flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref LV_ITEM lpBuffer, uint nSize, out int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int dwSize, out int lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
internal static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, Win32AllocationTypes dwFreeType);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); public string GetListViewItem(IntPtr hwnd, uint processId, int item, int subItem = 0)
{
const int dwBufferSize = 2048;
const int LVM_FIRST = 0x1000;
const int LVM_GETITEMW = LVM_FIRST + 75;
const int LVIF_TEXT = 0x00000001; int bytesWrittenOrRead = 0;
LV_ITEM lvItem;
string retval;
bool bSuccess;
IntPtr hProcess = IntPtr.Zero;
IntPtr lpRemoteBuffer = IntPtr.Zero;
IntPtr lpLocalBuffer = IntPtr.Zero; try {
lvItem = new LV_ITEM();
lpLocalBuffer = Marshal.AllocHGlobal(dwBufferSize);
hProcess = OpenProcess(Win32ProcessAccessType.AllAccess, false, processId);
if (hProcess == IntPtr.Zero)
throw new ApplicationException("Failed to access process!"); lpRemoteBuffer = VirtualAllocEx(hProcess, IntPtr.Zero, dwBufferSize, Win32AllocationTypes.MEM_COMMIT, Win32MemoryProtection.PAGE_READWRITE);
if (lpRemoteBuffer == IntPtr.Zero)
throw new SystemException("Failed to allocate memory in remote process"); lvItem.mask = LVIF_TEXT;
lvItem.iItem = item;
lvItem.iSubItem = subItem;
unsafe {
lvItem.pszText = (char*)(lpRemoteBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM)));
}
lvItem.cchTextMax = 500; bSuccess = WriteProcessMemory(hProcess, lpRemoteBuffer, ref lvItem, (uint)Marshal.SizeOf(typeof(LV_ITEM)), out bytesWrittenOrRead);
if (!bSuccess)
throw new SystemException("Failed to write to process memory"); SendMessage(hwnd, LVM_GETITEMW, IntPtr.Zero, lpRemoteBuffer); bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize, out bytesWrittenOrRead); if (!bSuccess)
throw new SystemException("Failed to read from process memory"); retval = Marshal.PtrToStringUni((IntPtr)(lpLocalBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM))));
}
finally {
if (lpLocalBuffer != IntPtr.Zero)
Marshal.FreeHGlobal(lpLocalBuffer);
if (lpRemoteBuffer != IntPtr.Zero)
VirtualFreeEx(hProcess, lpRemoteBuffer, 0, Win32AllocationTypes.MEM_RELEASE);
if (hProcess != IntPtr.Zero)
CloseHandle(hProcess);
}
return retval;
} }
}

Read ListViewItem content from another process z的更多相关文章

  1. android 开发-Process and Thread

    目录 1 android中进程与线程 - Processes and Threads 1.1 进程 - Processes 1.1.1 进程的生命期 1.2 线程 - Threads 1.2.1 工作 ...

  2. C# 异步编程小结

    APM 异步编程模型,Asynchronous Programming Model EAP 基于事件的异步编程模式,Event-based Asynchronous Pattern TAP 基于任务的 ...

  3. SAP ECC CO 配置

    SAP ECC 6.0 Configuration Document Controlling (CO) Table of Content TOC \o \h \z 1. Enterprise Stru ...

  4. SAP ECC MM 配置文档

    SAP ECC 6.0 Configuration Document Materials Management (MM) Table of Content TOC \o \h \z 1. Genera ...

  5. JAVA开发--U盘EXE恢复工具

    原理比较简单,在学校机房U盘总被感染,写一个工具来方便用 package com.udiskrecover; import java.awt.Container; import java.awt.Fl ...

  6. linux 神器之wget

    1.什么是Wget? 首页,它是网络命令中最基本的.最好用的命令之一; 文字接口网页浏览器的好工具. 它(GNU Wget)是一个非交互从网上下载的自由工具(功能).它支持http.ftp.https ...

  7. RFC 2616

    Network Working Group R. Fielding Request for Comments: 2616 UC Irvine Obsoletes: 2068 J. Gettys Cat ...

  8. 【C/C++开发】c++ 工具库 (zz)

    下面是收集的一些开发工具包,主要是C/C++方面的,涉及图形.图像.游戏.人工智能等各个方面,感觉是一个比较全的资源.供参考!  原文的出处:http://www.codemonsters.de/ho ...

  9. .NET基础拾遗(5)多线程开发基础

    Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...

随机推荐

  1. 小shell函数

     whoport() {  port=$1  echo "------ who occupied port: $port ----------"  info=$(sudo lsof ...

  2. LinqToExcel常用对象

    1.ExcelQueryFactory对象(1)获取工作表名集合IEnumerable<string> GetWorksheetNames() //获取工作薄中的工作表名 foreach ...

  3. jQuery EasyUI之DataGrid使用示例

    jQuery EasyUI是一个轻量级的Web前端开发框架,提供了很多的现成组件帮助程序员减轻前端代码开发量,之前有个项目中就用到了其中的DataGrid. jQuery EasyUI框架的官方主页: ...

  4. jquery控制左右箭头滚动图片列表

    jquery控制左右箭头滚动图片列表的实例. 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&q ...

  5. Challenge Checkio(python)—初尝python练习网站

    最近在找点python语言练习的网站,发现这个网站不错 http://www.checkio.org/ 页面设计的也比较漂亮,比较适合学习python的语法知识.不过注册这个网站 开始就得解决一个py ...

  6. ASP.NET 学习小记 -- “迷你”MVC实现(1)

    ASP.NET 由于采用了管道式设计,具有很好的扩展性.整个ASP.NET MVC应用框架就是通过扩展ASP.NET实现的.通过ASP.NET的管道设计,我们知道,ASP.NET的扩展点主要是体现在H ...

  7. Sublime Text 3插件之SublimeTmpl:新建文件的模版插件

    SublimeTmpl能新建html.css.javascript.php.python.ruby六种类型的文件模板,所有的文件模板都在插件目录的templates文件夹里,可以自定义编辑文件模板. ...

  8. 微软Hololens学院教程-Hologram 210 Gaze(凝视)【微软教程已经更新,本文是老版本】

    这是老版本的教程,为了不耽误大家的时间,请直接看原文,本文仅供参考哦!原文链接:https://developer.microsoft.com/EN-US/WINDOWS/HOLOGRAPHIC/ho ...

  9. 使用.net 的Chart控件绘制曲线图

    在进行软件开发过程中我们可能会碰到需要生成图表的情况,在.NET中以前经常用GDI去绘制,虽然效果也不错,自从.NET 4.0开始,专门为绘制图表而生的Chart控件出现了,有了它,就可以轻松的绘制你 ...

  10. Java OCR tesseract 图像智能字符识别技术 Java实现

    Java OCR tesseract 图像智能字符识别技术 Java代码实现 接着上一篇OCR所说的,上一篇给大家介绍了tesseract 在命令行的简单用法,当然了要继承到我们的程序中,还是需要代码 ...