如何将“IStorageItem”的实现传递给 DataPackage.SetStorageItems(items) 并且不会在 UWP 上引发 InvalidCastException?

Posted

技术标签:

【中文标题】如何将“IStorageItem”的实现传递给 DataPackage.SetStorageItems(items) 并且不会在 UWP 上引发 InvalidCastException?【英文标题】:How can I pass an implementation of "IStorageItem" to DataPackage.SetStorageItems(items) and don't get an InvalidCastException raised on UWP? 【发布时间】:2019-07-01 09:06:14 【问题描述】:

我正在开发一个能够共享其文件的 UWP 应用程序。我关注了the documentation from Microsoft,解决方案效果很好。

这是我的实现:

public void ShareLocalFile(LocalFileToShare file)

    DataTransferManager.GetForCurrentView().DataRequested += async (sender, args) =>
    
        var deferral = args.Request.GetDeferral();

        try
        
            var storageFile = await StorageFile.GetFileFromPathAsync(file.FilePath).AsTask();

            args.Request.Data.SetStorageItems(new[]  storageFile );
        
        finally
        
            deferral.Complete();
        
    ;

    DataTransferManager.ShowShareUI();

但是,该应用程序会存储所有名称不可读的文件,这使用户对共享感到不安。 因此,我想共享一个具有替代名称的文件,而不是在文件系统上实际重命名它,因为文件是在第三方阅读器中打开的。此外,文件很大,用新名称复制它们不是一个好的选择。

首先我认为我可以建立一个符号链接,但it's only possible with Administrator rights

然后我查看了the signature of "void DataPackage.SetStorageItems(IEnumerable value)" method 并猜测可能会传递我自己的IStorageItem 实现我做了什么:

public class StorageItemWithAlternativeName : IStorageItem

    private readonly IStorageItem storageItem;

    public StorageItemWithAlternativeName(IStorageItem storageItem, string alternativeItemName)
    
        this.storageItem = storageItem;
        Name = alternativeItemName;
    

    public string Name  get; 

    // the interface implementation omitted for briefness but it simply delegates all actions to the decorated storageItem


public static class LocalFileToShareExtensions

    public static async Task<IStorageItem> GetStorageItem(this LocalFileToShare file)
    
        var storageFile = await StorageFile.GetFileFromPathAsync(file.FilePath).AsTask();

        if (!string.IsNullOrWhiteSpace(file.AlternativeFileName))
        
            storageFile = new StorageItemWithAlternativeName(storageFile, file.AlternativeFileName);
        

        return storageFile;
    

在这里我失败了。错误非常愚蠢 - SetStorageItems 方法抛出 InvalidCastException: “不支持这样的接口。 该集合包含无法转换为只读形式的项目。"

我调查了windows事件日志,发现如下条目:

Faulting application name: [MyApp].Windows.exe, version: 7.0.0.0, time stamp:    0x5bb69bfe
Faulting module name: combase.dll, version: 10.0.17763.253, time stamp: 0xa3f81b2d
Exception code: 0xc000027b
Fault offset: 0x00209931
Faulting process id: 0x4ee4
Faulting application start time: 0x01d4be3ccca1f00f
Faulting application path: [PathToMyApp].Windows.exe
Faulting module path: C:\WINDOWS\System32\combase.dll
Report Id: 35999df1-6b4f-4675-a821-a84e6ea0cfb4
Faulting package full name: [MyAppPackageName]
Faulting package-relative application ID: App

DataPackage 对象似乎与 COM 通信,所以我也在我的程序集上尝试了[assembly: [ComVisible(true)] 属性,但我没有成功。

问题是我怎样才能使系统变笨并共享一个不同名称的文件? 是否可以将我自己的实现传递给 UWP SDK 方法?因为现在它违反了the Liskov substitution principle。

我将不胜感激!

【问题讨论】:

IStorageItem 存在,因此 StorageFolder 和 StorageFile 都可以实现它。但是你不能在你的自定义类中实现它。你想要的是 StorageFile.CreateStreamedFileAsync。这使您可以创建虚拟文件。 【参考方案1】:

请尝试使用带有参数readOnly:false的重载方法SetStorageItems,如下所示:

args.Request.Data.SetStorageItems(new[]  storageFile , false);

【讨论】:

谢谢@Alexander!它使用了一个小技巧。我还必须实现来自StorageFile 的所有接口:class StorageFileWithAlternativeName : IStorageFile, IStorageItemProperties2, IStorageItem2, IStorageItemPropertiesWithProvider, IStorageFilePropertiesWithAvailability, IStorageFile2 我想其中一些是不必要的,但如果只实现IStorageItem 接口,则无法将文件附加到MailClient 中的电子邮件。 P.S. Skype 使用这种方法会崩溃,尽管我可以接受,因为我只需要 MailClient 正常工作。 酷,@GennadiySakhnyuk,!

以上是关于如何将“IStorageItem”的实现传递给 DataPackage.SetStorageItems(items) 并且不会在 UWP 上引发 InvalidCastException?的主要内容,如果未能解决你的问题,请参考以下文章

$attrs和$listeners

如何将两个变量传递给 python 函数并返回两个变量? [复制]

如何将环境变量传递给传递给xmllint的命令?

如何将对象列表传递给函数,该函数需要实现接口的对象列表?

如何将消息传递给websocket?

如何将数组传递给 IN() 子句? [复制]