zoukankan      html  css  js  c++  java
  • 终端服务的剪贴板的缺陷,导致WPF调用Clipboard.SetText() 失败

    这是一个在实际项目中遇到的问题,在VPN和远程桌面中,WPF程序对系统剪贴板进行操作的时候,发生CLIPBRD_E_CANT_OPEN异常。从异常本身来看,很明显,是COM有问题。



    代码很简单 Clipboard.SetText(mSelection); 但是注意,这个是WPF的窗口,所以调用的是 System.Windows.Clipboard,而不是WinForm的System.Windows.Forms.Clipboard。


    经过一番搜索,找到了根源: 

    http://stackoverflow.com/questions/68666/clipbrd-e-cant-open-error-when-setting-the-clipboard-from-net

    http://blogs.microsoft.co.il/blogs/tamir/archive/2007/10/24/clipboard-setdata-getdata-troubles-with-vpc-and-ts.aspx

    原因是微软的Terminal Service 的Clipboard有一个bug,解决方法就是用try-catch包一下函数调用,然后多调用几次,每次之后呢,Sleep一点时间片,代码看上去就是:

    for (int i = 0; i < 10; i++)
    {
        try
        {
            Clipboard.SetText(str);
            return;
        }
        catch { }
        System.Threading.Thread.Sleep(10);
    }

    而有人也指出,在.Net 2.0 SP1的时候,微软对Winform的剪切板做了修正,内部就做了这个处理,但是WPF没有!好,那就来看看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();
        }
    }



  • 相关阅读:
    《WF编程》系列之 承载工作流:跟踪服务 Tracking Service
    C#中隐藏(new)和方法重写(override)和重载(overload)的区别
    一套完整自定义工作流的实现
    工作流规范
    新宇面试题
    c# 多态性
    SQL Join连接详解
    Minix安装及配置指南(转载)
    游戏开发的学习资料汇总
    error LNK2019: 无法解析的外部符号 __vsnprintf 问题的解决方法
  • 原文地址:https://www.cnblogs.com/puncha/p/3876974.html
Copyright © 2011-2022 走看看