C# - System.Windows.Forms.Clipboard.GetDataObject()犯规响应 [英] C# - System.Windows.Forms.Clipboard.GetDataObject() doesnt response

查看:237
本文介绍了C# - System.Windows.Forms.Clipboard.GetDataObject()犯规响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有谁知道为什么 System.Windows.Forms.Clipboard.GetDataObject()从不同的线程和前主线程停止,调用后调用时它不返回 System.Windows.Forms.Clipboard.Clear()主线程中?

Does anybody know why System.Windows.Forms.Clipboard.GetDataObject() doesnt return when calling it from a different thread and before the main thread stops, after calling System.Windows.Forms.Clipboard.Clear() within the main thread?

我已经写了一个示例程序来解释我的问题:

I have written a sample Program to explain my question:

public class ClipboardDemo
{
    [STAThread]
    public static void Main(string[] args)
    {
        Thread.CurrentThread.Name = "MAIN_THREAD";

        Thread clipboardViewerThread = new Thread(RunClipboardViewer);
        clipboardViewerThread.Name = "CLIPBOARD_VIEWER_THREAD";
        clipboardViewerThread.SetApartmentState(ApartmentState.STA);

        Thread clipboardClearerThread = new Thread(RunClipboardClearer);
        clipboardClearerThread.Name = "CLIPBOARD_CLEARER_THREAD";
        clipboardClearerThread.SetApartmentState(ApartmentState.STA);

        Console.WriteLine("Starting " + clipboardViewerThread.Name + ", expecting initial WM_DRAWCLIPBOARD message...");
        clipboardViewerThread.Start();
        Thread.Sleep(1000);

        Console.WriteLine("Clearing clipboard from " + clipboardClearerThread.Name + ", expecting WM_DRAWCLIPBOARD message...");
        clipboardClearerThread.Start();
        clipboardClearerThread.Join();

        Console.WriteLine("Clearing clipboard from " + Thread.CurrentThread.Name + ", expecting WM_DRAWCLIPBOARD message...");
        Clipboard.Clear();
        Thread.Sleep(1000);

        Application.Exit();
        Console.WriteLine("\t" + Thread.CurrentThread.Name + " stopped!");
    }

    private static void RunClipboardViewer()
    {
        ClipboardViewer viewer = new ClipboardViewer();
        viewer.ViewClipboard();
        viewer.Dispose();
    }

    private static void RunClipboardClearer()
    {
        Clipboard.Clear();
    }
}

internal class ClipboardViewer : NativeWindow, IDisposable
{
    private const int WM_CREATE = 0x0001;
    private const int WM_DRAWCLIPBOARD = 0x0308;
    private const int WM_CHANGECBCHAIN = 0x030D;

    private IntPtr nextViewer;

    public void ViewClipboard()
    {
        base.CreateHandle(new CreateParams());
        Application.Run();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        switch (m.Msg)
        {
            case WM_CREATE:
                nextViewer = User32Interop.SetClipboardViewer(base.Handle);
                break;
            case WM_DRAWCLIPBOARD:
                if (nextViewer != IntPtr.Zero)
                {
                    User32Interop.SendMessage(nextViewer, WM_DRAWCLIPBOARD, m.WParam, m.LParam);
                }
                Console.WriteLine("\tClipboard changed in " + Thread.CurrentThread.Name + ". Trying to receive data object...");
                Clipboard.GetDataObject();
                Console.WriteLine("\tData object received!");
                break;
            case WM_CHANGECBCHAIN:
                if (m.WParam == nextViewer)
                {
                    nextViewer = m.LParam;
                }
                else if (nextViewer != IntPtr.Zero)
                {
                    User32Interop.SendMessage(nextViewer, WM_CHANGECBCHAIN, m.WParam, m.LParam);
                }
                break;
        }
    }

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            User32Interop.ChangeClipboardChain(base.Handle, nextViewer);
        }
        base.DestroyHandle();
    }

    ~ClipboardViewer()
    {
        Dispose(false);
    }
}

internal static class User32Interop
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
}

这样做的输出格式为:

The formatted output of this is:

Starting CLIPBOARD_VIEWER_THREAD, expecting initial WM_DRAWCLIPBOARD message...
  Clipboard changed in CLIPBOARD_VIEWER_THREAD. Trying to receive data object...
  Data object received!

Clearing clipboard from CLIPBOARD_CLEARER_THREAD, expecting WM_DRAWCLIPBOARD message...
  Clipboard changed in CLIPBOARD_VIEWER_THREAD. Trying to receive data object...
  Data object received!

Clearing clipboard from MAIN_THREAD, expecting WM_DRAWCLIPBOARD message...
  Clipboard changed in CLIPBOARD_VIEWER_THREAD. Trying to receive data object...
  MAIN_THREAD stopped!
  Data object received!

正如你可以在最后三行看, System.Windows.Forms.Clipboard.GetDataObject()返回时主线程停止,而不是更早。是否有人知道这个问题的解决方案?

As you can see in the last three lines, System.Windows.Forms.Clipboard.GetDataObject() returns when the main thread stops, but not earlier. Does anybody know a solution for this problem?

在此先感谢!

推荐答案

您这样做是正确的,选择STA的工作线程,并让他们抽消息循环。除了一:你的主线程。它只是偶然泵。为Thread.join()调用,使CLR泵。但Thread.sleep()方法不抽。你可以用这种随意更换和修复您的问题:

You are doing it right, selecting STA for the worker threads and having them pump a message loop. Except in one: your main thread. It pumps only by accident. The Thread.Join() call makes the CLR pump. But Thread.Sleep() doesn't pump. You can arbitrarily replace it with this and fix your problem:

    var dummy = new AutoResetEvent(false);
    dummy.WaitOne(1000);

但是,这是一个黑客。我知道这只是一个测试程序,想想你真正的人会是什么样子。

But that's a hack. I realize this is just a test app, think about what your real one is going to look like.

这篇关于C# - System.Windows.Forms.Clipboard.GetDataObject()犯规响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆