【问题标题】:Making file picker asynchronous - Windows Phone 8.1使文件选择器异步 - Windows Phone 8.1
【发布时间】:2014-09-12 00:59:53
【问题描述】:

我尝试使用 TaskComplectionSource 使文件打开选择器异步,但有时我会以 -1 返回值关闭我的应用程序,有时我会遇到如下异常:

[System.Runtime.InteropServices.COMException] = {System.Runtime.InteropServices.COMException (0x80004005): Unspecified error

Unspecified error

   at Windows.Storage.Pickers.FileOpenPicker.PickSingleFileAndContinue()
   at PhotosGraphos.Mobile.Common.StorageFileExtensions.<PickSingleFileAsyncMobile..

代码:

public static class StorageFileExtensions
{
    private static TaskCompletionSource<StorageFile> PickFileTaskCompletionSource;

    private static bool isPickingFileInProgress;
    public static async Task<StorageFile> PickSingleFileAsyncMobile(this FileOpenPicker openPicker)
    {
        if (isPickingFileInProgress)
            return null;

        isPickingFileInProgress = true;
        PickFileTaskCompletionSource = new TaskCompletionSource<StorageFile>();

        var currentView = CoreApplication.GetCurrentView();
        currentView.Activated += OnActivated;
        openPicker.PickSingleFileAndContinue();

        StorageFile pickedFile;
        try
        {
            pickedFile = await PickFileTaskCompletionSource.Task;
        }
        catch (TaskCanceledException)
        {
            pickedFile = null;
        }
        finally
        {
            PickFileTaskCompletionSource = null;
            isPickingFileInProgress = false;
        }

        return pickedFile;
    }

    private static void OnActivated(CoreApplicationView sender, IActivatedEventArgs args)
    {
        var continuationArgs = args as FileOpenPickerContinuationEventArgs;
        sender.Activated -= OnActivated;

        if (continuationArgs != null && continuationArgs.Files.Any())
        {
            StorageFile pickedFile = continuationArgs.Files.First();
            PickFileTaskCompletionSource.SetResult(pickedFile);
        }
        else
        {
            PickFileTaskCompletionSource.SetCanceled();
        }
    }
}

奇怪的是——这个错误在调试时几乎不会重现。有谁知道这可能是什么原因?

【问题讨论】:

  • 对于初学者来说,您不应该在每次调用此方法时都使用静态任务完成源。它应该是方法调用的本地。
  • 你是对的 - 这显然是“代码气味”。但是,我在我的问题中提到过它没有任何问题 - 它没有在此方法之外的任何地方使用,它不是多线程方法(无法从非 ui 线程启动选择器),因此这里不存在竞争条件.

标签: c# windows-runtime windows-phone-8.1


【解决方案1】:

不要那样做(不要试图将 Continuation 行为转变为 async)。为什么?

通常,当您的应用程序进入后台时(例如,当您调用文件选择器时),它会被暂停,这里有一个小缺陷 - 当您连接了调试器时,您的应用程序将在不暂停的情况下运行。这肯定会引起一些麻烦。

另请注意,当您正常运行您的应用并触发选择器时,在某些情况下您的应用可能会被终止(资源不足,用户关闭它...)。因此,您需要 VS 作为模板添加的两件事:ContinuationManagerSuspensionManager。更多信息请访问MSDN。在同一个链接中,您将找到一个调试应用程序的好方法:

按照这些步骤来测试您的应用在调用 AndContinue 方法后终止的情况。这些步骤可确保调试器在完成操作并继续后重新连接到您的应用。

  1. 在 Visual Studio 中,右键单击您的项目并选择属性。

  2. 在项目设计器中,在“启动”操作下的“调试”选项卡上,启用“不启动,但在启动时调试我的代码”。

  3. 通过调试运行您的应用。这会部署应用程序,但不会运行它。

  4. 手动启动您的应用。调试器附加到应用程序。如果您的代码中有断点,调试器将在断点处停止。当您的应用调用 AndContinue 方法时,调试器会继续运行。

  5. 如果您的应用调用文件选择器,请等到您打开文件提供程序(例如,电话、照片或 OneDrive)。如果您的应用调用在线身份提供商,请等待身份验证页面打开。

  6. 在“调试位置”工具栏的“进程”下拉列表中,为您的应用选择进程。在 Lifecycle Events 下拉列表中,选择 Suspend and Shutdown 以终止您的应用,但让模拟器继续运行。

  7. AndContinue 操作完成后,调试器会在应用继续运行时自动重新附加到您的应用。

【讨论】:

  • 很好的答案,虽然已经知道 ContinuationManager 我认为它可以简化。当应用程序被暂停时,您知道等待的 TaskCompletionSource 到底发生了什么吗?
  • @fex 在大多数情况下,资源都保留在内存中:操作系统会尝试在内存中保留尽可能多的挂起应用程序 - 您可以在MSDN 阅读更多信息。我不确定程序是否会在 await TCS 之后返回该点 - 当然你可以测试它 - 在 Lifecycle Events 下拉列表 中选择 Suspend,然后 Resume - 你可以测试会发生什么。
【解决方案2】:

我已将文件选择器更改为 @Romasz 提供的标准方式 - 它仍然崩溃。我已经调试了几个小时,我得到了相同的COMException,但有时提供了信息:

"GetNavigationState doesn't support serialization of a parameter type which was passed to Frame.Navigate"

似乎带有TaskCompletionSource 的代码可以工作,这并没有错。我在 Frame 的 msdn 文档中发现了

Note: The serialization format used by these methods is for internal use only. Your app should not form any dependencies on it. Additionally, this format supports serialization only for basic types like string, char, numeric and GUID types.

我在导航参数中传递了我的模型类对象 - 所以它保存在导航堆栈中,因此无法序列化。教训是:不要使用非原始类型作为导航参数 - Frame.Navigate 应该禁止此类导航并抛出异常 - 但它不会..

编辑: 另一个错误 - 如果您将点击(假设点击按钮)或类似事件绑定到启动 FileOpenPicker 的命令,您需要检查之前是否调用过 picker.PickFile.. - 否则当您快速点击该按钮时,您会收到很少的电话到picker.PickFile..UnauthorizedAccessException 将被抛出。

【讨论】:

  • +1 - 只注意应用在后台被操作系统终止的情况(罕见但可能发生)。此外,我不会将 双击问题 称为错误,只是说明我们应该确保用户不会多次触发选择器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多