如何检测鼠标点击触发标记粘贴导致的控制台WCF服务冻结
这个问题我之前也碰到过!本质上不是你的WCF服务真的“无响应”了——是Windows控制台的快速编辑模式在搞鬼:当你点击控制台窗口激活它时,这个模式会自动进入“标记选择”状态,此时系统会直接暂停你的服务进程,直到你按回车/ESC退出选择模式为止。
下面给你两个解决方案,按实用性排序:
方案一:彻底避免触发标记模式(强烈推荐)
既然你点击控制台是为了让它置顶,那完全可以用代码把控制台窗口设置为始终顶层,这样不用手动点击,自然不会触发快速编辑的选择状态。
直接用Windows API实现就行,代码很简单:
首先定义一个辅助类:
using System; using System.Runtime.InteropServices; public static class ConsoleWindowHelper { private const int HWND_TOPMOST = -1; private const uint SWP_NOSIZE = 0x0001; private const uint SWP_NOMOVE = 0x0002; private const uint SWP_SHOWWINDOW = 0x0040; [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("kernel32.dll")] private static extern IntPtr GetConsoleWindow(); // 调用这个方法设置控制台是否置顶 public static void SetConsoleTopmost(bool keepTopmost) { var consoleHandle = GetConsoleWindow(); SetWindowPos(consoleHandle, keepTopmost ? HWND_TOPMOST : 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } }
然后在你的WCF服务启动代码里加一行:
// 启动后让控制台保持顶层 ConsoleWindowHelper.SetConsoleTopmost(true);
这样控制台就会一直飘在所有窗口上面,再也不用手动点击,完美避开标记模式的坑,同时你依然可以正常使用标记粘贴功能(需要的时候手动按Ctrl+Shift+M进入标记模式,或者右键菜单选择标记)。
方案二:检测控制台是否处于标记选择状态(适合必须监控的场景)
如果一定要检测“冻结”状态,这里要注意一个关键点:当控制台进入标记模式时,你的服务进程会被系统完全暂停——进程里的所有线程都会停止运行,所以你没法在同一个进程里检测到“我现在被暂停了”。必须用一个独立的监控进程来做这件事。
下面是监控进程的核心代码,可以检测目标控制台是否处于选择模式:
using System; using System.Runtime.InteropServices; public class ConsoleSelectionMonitor { private const uint CONSOLE_SELECTION_IN_PROGRESS = 0x0001; [StructLayout(LayoutKind.Sequential)] private struct CONSOLE_SELECTION_INFO { public uint dwSize; public uint dwFlags; public COORD SelectionAnchor; public SMALL_RECT SelectionRect; } [StructLayout(LayoutKind.Sequential)] private struct COORD { public short X; public short Y; } [StructLayout(LayoutKind.Sequential)] private struct SMALL_RECT { public short Left; public short Top; public short Right; public short Bottom; } [DllImport("kernel32.dll", SetLastError = true)] private static extern bool AttachConsole(uint targetProcessId); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeConsole(); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool GetConsoleSelectionInfo(ref CONSOLE_SELECTION_INFO selectionInfo); // 传入目标进程ID,返回是否处于标记选择模式 public static bool IsConsoleInSelectionMode(uint targetProcessId) { if (!AttachConsole(targetProcessId)) { // 附加失败,比如目标进程没有控制台,或者权限不够 return false; } try { var selectionInfo = new CONSOLE_SELECTION_INFO(); selectionInfo.dwSize = (uint)Marshal.SizeOf(selectionInfo); if (GetConsoleSelectionInfo(ref selectionInfo)) { return (selectionInfo.dwFlags & CONSOLE_SELECTION_IN_PROGRESS) != 0; } return false; } finally { // 必须释放控制台关联,否则监控进程的控制台会被占用 FreeConsole(); } } }
监控进程可以定期调用IsConsoleInSelectionMode方法(比如用定时器每隔1秒查一次),传入你的WCF服务进程ID,就能知道它是不是因为标记模式被暂停了。
不过这个方案需要额外维护一个监控进程,不如方案一省心,所以优先推荐方案一。
内容的提问来源于stack exchange,提问作者flayn
相关产品推荐
相关产品推荐

