我有一个涉及窗口句柄的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。
根据MSDN,IsWindow()
应返回0
,如果窗口未与句柄关联,则不会使线程崩溃。
有谁知道为什么会这样? Windows事件日志中没有抛出异常或任何详细信息。
谢谢。
对于那些不想弄清楚buildCache()过程的人: (1.)调用
buildCache()
时,将清除<HWnd, Title>
值的字典。 (2.)调用Win32函数EnumWindows()
,为每个窗口句柄调用saveHWndHandler()
方法。 (3.)saveHWndHandler()
将检查当前窗口句柄是否仍然存在,调用另一个Win32来从句柄获取窗口标题。 (4.)标题和窗口句柄被添加到字典中。
答案 0 :(得分:3)
我无法重现您的问题,但可能的问题是您的P / Invoke签名的全部错误。 P {Invoke代码中至少需要HWND
,LPARAM
和WPARAM
数据类型映射到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);
您需要更改相应的实例方法签名和用法以匹配这些正确的签名。