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

C# 如何在非主线程中获取剪贴板数据?,c#,multithreading,winforms,clipboard,C#,Multithreading,Winforms,Clipboard,我试图用下面的代码从剪贴板中获取数据 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(TextData

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

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)

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

Clipboard.GetText(TextDataFormat.Text)使用COM并在未标记STAThreadAttribute的线程中调用时引发异常

解决此问题的一种方法是使用委托将Clipboard.GetText的调用返回到带有Invoke的主线程。但在这种情况下,线程将在调用时冻结它的执行,直到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;
        }
    }
GetText(TextDataFormat.Text)使用COM并在未标记STAThreadAttribute的线程中调用时引发异常

解决此问题的一种方法是使用委托将Clipboard.GetText的调用返回到带有Invoke的主线程。但在这种情况下,线程将在调用时冻结它的执行,直到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;
        }
    }

我最后使用下面的方法访问剪贴板文本

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

我最后使用下面的方法访问剪贴板文本

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

下面的代码适用于我

        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 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)
        {
        }
灵感来源于本文和本文来源

谢谢你的帮助,但我最后选择了另一条捷径。将它添加到答案中。谢谢您的帮助,但我最终选择了另一个较短的方法。将它添加到答案中。