c#中监控剪贴板的两种方法

By Anran on 四月 3rd, 2010

最近在做一个云剪贴板的程序,需要用一个c#桌面应用程序来监控剪贴板。本来想的是,新建一个线程用while循环检查剪贴板内容的改动,不过发现不成功,如果用GetText()访问剪贴板则不管剪贴板内有没有文字都返回空字符串,用SetText()设置剪贴板则出错:“在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。”

所以我google了一下,发现貌似监视剪贴板可以不用“幕后线程”去不断检查,有专门的API来进行监视,并通过触发事件来告诉我们。


方法一:

首先要using一个System.Runtime.InteropServices.

然后写下下面的声明:

       [DllImport("User32.dll", CharSet = CharSet.Auto)]
       public static extern IntPtr SetClipboardViewer(IntPtr hWnd);

       [DllImport("User32.dll", CharSet = CharSet.Auto)]
       public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
       IntPtr ClipboardViewerNext;
       private void RegisterClipboardViewer()
       {
           ClipboardViewerNext = SetClipboardViewer(this.Handle);
       }

       private void UnregisterClipboardViewer()
       {
           ChangeClipboardChain(this.Handle, ClipboardViewerNext);
       }

然后在Form_Load中进行RegisterClipboardViewer:

        private void Form1_Load(object sender, EventArgs e)
        {
            RegisterClipboardViewer();
        }

然后重写Form.WndProc方法:

        protected override void WndProc(ref Message m)
        {
            switch ((int)m.Msg)
            {
                case 0x308: //WM_DRAWCLIPBOARD
                    {
                        //Do something here..
                        break;
                    }
                default:
                    {
                        base.WndProc(ref m);
                        break;
                    }
            }
        }

然后就OK了。

但是我对这种API调用很不爽,看着十分别扭,而且不google一下的话不可能知道。难道就真的不能用“幕后线程”去监控剪贴板么?

方法二:

答案当然是否定的。经过我的探究,既然访问ClipBoard需要单线程模式,那咱就设一下:

            System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(func));
            th.SetApartmentState(System.Threading.ApartmentState.STA);
            th.Start();

然后在func()方法里写上个while(true)的循环去监视就没有之前的问题了。

标签:
5 Comments

Posted By: kyle AUSTRALIA Mozilla Firefox Windows On: 九月 28, 2010 At: 10:30 上午

你好,按照你的方法做了测试,代码如下,但是出错,不知道什么问题? 麻烦帮忙看看。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Net.Mail;
using System.Runtime.InteropServices;

namespace email
{
///
/// Interaction logic for MainWindow.xaml
///

public partial class MainWindow : Window
{
string textInClipboard = null;
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
try
{ //using System.Net.Mail; based on .NET 3.5
SmtpClient smtp = new SmtpClient();
smtp.Host = "mail.agriview.com.au";
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.Credentials = new System.Net.NetworkCredential("kyle.l@agriview.com.au", "joyce");
MailMessage mm = new MailMessage("kyle.l@agriview.com.au", "kyle30542@gmail.com");
mm.Body = "this is the body of email";
mm.Subject = "mail subject";
smtp.Send(mm);
}
catch
{
throw;
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(viewClipboard));
th.SetApartmentState(System.Threading.ApartmentState.STA);
th.Start();
textInClipboard = Clipboard.GetText();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
//轮询,改变则弹出
if (!Clipboard.GetText().Equals(textInClipboard))
{
MessageBox.Show(Clipboard.GetText() + "" + textInClipboard);
textInClipboard = Clipboard.GetText();
}

}
public void viewClipboard()
{

while (true)
{
try
{
string currentTxt = Clipboard.GetText();
if (currentTxt != textInClipboard)
{
//MessageBox.Show(currentTxt);
button2.Content = currentTxt;
textInClipboard = currentTxt;
}
else
{
//MessageBox.Show(Clipboard.GetText());
}
}
catch
{ }

}
}

}
}

Posted By: Anran CHINA Internet Explorer Windows On: 九月 28, 2010 At: 11:27 上午

等我回家给你试试的。。请问错误信息是什么呢?

Posted By: kyle AUSTRALIA Mozilla Firefox Windows On: 九月 28, 2010 At: 2:31 下午

多谢你这么快的回复。

提示 “由于其他线程拥有此对象,因此调用线程无法对其进行访问”

能否给出完整的例子,谢谢。

第一种方法也好像不行的。提示找不到this.handle方法。

Posted By: Anran CHINA Internet Explorer Windows On: 九月 28, 2010 At: 5:11 下午

这是个wpf程序吧。。我只装了vs2008所以没法调试wpf,不过我认为是因为安全原因所以不允许wpf执行win32 api,也就不能访问剪贴板。google到的用threading.dispatcher的invoke试试。。

Posted By: kyle AUSTRALIA Mozilla Firefox Windows On: 九月 30, 2010 At: 8:02 上午

多谢回复。回头我自己再试试

Leave a reply