C#在非主线程获取剪切板内容 - 小众知识

C#在非主线程获取剪切板内容

2023-02-06 06:24:58 苏内容
  标签: C#/线程
阅读:3090

How to get clipboard data in non main thread?

I'm trying to get data from Clipboard with the below code.

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

I'm getting whatever text copied to clipboard with this line -

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

But below line returns empty string or blank string.

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

在子线程,无法正确获得剪切板内容

I'm not getting what's the issue. Can somebody help me to understand. If it's multi-threading, please point me to right direction.



Clipboard.GetText(TextDataFormat.Text) use COM and throws exception if called in thread which not marked with STAThreadAttribute.

One way to solve is use delegate to return call of Clipboard.GetText to main thread with Invoke. But in this case thread will freeze it execution on Invoke till the SampleMethod() ends it execution on main form thread and main thread will be free.

Other way is to use own call to COM to get clipboard text instead of System.Windows.Forms.Clipboard.GetText(), see ClipboardCom.GetText(), this method not need waiting main form thread.

   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;

       }    }



4

I finally used below method to access Clipboard text.

private string GetClipboardData()

   {

       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;

       }    }



The following code worked for me.

        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)

       {        }

Inspired by the article and source from here Thread Apartment Safe Open/Save File Dialogs for C#


扩展阅读
相关阅读
© CopyRight 2010-2021, PREDREAM.ORG, Inc.All Rights Reserved. 京ICP备13045924号-1