代码之家  ›  专栏  ›  技术社区  ›  Ashok

如何在非主线程中获取剪贴板数据?

  •  1
  • Ashok  · 技术社区  · 7 年前

    我试图用下面的代码从剪贴板中获取数据。

    private void TestBtn_Click(object sender, EventArgs e)
    {
     Thread sampleThread = new Thread(SampleMethod);
     sampleThread.IsBackground = true;
     sampleThread.Start();
     Thread.Sleep(2000);
     var textFromMain = Clipboard.GetText(TextDataFormat.Text);
    }
    
    private void SampleMethod()
    {
     var textFromThread = Clipboard.GetText(TextDataFormat.Text);
     Thread.Sleep(1000);
    }
    

    我用这一行将任何文本复制到剪贴板-

    var textFromMain = Clipboard.GetText(TextDataFormat.Text);

    但下面的行返回空字符串或空字符串。

    var textFromThread = Clipboard.GetText(TextDataFormat.Text);

    我不明白问题出在哪里。有人能帮我理解吗。如果是多线程,请给我指出正确的方向。

    3 回复  |  直到 7 年前
        1
  •  5
  •   Yousha Aleayoub Albert    3 年前

    Clipboard.GetText(TextDataFormat.Text) 使用COM和抛出 例外 如果在未标记的线程中调用 STAThreadAttribute .

    一种解决方法是使用委托返回 Clipboard.GetText 调用到主线程。但在这种情况下,线程将在调用时冻结其执行,直到 SampleMethod() 在主窗体线程上结束它的执行,主线程将被释放。

    另一种方法是使用自己对COM的调用来获取剪贴板文本,而不是 System.Windows.Forms.Clipboard.GetText() 看见 ClipboardCom.GetText() ,此方法不需要等待主窗体线程。

        private string _textFromMain, _textFromThreadByInvoke, _textFromThreadByCom;
    
        private delegate string GetClipboardInvoke(TextDataFormat textformat);
        private void SampleInvokeMethod()
        {
            GetClipboardInvoke invokerClipboard = new GetClipboardInvoke(Clipboard.GetText);
            _textFromThreadByInvoke = (string)this.Invoke(invokerClipboard, TextDataFormat.Text); // where this is a Form
            Thread.Sleep(1000);
        }
    
    
    
        private void button1_Click(object sender, EventArgs e)
        {
            Thread sampleInvokeThread = new Thread(SampleInvokeMethod) { IsBackground = true };
            sampleInvokeThread.Start();
    
            Thread sampleComThread = new Thread(SampleComMethod) { IsBackground = true };
            sampleComThread.Start();
    
            Thread.Sleep(10000);
            _textFromMain = Clipboard.GetText(TextDataFormat.Text);
        }
    
    
        private void SampleComMethod()
        {
            _textFromThreadByCom = ClipboardCom.GetText();
            Thread.Sleep(1000);
        }
    
        public static class ClipboardCom
        {
            [DllImport("user32.dll")]
            static extern IntPtr GetClipboardData(uint uFormat);
            [DllImport("user32.dll")]
            static extern bool IsClipboardFormatAvailable(uint format);
            [DllImport("user32.dll", SetLastError = true)]
            static extern bool OpenClipboard(IntPtr hWndNewOwner);
            [DllImport("user32.dll", SetLastError = true)]
            static extern bool CloseClipboard();
            [DllImport("kernel32.dll")]
            static extern IntPtr GlobalLock(IntPtr hMem);
            [DllImport("kernel32.dll")]
            static extern bool GlobalUnlock(IntPtr hMem);
    
            const uint CF_UNICODETEXT = 13;
    
            public static string GetText()
            {
                if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
                    return null;
                if (!OpenClipboard(IntPtr.Zero))
                    return null;
    
                string data = null;
                var hGlobal = GetClipboardData(CF_UNICODETEXT);
                if (hGlobal != IntPtr.Zero)
                {
                    var lpwcstr = GlobalLock(hGlobal);
                    if (lpwcstr != IntPtr.Zero)
                    {
                        data = Marshal.PtrToStringUni(lpwcstr);
                        GlobalUnlock(lpwcstr);
                    }
                }
                CloseClipboard();
    
                return data;
            }
        }
    
        2
  •  3
  •   Ashok    7 年前

    我最后使用以下方法访问剪贴板文本。

    private string GetClipBoradData()
        {
            try
            {
                string clipboardData= null;
                Exception threadEx = null;
                Thread staThread = new Thread(
                    delegate ()
                    {
                        try
                        {
                            clipboardData= Clipboard.GetText(TextDataFormat.Text);
                        }
    
                        catch (Exception ex)
                        {
                            threadEx = ex;
                        }
                    });
                staThread.SetApartmentState(ApartmentState.STA);
                staThread.Start();
                staThread.Join();
                return clipboardData;
            }
            catch (Exception exception)
            {
                return string.Empty;
            }
        }
    
        3
  •  1
  •   Sajeeb Chandan Saha    4 年前

    以下代码适用于我。

            Thread theThread = new Thread((ThreadStart)delegate
            {
                var selectedPodcastPlaylist = (PodcastPlaylist)metroGridPodcastPlaylist.SelectedRows[0].DataBoundItem;
                Clipboard.SetText(selectedPodcastPlaylist.URI);
            });
    
            try
            {
                //set STA as the Open file dialog needs it to work
                theThread.TrySetApartmentState(ApartmentState.STA);
    
                //start the thread
                theThread.Start();
    
                // Wait for thread to get started
                while (!theThread.IsAlive) { Thread.Sleep(1); }
    
                // Wait a tick more (@see: http://scn.sap.com/thread/45710)
                Thread.Sleep(1);
    
                //wait for the dialog thread to finish
                theThread.Join();
            }
            catch (Exception)
            {
            }
    

    受文章和来源启发 Thread Apartment Safe Open/Save File Dialogs for C#