2011-09-09 5 views
4

System.Windows.Forms.Clipboard.GetDataObject() 다른 스레드에서 호출 할 때 및 주 스레드가 중지되기 전에 주 스레드 내에서 System.Windows.Forms.Clipboard.Clear()을 호출 한 후 그 이유를 아는 사람이 있습니까?System.Windows.Forms.Clipboard.GetDataObject() doesnt 응답

나는 내 질문에 설명하는 샘플 프로그램을 작성했습니다

:

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); 
} 

이의 형식화 된 출력은 다음과 같습니다

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! 

지난 3 개 라인에서 볼 수 있듯이, System.Windows.Forms.Clipboard.GetDataObject() 반환 할 때 주 스레드는 멈추지 만 이전에는 그렇지 않습니다. 아무도이 문제에 대한 해결책을 알고 있습니까?

미리 감사드립니다.

+0

빠른 답장을 보내 주셔서 감사합니다. 모든 스레드에 대해 Thread.SetApartmentState (ApartmentState.STA)를 호출했고 주 스레드의 Main() 메서드에 STAThreadAttribute를 추가하여 모든 스레드가 STA 모드로 실행되도록했습니다. 하나의 프로세스에서 여러 개의 STA 스레드를 가질 수는 없습니까? 내 접근 방식은 클립 보드 변경 이벤트를 잡기위한 클래스 라이브러리를 작성하는 것이므로 모든 UI가 havent ... –

+0

실례합니다 Jon Skeet, 저는 stackoverflow에 새로운 것 같습니다. 실수로 게시물을 삭제 한 것으로 보입니다. S 가능한 한 당신은 내가 STA 스레드 내에서만 클립 보드에 액세스 할 수 있고 Control.Invoke() 접근 방식을 권유한다고 말한 msdn에 대해 기억하고 있다는 것을 기억하십시오. –

답변

3

작업자 스레드에 대해 STA를 선택하고 메시지 루프를 펌프하는 것이 좋습니다. 하나만 빼면 : 당신의 주요 실. 우연히 만 펌프질합니다. Thread.Join() 호출은 CLR 펌프를 만듭니다. 하지만 Thread.Sleep()은 펌프를 작동시키지 않습니다. 임의로이를 다음과 같이 바꾸고 문제를 해결할 수 있습니다.

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

해킹입니다. 이 앱이 테스트 앱일 뿐이라는 것을 알고 있습니다. 실제 앱이 어떻게 보이는지 생각해보십시오.

+0

해킹 해 주셔서 감사합니다. 그러나 winforms에 익숙하지 않기 때문에 나는 왜 그 이유를 정말로 이해하지 못합니다. 내 실수를 이해하는 데 도움이 될만한 설명이나 링크를 제공 할 수 있다면 좋을 것입니다. –

+0

STA의 중요성에 대한 답변을 확인하십시오. http://stackoverflow.com/questions/4154429/apartmentstate-for-dummies/4156000#4156000 –