Winapi 如何唯一标识从其他应用程序检索的textbox控件?
我正在用C语言编写一个应用程序来远程控制另一个应用程序。我还没有访问该应用程序的机会,但它最有可能是用Visual C++或VB6编写的。我目前正在远程控制我创建的一个简单的C#winforms程序。到目前为止,我可以使用PInvoke以类似于Spy++的方式获得所有控件。我还可以在文本框中设置文本。我的问题是,我无法在我控制的应用程序执行之间唯一地标识特定的文本框。例如,我不能使用hWnd,因为每次运行应用程序时它都是不同的。GetWindowLong返回hWnd是什么,因此没有帮助。互联网上有一条消息叫做WM_GETCONTROLNAME。下面的可怕代码试图使用该消息获取开发人员用于唯一标识控件的名称。SendMessage似乎返回了名称中的字节数。但包含该名称的缓冲区返回全零 所以问题来了。如何修复下面的代码,使其正确返回该名称?(希望这是一个容易纠正的愚蠢错误)或者,同样好的是,是否有其他ID可以保证每次运行程序时都是相同的?如果没有唯一标识textbox控件的东西,我的代码就无法区分它们 我确实有一个想法。在我看来,使用文本框的位置来区分它们是可行的。但我更希望下面的代码能够正常工作Winapi 如何唯一标识从其他应用程序检索的textbox控件?,winapi,Winapi,我正在用C语言编写一个应用程序来远程控制另一个应用程序。我还没有访问该应用程序的机会,但它最有可能是用Visual C++或VB6编写的。我目前正在远程控制我创建的一个简单的C#winforms程序。到目前为止,我可以使用PInvoke以类似于Spy++的方式获得所有控件。我还可以在文本框中设置文本。我的问题是,我无法在我控制的应用程序执行之间唯一地标识特定的文本框。例如,我不能使用hWnd,因为每次运行应用程序时它都是不同的。GetWindowLong返回hWnd是什么,因此没有帮助。互联网上
public static string GetWindowText(IntPtr Handle)
{
int BufferSize = 256;
uint Message = RegisterWindowMessage("WM_GETCONTROLNAME");
StringBuilder sb = new StringBuilder(BufferSize);
byte[] ControlName = new byte[BufferSize];
long BytesRead = 0;
IntPtr BytesReadPointer = new IntPtr(BytesRead);
IntPtr OtherMem = IntPtr.Zero;
try
{
OtherMem = VirtualAllocEx(Handle, IntPtr.Zero, new IntPtr(sb.Capacity), AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
var Result = SendMessage(Handle, Message, new IntPtr(sb.Capacity), OtherMem);
//Result contains different numbers which seem like the correct lengths
var Result2 = ReadProcessMemory(Handle, OtherMem, ControlName, BufferSize, out BytesReadPointer);
//ControlName always comes back blank.
}
finally
{
var Result3 = VirtualFreeEx(Handle, OtherMem, BufferSize, FreeType.Release);
}
return ""; // Convert ControlName to a string
}
因此,在睡了一个好觉和做了一点小修补之后,我设法自己解决了这个问题。有两个问题。首先,我将句柄传递给VirtualAllocEx控件。但是在该函数的文档中,它说它需要一个进程句柄。于是开始供应。然后VirtualAllocEx告诉我我有一个无效的句柄。因此,传递给VirualAllocEx的句柄必须设置PROCESS\u VM\u操作。所以我调用OpenProcess来获取该句柄,然后将其传递给VirtualAllocEx。在那之后,一切顺利。我只需要将返回的字节数组转换成字符串,并修剪一堆空值。这将返回控件的Name属性。我会在这里发布代码,以防其他人需要类似的东西
[Flags]
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VirtualMemoryOperation = 0x00000008,
VirtualMemoryRead = 0x00000010,
VirtualMemoryWrite = 0x00000020,
DuplicateHandle = 0x00000040,
CreateProcess = 0x000000080,
SetQuota = 0x00000100,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
QueryLimitedInformation = 0x00001000,
Synchronize = 0x00100000
}
public static string GetWindowText(IntPtr ControlHandle, IntPtr ProcessHandle)
{
int BufferSize = 256;
StringBuilder sb = new StringBuilder(BufferSize);
byte[] ControlNameByteArray = new byte[BufferSize];
long BytesRead = 0;
IntPtr BytesReadPointer = new IntPtr(BytesRead);
IntPtr RemoteProcessMemoryAddress = IntPtr.Zero;
IntPtr RemoteProcessHandle = IntPtr.Zero;
try
{
uint Message = RegisterWindowMessage("WM_GETCONTROLNAME");
RemoteProcessHandle = OpenProcess(ProcessAccessFlags.CreateThread |
ProcessAccessFlags.QueryInformation |
ProcessAccessFlags.VirtualMemoryOperation |
ProcessAccessFlags.VirtualMemoryWrite |
ProcessAccessFlags.VirtualMemoryRead, false, Process.Id);
RemoteProcessMemoryAddress = VirtualAllocEx(RemoteProcessHandle, IntPtr.Zero, new IntPtr(sb.Capacity), AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
if (RemoteProcessMemoryAddress == IntPtr.Zero)
{
throw new Exception("GetWindowText: " + GetLastWin32Error());
}
SendMessage(ControlHandle, Message, new IntPtr(sb.Capacity), RemoteProcessMemoryAddress);
ReadProcessMemory(RemoteProcessHandle, RemoteProcessMemoryAddress, ControlNameByteArray, BufferSize, out BytesReadPointer);
string ControlName = Encoding.Unicode.GetString(ControlNameByteArray).TrimEnd('\0');
return ControlName;
}
finally
{
VirtualFreeEx(RemoteProcessHandle, RemoteProcessMemoryAddress, BufferSize, FreeType.Release);
}
}
为什么不自动使用呢?或者,你可以尝试使用。可用于任何父窗口类型,而不仅仅用于对话框。使用该工具可验证目标应用程序是否实现了自动化接口。如果是这样的话,就使用UI自动化(而不是你所提出的令人震惊的复杂和脆弱的黑客)。