【问题标题】:What's wrong with this unmanaged code in C#?C# 中的这个非托管代码有什么问题?
【发布时间】: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_GETTEXT groveling。
  • @TideGu:一致性很好,正确性更好。 PInvoke.User32 没有什么神圣之处。特别是,真正的功能是SendMessage。围绕它的托管包装器就是:托管包装器。拥有一个,拥有一千个,只要它使调用非托管代码变得轻松(并且StringBuilder 比传递指针更轻松)。如果您不想将世界其他地方暴露给此版本,您甚至可以将其声明为私有方法。
  • @TideGu:Hans Passant 已经解释过它是堆损坏。实际上,仅在 Debug 而不是 Release 中看到问题是很常见的,反之亦然,这取决于您遇到了什么错误,因为内存布局以及 marshaler 和堆分配器本身执行的检查在模式之间是不同的.这就是为什么您通常希望完全避免手动分配缓冲区的原因——这太容易出错了。

标签: c# unmanaged sendmessage


【解决方案1】:
IntPtr iptr = Marshal.AllocHGlobal(buff.Length);

尺寸错误,您需要buff.Length * sizeof(char)。是现在分配的两倍。如所写,代码破坏了操作系统使用的同一个堆,接下来任何事情都可能发生。 AVE 是正常且快乐的结果,但不能保证。

【讨论】:

  • 谢谢!完美解决了这个问题!我专注于hWnd 本身的价值,但从没想过它会溢出!更糟糕的是,我在编组方面的经验很少,很多其他帖子都是AllocHGlobal -ing 字节,不需要乘数,所以我没有考虑大小。非常感谢您的大力帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 2015-02-16
  • 2015-02-23
  • 1970-01-01
相关资源
最近更新 更多