Read ListViewItem content from another process z
2014-04-22 20:45
549 查看
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#:
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:
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:
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):
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;
}
}
}
相关文章推荐
- 如何利用Win32API取得另一支程式中的ListView內的所有值(RegisterHotKey,ReadProcessMemory,WindowFromPoint和VirtualAllocEx)
- Shifting List Item Values From One List To Another In Oracle Forms
- Error:Could not read entry ':app:processDebugManifest' from cache taskArtifacts.bin
- Permission Denial: opening provider com.xxc.day4(包名).TestContentProvider from ProcessRecord
- ListView在显示较少数据是,高度由item的个数决定,wrap_content有效
- read numbers from file and exchange their positions then write into another file
- Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement' 的解决办法
- Android studio 编译失败Error:Could not read entry ':app:processDebugManifest' from cache taskArtifacts.b
- How to get the password text in a text with password property from another process using C++ - 用C++如何从不同进程获取密码框文本
- Mysql SQL random read n item data from a table ( Mysql随机读取SQL查询语句 )
- Android studio 编译失败Error:Could not read entry ':app:processDebugManifest' from cache taskArtifacts.b
- Error:Could not read entry ':app:processDebugManifest' from cache taskArtifacts.bin
- Get Content Item using Query Componen from VCM
- Error:Could not read entry ‘:app:processJylcDebugManifest from cache taskArtifacts.bin
- Android Studio 编译失败 Could not read entry ':app:processDebugManifest' from cache taskArtifacts.bin
- The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make s
- 例子:Read content from file to Arraylist
- 通过ContentObserver监听数据库某表的增加、删除、更新动作,实现listView异步单项Item的刷新
- 【转】IIS: The process cannot access the file because it is being used by another process. (Exception from HRESULT: 0x80070020)
- ListView无障碍识别整个listView,不识别item,设置了setContentDescription也没有用