分享

WPF Clipboard剪贴板缺陷解决方案

 kingjae 2014-09-23
 
异常信息“OpenClipboard HRESULT:0x800401D0 (CLIPBRD_E_CANT_OPEN
WPF本身对Clipboard处理的问题,在.Net4.0及之前的版本中都有问题,在.Net4.5中修复了,但XP下安装不了.NET4.5,而我的程序又必须在XP下运行。
问题主要是由于:在程序访问剪切板的时候,有其他程序正在占用剪切板,导致自己的程序无法访问,从而抛出异常。

  1. 自行截获异常,进行处理
         for (int i = 0; i < 10; i++)
         {
              try
              {
                   Clipboard.SetText(lineTexts[lineIndex]);
                   break;
              }
              catch
              {
                   System.Threading.Thread.Sleep(10);//这句加不加都没关系
              }
          }
        这种方法处理过程中UI会有一小段时间的假死。。。可以考虑多线程?

   2. 换一种方式设置剪切板
          Clipboard.SetDataObject(lineTexts[lineIndex]);
        就这一句。。。这种方法不会抛异常,UI也没有假死,非常正常!估计SetDataObject方法跟SetText方法的实现不一样,没有细究。。。

   3. 跟方法1类似,不过有点高级
剪切板处理的那句代码不变,还是使用SetText方法。
    在App.xaml文件中添加下面代码中红色的部分

             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml"
             DispatcherUnhandledException="Application_DispatcherUnhandledException">
   
        
   

          在App.xaml.cs文件中添加代码:
void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
      var comException = e.Exception as System.Runtime.InteropServices.COMException;
      if (comException != null && comException.ErrorCode == -2147221040)///OpenClipboard HRESULT:0x800401D0 (CLIPBRD_E_CANT_OPEN))
          e.Handled = true;
}
4.使用winform剪贴板
 System.Windows.Forms.Clipboard.SetText(txt2.Text); 
看一下WinForm和WPF的代码
WinForm的SetText()最终会调用SetDataObject(data, copy, 10, 100),这个是SetDataObject()的签名:
[UIPermission(SecurityAction.Demand, Clipboard=UIPermissionClipboard.OwnClipboard)]
public static void SetDataObject(object data, bool copy, int retryTimes, int retryDelay)
{
    if (Application.OleRequired() != ApartmentState.STA)
    {
        throw new ThreadStateException(SR.GetString("ThreadMustBeSTA"));
    }
    if (data == null)
    {
        throw new ArgumentNullException("data");
    }
    if (retryTimes < 0)
    {
        object[] args = new object[] { "retryTimes", retryTimes.ToString(CultureInfo.CurrentCulture), 0.ToString(CultureInfo.CurrentCulture) };
        throw new ArgumentOutOfRangeException("retryTimes", SR.GetString("InvalidLowBoundArgumentEx", args));
    }
    if (retryDelay < 0)
    {
        object[] objArray2 = new object[] { "retryDelay", retryDelay.ToString(CultureInfo.CurrentCulture), 0.ToString(CultureInfo.CurrentCulture) };
        throw new ArgumentOutOfRangeException("retryDelay", SR.GetString("InvalidLowBoundArgumentEx", objArray2));
    }
    DataObject obj2 = null;
    if (!(data is IDataObject))
    {
        obj2 = new DataObject(data);
    }
    bool flag = false;
    try
    {
        IntSecurity.ClipboardRead.Demand();
    }
    catch (SecurityException)
    {
        flag = true;
    }
    if (flag)
    {
        if (obj2 == null)
        {
            obj2 = data as DataObject;
        }
        if (!IsFormatValid(obj2))
        {
            throw new SecurityException(SR.GetString("ClipboardSecurityException"));
        }
    }
    if (obj2 != null)
    {
        obj2.RestrictedFormats = flag;
    }
    int num2 = retryTimes;
    IntSecurity.UnmanagedCode.Assert();
    try
    {
        int num;
        do
        {
            if (data is IDataObject)
            {
                num = UnsafeNativeMethods.OleSetClipboard((IDataObject) data);
            }
            else
            {
                num = UnsafeNativeMethods.OleSetClipboard(obj2);
            }
            if (num != 0)
            {
                if (num2 == 0)
                {
                    ThrowIfFailed(num);
                }
                num2--;
                Thread.Sleep(retryDelay);
            }
        }
        while (num != 0);
        if (copy)
        {
            num2 = retryTimes;
            do
            {
                num = UnsafeNativeMethods.OleFlushClipboard();
                if (num != 0)
                {
                    if (num2 == 0)
                    {
                        ThrowIfFailed(num);
                    }
                    num2--;
                    Thread.Sleep(retryDelay);
                }
            }
            while (num != 0);
        }
    }
    finally
    {
        CodeAccessPermission.RevertAssert();
    }
}
 
 而WPF的SetText(),虽然签名一样,但是具有完全不同的实现:
[SecurityCritical]
public static void SetDataObject(object data, bool copy)
{
    SecurityHelper.DemandAllClipboardPermission();
    CriticalSetDataObject(data, copy);
}
[FriendAccessAllowed, SecurityCritical]
internal static void CriticalSetDataObject(object data, bool copy)
{
    IDataObject obj2;
    if (data == null)
    {
        throw new ArgumentNullException("data");
    }
    if (data is DataObject)
    {
        obj2 = (DataObject) data;
    }
    else if (data is IDataObject)
    {
        SecurityHelper.DemandUnmanagedCode();
        obj2 = (IDataObject) data;
    }
    else
    {
        obj2 = new DataObject(data);
    }
    int num2 = 10;
    while (true)
    {
        int hr = OleServicesContext.CurrentOleServicesContext.OleSetClipboard(obj2);
        if (NativeMethods.Succeeded(hr))
        {
            break;
        }
        if (--num2 == 0)
        {
            Marshal.ThrowExceptionForHR(hr);
        }
        Thread.Sleep(100);
    }
    if (copy)
    {
        Thread.Sleep(10);
        Flush();
    }
}

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多