[英]Invoke delegate on main thread in console application
在Windows应用程序中,当使用多个线程时,我知道有必要调用主线程来更新GUI组件。 如何在控制台应用程序中完成?
例如,我有两个线程,一个主线程和一个辅助线程。 辅助线程始终在侦听全局热键; 当它被按下时,辅助线程执行一个事件,该事件延伸到win32 api方法AnimateWindow。 我收到一个错误,因为只允许主线程执行所述函数。
当“调用”不可用时,如何有效地告诉主线程执行该方法?
class Hud { bool isHidden = false; int keyId; private static IntPtr windowHandle; public void Init(string[] args) { windowHandle = Process.GetCurrentProcess().MainWindowHandle; SetupHotkey(); InitPowershell(args); Cleanup(); } private void Cleanup() { HotKeyManager.UnregisterHotKey(keyId); } private void SetupHotkey() { keyId = HotKeyManager.RegisterHotKey(Keys.Oemtilde, KeyModifiers.Control); HotKeyManager.HotKeyPressed += new EventHandler<HotKeyEventArgs>(HotKeyManager_HotKeyPressed); } void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e) { ToggleWindow(); } private void ToggleWindow() { //exception is thrown because a thread other than the one the console was created in is trying to call AnimateWindow if (isHidden) { if (!User32.AnimateWindow(windowHandle, 200, AnimateWindowFlags.AW_VER_NEGATIVE | AnimateWindowFlags.AW_SLIDE)) throw new Win32Exception(Marshal.GetLastWin32Error()); } else { if (!User32.AnimateWindow(windowHandle, 200, AnimateWindowFlags.AW_VER_POSITIVE | AnimateWindowFlags.AW_HIDE)) throw new Win32Exception(Marshal.GetLastWin32Error()); } isHidden = !isHidden; } private void InitPowershell(string[] args) { var config = RunspaceConfiguration.Create(); ConsoleShell.Start(config, "", "", args); } }
在以下情况下,该功能将失败:
- [...]
- 如果线程不拥有窗口。 [...]
所以这里没有“主要”线程(AFAIK Win32Api不关心执行程序入口点的女巫线程)。
唯一的条件是您必须在拥有您正在设置动画的窗口的线程上执行AnimateWindow。 这就是调用CreateWindow的那个,因为它是定义线程/消息循环afinity的函数。
既然您已经发布了示例代码,那么您将遇到的问题是您没有尝试为任何窗口设置动画......但是您正在尝试为控制台窗口设置动画...而且您不在它上面所有者线程(否则当你在应用程序中创建一个无限循环时它不会刷新)...所以调用AnimateWindow是不可能的,除非你设法强制windows在该线程上执行代码。
事实控制台窗口实际上归CSRSS所有。巫婆是一个以提升权利执行的系统流程,无论如何都会使它们变得非常危险。
由于windows vista,甚至不可能由于进程保护而向这些窗口发送消息,因此任何可能被利用以前强制该线程执行代码的漏洞现在都应该是不可用的。
有关控制台窗口特性的详细信息,请参阅为什么不在Windows XP上设置控制台窗口? 在Raymond Chen博客上发帖(来自微软windows shell团队所以它几乎来自源码)
通常情况下,它不是在控制台应用程序中完成的。 如果你正在尝试使用Win32 GUI API,我应该真正运行一个消息循环,我怀疑。
您可以从控制台应用程序调用Application.Run()
或Application.Run(ApplicationContext)
来启动新的消息循环。 我们的想法是使用SynchronizationContext.Current
来编组回主线程。 但是,我还没有设法让它工作...你需要以某种方式强制它将其消息循环注册为当前的同步上下文,我还没有设法说服它这样做:(
我在想背景工作者,但是因为你没有一个不可能的UI线程( 见这个问题 )
使用信号量的“经典”线程方法应该可行。 使用线程安全队列或集合来存储事件,并通知主线程有工作要通过同步对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.