C# - 等待WinForms消息循环

时间:2011-09-12 18:58:15

标签: c# .net winforms multithreading message-loop

我必须编写一个C#API来注册全局热键。要接收WM_HOTKEY消息,我使用System.Windows.Forms.NativeWindow并使用System.Windows.Forms.Application.Run(ApplicationContext)运行自己的消息循环。当用户想要注册热键时,他必须运行一个名为RegisterHotkey()的方法,该方法使用System.Windows.Forms.ApplicationContext.ExitThread()停止消息循环,并使用RegisterHotKey()(P / Invoke)函数注册热键并且再次启动消息循环。这是必需的,因为必须在创建窗口的同一个线程中调用RegisterHotKey(),这必须在运行消息循环的同一个线程中实例化。

问题是,如果用户在启动运行消息循环的线程后不久调用RegisterHotkey()方法,ApplicationContext.ExitThread()会在Application.Run(ApplicationContext)之前调用,因此应用程序无限期地阻塞。有人知道等待消息循环开始的方法吗?

提前致谢!

4 个答案:

答案 0 :(得分:5)

因此需要从创建窗口的同一线程调用RegisterHotKey并启动消息循环。为什么不将RegisterHotKey的执行注入自定义消息循环线程?这样您就不需要停止并重新启动消息循环。你可以重复使用你开始的第一个,同时避免奇怪的竞争条件。

您可以使用ISynchronizeInvoke.Invoke将委托注入另一个线程,该委托将该委托封送到托管ISynchronizeInvoke实例的线程上。以下是它的完成方式。

void Main()
{
  var f = new Form();

  // Start your custom message loop here.
  new Thread(
    () =>
    {
      var nw = NativeWindow.FromHandle(f.Handle);
      Application.Run(new ApplicationContext(f));
    }

  // This can be called from any thread.
  f.Invoke(
    (Action)(() =>
    {
      RegisterHotKey(/*...*/);
    }), null);
}

我不知道......也许你会想要根据你所追求的行为来呼叫UnregisterHotKey。我对这些API并不熟悉,因此我无法评论它们的使用方式。

如果您不希望创建任意Form个实例,那么您可能可以通过SendMessage等向线程提交自定义消息并在NativeWindow.WndProc中处理它获得与ISynchronizeInvoke方法自动提供的效果相同的效果。

答案 1 :(得分:1)

我不知道是否有更好的方法,但您可以使用Mutex并在致电Application.Run时重置,并在致电Mutex.Wait()时使用Applicationcontext.ExitThread()

答案 2 :(得分:1)

您可能会尝试等待,直到触发Application.Idle事件以允许用户调用RegisterHotKey。

答案 3 :(得分:0)

我知道这个帖子已经老了,但我在尝试解决问题时来到这里,并花了一些时间查看接受的答案。它当然基本上是正确的,但是由于代码没有编译,它不会启动它创建的线程,即使我们修复它在创建f之前调用f.Invoke,所以抛出异常

我解决了所有问题,工作代码如下。我已经对答案做了评论,但我没有足够的代表。完成后,我有点不确定在以这种方式调用Application.Run之前强制创建表单的有效性,还是做了新表单'在一个线程上,然后将其传递给另一个线程以完全创建(特别是因为这很容易避免):

static void Main(string[] args)
{
    AutoResetEvent are = new AutoResetEvent(false);
    Form f = new Form();
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

    new Thread(
        () =>
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            var nw = NativeWindow.FromHandle(f.Handle);
            are.Set();
            Application.Run(f);
        }).Start();

    are.WaitOne(new TimeSpan(0, 0, 2));

    f.Invoke(
        (Action)(() =>
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        }), null);

    Console.ReadLine();
}