How can I pass an implementation of "IStorageItem" to DataPackage.SetStorageItems(items) and don't get an InvalidCastException raised on UWP?

2

I'm developing a UWP application which shall be able to share its files. I followed the documentation from Microsoft and the solution worked pretty well.

Here is my implementation:

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

However, the app stores all files with not human-readable names which makes users be uneasy with the sharing. So, I wanted to share a file with an alternative name without actually renaming it on the file system because files are opened in a third-party reader. In addition, the files are quite big and copying them with new names is not a good choice.

Firstly I thought that I could make a symbolic link but it's only possible with Administrator rights

Then I looked at the signature of "void DataPackage.SetStorageItems(IEnumerable value)" method and guessed that it's probably possible to pass there my own implementation of IStorageItem what I did:

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

And here I failed. The error is quite silly - SetStorageItems method throws an InvalidCastException: "No such interface supported. The collection contains item(s) that can't be converted to read-only form."

I investigated the windows event log and found the following entry:

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

It seems that the DataPackage object communicates with COM so I also tried [assembly: [ComVisible(true)] attribute on my assembly but I did not succeed.

The question is how can I kind of dumb the system and share a file with a different name? Is it possible to pass my own implementations to UWP SDK methods? Because now it violates the Liskov substitution principle.

I will appreciate any help with this!

c#
.net
windows
uwp
xamarin.uwp

1 Answer

1

Please try to use the overloaded method SetStorageItems with parameter readOnly:false, something like this:

args.Request.Data.SetStorageItems(new[] { storageFile }, false);
answered on Stack Overflow Feb 7, 2019 by Alexander Lysenko

User contributions licensed under CC BY-SA 3.0