【问题标题】:Processing windows messages, threading COM objects处理 windows 消息,线程化 COM 对象
【发布时间】:2013-08-30 16:16:45
【问题描述】:

我有一个应用程序可以捕获发送到窗体的 Windows 消息,并在系统剪贴板有更新时运行一个方法:

protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case WM_CLIPBOARDUPDATE:

            if (IsClipboardListenerOn)
                OnClipboardChanged();

            break;
    }

    base.WndProc(ref m);
}

在某些情况下,我不希望我的方法 OnClipboardChanged() 运行。我的解决方案是设置一个静态全局变量IsClipboardListenerOn 并根据需要切换它。这没有完成工作,因为 Windows 消息直到 我的函数返回后才被处理,所以它总是正确的:

private void some_event(object sender, EventArgs e)
{
    MainForm.IsClipboardListenerOn = false;

    // some code that makes the clipboard changed message fire

    MainForm.IsClipboardListenerOn = true;

   // when this returns the WM_CLIPBOARDUPDATE message gets caught
}

下一个想法是在线程上运行触发消息事件的代码并等待线程返回以确保它在全局标志关闭时执行:

    Thread thread1 = new Thread(() => clipboard_stuff());
    thread1.SetApartmentState(ApartmentState.STA);

    MainForm.IsClipboardListenerOn = false;

    thread1.Start();        

    thread1.Join();
    MainForm.IsClipboardListenerOn = true;

但这也不起作用。事实上,有时它会执行我的 OnClipboardChanged() 方法两次。我知道系统剪贴板需要单线程单元;这是我的问题的一部分吗?我要解决这个问题了吗?我可以利用其他一些技术吗?

【问题讨论】:

    标签: c# .net multithreading winforms wndproc


    【解决方案1】:

    好吧,那是行不通的。 Windows 正在等待您再次开始发送消息。一种解决方法是延迟将标志设置回 true,直到所有待处理的消息都发送完毕。这可以通过使用 Control.BeginInvoke() 方法优雅地完成。像这样:

    private void some_event(object sender, EventArgs e)
    {
        MainForm.IsClipboardListenerOn = false;
        // some code that makes the clipboard changed message fire
        //...
        this.BeginInvoke(new Action(() => MainForm.IsClipboardListenerOn = true));
    }
    

    假设 some_event() 是表单的成员。我猜 MainForm.BeginInvoke() 也可以。

    【讨论】:

    • 抛出 NullReferenceException。 some_event 是 ToolStripMenuItem 单击事件的委托。我的全局标志定义为:static public bool IsClipboardListenerOn = true;
    • 我不知道你的对象引用是什么样的。选择不为空的东西:)
    • 很抱歉,我只是不熟悉您的基于 LINQ 的代码。那里不应该有任何空值。您调用一个新的 Action 对象并使用匿名委托对其进行实例化?您介意详细说明一下您的代码实际上在做什么吗?
    • 这不是 Linq,只是一个 lambda 表达式。您在 Thread 构造函数中自己的代码中使用的完全相同的动物。代码只是将一个委托对象传递给表单的 BeginInvoke() 方法。就像您将一个传递给 Thread 构造函数一样。
    • 是的 lambda,我的错。我想我只是不明白那里可能是空的。我将尝试进一步了解并继续调试。我收到后会回来更新问题状态,谢谢。
    【解决方案2】:

    听起来应该有一个比我要建议的更清洁的解决方案,但你知道他们说什么......如果它很愚蠢并且有效,那么它并不愚蠢。

    如果您确定要触发您想忽略的剪贴板消息,请设置一个标志并让WndProc 将其关闭:

    private void some_event(object sender, EventArgs e)
    {
        MainForm.SkipNextClipboardMessage = true;
        // some code that makes the clipboard changed message fire
    }
    
    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_CLIPBOARDUPDATE: 
                if (SkipNextClipboardMessage)
                    SkipNextClipboardMessage = false;
                else                    
                    OnClipboardChanged();
                break;
        }
        base.WndProc(ref m);
    }
    

    另一个肯定会引起读者反感的可怕想法是像您在代码的第一个版本中所做的那样,并在诱导您的剪贴板更改方法以强制处理消息后添加对Application.DoEvents()的调用。

    是的,是的,这是邪恶的,我知道我会下地狱的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-11
      • 1970-01-01
      相关资源
      最近更新 更多