我需要更改外部应用程序拥有的TreeView(恰好是“ SysTreeView32”)项中的复选框状态-为了实现自动化。我已经有TreeView句柄和TreeViewItem句柄。我还找到了一些示例,可以用来设置复选框状态,但是由于某些原因,它无法正常工作(SendMessage返回0或导致整个应用程序崩溃)。但是要代码。我已经尝试过的是这个:
TVITEM结构:
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Auto)]
internal struct TVITEM
{
public int mask;
public IntPtr hItem;
public int state;
public int stateMask;
[MarshalAs(UnmanagedType.LPTStr)]
public string pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public IntPtr lParam;
}
为SendMessage拨叫
:[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, ref TVITEM lParam);
我的方法:
internal static void SetTreeNodeState(int treeViewHandler, int treeViewItemHandler, bool state)
{
TVITEM tvItem = new TVITEM();
tvItem.mask = TVIF_STATE | TVIF_HANDLE;
tvItem.hItem = (IntPtr)treeViewItemHandler;
tvItem.stateMask = TVIS_STATEIMAGEMASK;
tvItem.state = (state ? 2 : 1) << 12;
var result = SendMessage((IntPtr)treeViewHandler, TVM_SETITEMW, IntPtr.Zero, ref tvItem);
}
这是最接近的方法(我认为,最后我没有使目标应用程序崩溃过一次)。当然,我尝试使用Spy ++嗅探目标树视图的消息。让我担心的是Spy ++显示,用于SendMessage的LParam实际上是“ TVITEMEXW”,但是我可以通过beryl找到有关该结构的任何信息。
通常,我也尝试使用TVM_GETITEMW进行相同的思考,但是我没有使应用程序崩溃,SendMessage始终返回零。
我做错了什么?
答案 0 :(得分:1)
发送此特定消息时,应提供结构的地址。由于该窗口由另一个进程拥有,因此您提供的地址无效。 Windows进程具有隔离的虚拟内存地址空间。您提供的地址在您的流程中有效,但仅在您的流程中有效。
为了解决此问题并发送此消息,您需要使用VirtualAllocEx
在目标进程中分配内存。您还需要使用WriteProcessMemory
来填充结构。如果您的过程和目标过程的位数不同,则需要注意结构布局的所有可能问题。您需要对像pszText
这样本身就是指针的成员执行相同的技巧。
这里已经有很多问题涉及跨进程消息编组。我相信您将能够找到它们。同样,在知道问题后,您会在网上找到很多教程。
也许更大的问题是,其他进程可能不会以这种方式响应您期望从外部戳入的方式。如果您发现能够编写自己的跨过程自动化非常困难,请不要感到惊讶。为何不使用UI自动化,而不是这样做?
答案 1 :(得分:1)
Okey,感谢David Heffernan,我弄清楚了这一点。我已经为SendMessage创建了重载,它通过引用将lParam接受为对象:
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, Int32 nSize, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, AllocationType dwFreeType);
private static IntPtr SendMessage<T>(Process process, IntPtr hWnd, int msg, int wParam, ref T lParam)
{
uint size = (uint)Marshal.SizeOf(lParam);
byte[] buffer = new byte[size];
IntPtr processHandle = process.Handle;
IntPtr pPointer = VirtualAllocEx(processHandle, IntPtr.Zero, size, AllocationType.Commit | AllocationType.Reserve, MemoryProtection.ReadWrite);
IntPtr inputPtr = Marshal.AllocHGlobal((int)size);
IntPtr outputPtr = Marshal.AllocHGlobal((int)size);
Marshal.StructureToPtr(lParam, inputPtr, false);
WriteProcessMemory(processHandle, pPointer, inputPtr, size, out UIntPtr nNbBytesWritten);
IntPtr resultPtr = SendMessage(hWnd, msg, wParam, pPointer);
ReadProcessMemory(processHandle, pPointer, buffer, buffer.Length, out IntPtr nNbBytesRead);
Marshal.Copy(buffer, 0, outputPtr, (int)size);
T result = Marshal.PtrToStructure<T>(outputPtr);
lParam = result;
Marshal.FreeHGlobal(inputPtr);
Marshal.FreeHGlobal(outputPtr);
VirtualFreeEx(processHandle, pPointer, 0, AllocationType.Release);
return resultPtr;
}
用法示例
设置给定树视图项目的复选框状态:
internal static void SetTreeNodeState(IntPtr treeViewHandle, IntPtr treeViewItemHandle, bool state)
{
TVITEM tvItem = new TVITEM
{
mask = TVIF_STATE | TVIF_HANDLE,
hItem = treeViewItemHandle,
stateMask = TVIS_STATEIMAGEMASK,
state = (uint)(state ? 2 : 1) << 12
};
Process process = Process.GetProcessesByName("ProcessName")[0];
IntPtr ptr = SendMessage(process, treeViewHandle, TVM_SETITEMW, 0, ref tvItem);
}
获取给定树视图项目的复选框状态:
internal static bool GetTreeNodeState( IntPtr treeViewHandle, IntPtr treeViewItemHandle)
{
TVITEM tvItem = new TVITEM
{
mask = TVIF_STATE | TVIF_HANDLE,
hItem = treeViewItemHandle,
stateMask = TVIS_STATEIMAGEMASK,
state = 0
};
Process process = Process.GetProcessesByName("ProcessName")[0];
IntPtr ptr = SendMessage(process, treeViewHandle, TVM_GETITEMW, 0, ref tvItem);
if (ptr != IntPtr.Zero)
{
uint iState = tvItem.state >> 12;
return iState == 2 ? true : false;
}
return false;
}
TVITEM:
[StructLayout(LayoutKind.Sequential)]
internal struct TVITEM
{
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public IntPtr lParam;
}