C # - System.Windows.Forms.Clipboard.GetDataObject () not responding

Does anyone know why System.Windows.Forms.Clipboard.GetDataObject() does not return when called from another thread and until the main thread stops after calling System.Windows.Forms.Clipboard.Clear() in the main thread?

I wrote an example 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 result of this:

 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! 

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

Thanks in advance!

+4
source share
1 answer

You do it right by choosing STAs for workflows and pumping up a message loop. Except for one: your main thread. It pumps only by accident. Calling Thread.Join () makes the CLR pump. But Thread.Sleep () does not work. You can arbitrarily replace it with this and fix your problem:

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

But it's a hack. I understand that this is just a test application, think about how your real one will look.

+3
source

All Articles