枚举窗口句柄时C#Win32 Interop崩溃

时间:2014-06-23 17:39:55

标签: c# .net winapi interop pinvoke

我有一个涉及窗口句柄的Win32操作的C#包装器,但是当我调用Win32函数时,我遇到了意外崩溃,没有任何细节。

有趣的是,这个整个代码示例在构造类时(在应用程序初始化时)工作正常,但在调用相同的buildCache()方法后会失败。

以下是相关代码:

public delegate bool CallBack(int hWnd, int lParam);

public class Win32Interop {
    private Dictionary<int, string> windowCache = new Dictionary<int, string>();

    public Win32Interop() {
        buildCache();
    }

    public void buildCache() {
        windowCache.Clear();

        CallBack hWndCacher = new CallBack(saveHWndHandler);
        EnumWindows(hWndCacher, 0);
    }

    public void doThings(string title, uint message, bool rebuildCache = false) {
    //Use the window title to get its handle
        int hWnd = titleToHWnd(title, rebuildCache);
        SendMessage(hWnd, message, 0, 0);
    }

    private bool saveHWndHandler(int hWnd, int lParam) {
        if(IsWindow(hWnd) != 0) {       / ***** CRASHES HERE ***** /
            int length = GetWindowTextLength(hWnd);
            StringBuilder title = new StringBuilder(length + 1);

            GetWindowText(hWnd, title, title.Capacity);
            string formatted = title.ToString().Trim();

            windowCache.Add(hWnd, formatted);
        }

        return true;
    }

    private int titleToHWnd(string title, bool rebuildCache = false) {
        if(rebuildCache)
            buildCache();

        if(windowCache.ContainsValue(title)) {
            return windowCache.FirstOrDefault(x => x.Value.Contains(title)).Key;
        } else {
            throw new KeyNotFoundException(string.Format("\"{0}\" is not a window title which is available in the cache.", title));
        }
    }

    #region Win32 API Functions
    [DllImport("user32.dll")]
    private static extern int EnumWindows(CallBack lpEnumFunc, int lParam);

    [DllImport("user32.dll")]
    private static extern int GetWindowText(int hWnd, StringBuilder lpString, int maxCount);

    [DllImport("user32.dll")]
    private static extern int GetWindowTextLength(int hWnd);

    [DllImport("user32.dll")]
    private static extern int IsWindow(int hWnd);

    [DllImport("user32.lib")]
    private static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
    #endregion
}

saveHWndHandler()方法的内部,我标记了调试器显示执行停止的行。有趣的是,EnumWindows()通常返回约300个窗口句柄,并且它总是在迭代编号45或46上崩溃。它崩溃的窗口句柄是一个合理的值,例如12345。

根据MSDNIsWindow()应返回0,如果窗口未与句柄关联,则不会使线程崩溃。

有谁知道为什么会这样? Windows事件日志中没有抛出异常或任何详细信息。

谢谢。

  

对于那些不想弄清楚buildCache()过程的人:  (1.)调用buildCache()时,将清除<HWnd, Title>值的字典。  (2.)调用Win32函数EnumWindows(),为每个窗口句柄调用saveHWndHandler()方法。  (3.)saveHWndHandler()将检查当前窗口句柄是否仍然存在,调用另一个Win32来从句柄获取窗口标题。  (4.)标题和窗口句柄被添加到字典中。

1 个答案:

答案 0 :(得分:3)

我无法重现您的问题,但可能的问题是您的P / Invoke签名的全部错误。 P {Invoke代码中至少需要HWNDLPARAMWPARAM数据类型映射到IntPtr

private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString,
    int nMaxCount);

[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowTextLength(IntPtr hWnd);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindow(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam,
    IntPtr lParam);

您需要更改相应的实例方法签名和用法以匹配这些正确的签名。