【发布时间】:2017-10-01 06:16:37
【问题描述】:
我试图从层次结构中的每个控件中获取文本。如果我使用unsafe 方法,以下代码运行良好。但是,使用非托管版本似乎会破坏hWnd,导致hWnd = GetAncestor(hWnd, GetAncestorFlags.GA_PARENT) 抱怨:
System.AccessViolationException: '试图读取或写入受保护的内存。这通常表明其他内存已损坏。'
我检查了hWnd 在从GetWindowTextRaw 函数返回后没有改变,如果我在这个函数中注释掉第二个SendMessage 不会导致问题(尽管它显然不会得到窗口文本)。
(PS:我在 NuGet 中使用 PInvoke.User32)
// using static PInvoke.User32;
public static string GetWindowTextRaw(IntPtr hWnd) {
// Allocate correct string length first
int length = (int)SendMessage(hWnd, WindowMessage.WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
char[] buff = new char[length + 1];
IntPtr iptr = Marshal.AllocHGlobal(buff.Length);
SendMessage(hWnd, WindowMessage.WM_GETTEXT, (IntPtr)(length + 1), iptr);
Marshal.Copy(iptr, buff, 0, length + 1);
Marshal.FreeHGlobal(iptr);
//unsafe
//{
// fixed (char* p = buff)
// SendMessage(hWnd, WindowMessage.WM_GETTEXT, (IntPtr)(length + 1), (IntPtr)p);
//}
return new string(buff).TrimEnd('\0');
}
private void button1_Click(object sender, EventArgs {
POINT p;
IntPtr hWnd;
//while (true)
if (GetCursorPos(out p)) {
hWnd = WindowFromPoint(p); ;
Debug.Print($"{p.x} {p.y} 0x{(int)hWnd:x8}");
while (hWnd != IntPtr.Zero) {
Debug.Print($"{GetWindowTextRaw(hWnd)}");
hWnd = GetAncestor(hWnd, GetAncestorFlags.GA_PARENT);
}
Thread.Sleep(500);
}
}
【问题讨论】:
-
@Ðаn 怎么样?有参考吗?谢谢!
-
您的编组看起来过于复杂且容易出错。 Some inspiration。根据您的用例,您可能还需要考虑使用 UI Automation 而不是低级别的
WM_GETTEXTgroveling。 -
@TideGu:一致性很好,正确性更好。
PInvoke.User32没有什么神圣之处。特别是,真正的功能是SendMessage。围绕它的托管包装器就是:托管包装器。拥有一个,拥有一千个,只要它使调用非托管代码变得轻松(并且StringBuilder比传递指针更轻松)。如果您不想将世界其他地方暴露给此版本,您甚至可以将其声明为私有方法。 -
@TideGu:Hans Passant 已经解释过它是堆损坏。实际上,仅在 Debug 而不是 Release 中看到问题是很常见的,反之亦然,这取决于您遇到了什么错误,因为内存布局以及 marshaler 和堆分配器本身执行的检查在模式之间是不同的.这就是为什么您通常希望完全避免手动分配缓冲区的原因——这太容易出错了。
标签: c# unmanaged sendmessage