1

这个问题相关,我正在尝试实现一个使用 WinRT API 设置桌面壁纸的过程。为了模仿awaitC# 中的功能,我使用了此处此处概述的TTask.Future链接) 。

我的实现如下所示:

class procedure TUtilityWin.SetWallpaper(AFileName: String);
var
  lStorageFile: IStorageFile;
  liao_storagefile: IAsyncOperation_1__IStorageFile;
  lFutureTask: IFuture<IAsyncOperation_1__IStorageFile>;
begin
  //WinRT Implementation
  if TUserProfile_UserProfilePersonalizationSettings.IsSupported then
  begin
    lFutureTask:=TTask.Future<IAsyncOperation_1__IStorageFile>(
                          function: IAsyncOperation_1__IStorageFile
                          begin
                            Result:=TStorageFile.GetFileFromPathAsync(HSTRING(AFileName));
                          end);
    liao_storagefile:=lFutureTask.Value;
    lStorageFile:=liao_storagefile.GetResults;
    TUserProfile_UserProfilePersonalizationSettings.Current.TrySetWallpaperImageAsync(lStorageFile);
  end;
end;

据我了解,当我尝试获取lFutureTask.Value时,应用程序会暂停当前线程,直到 lFutureTask 完成(如果尚未完成),然后提供该值。但是,当我运行应用程序时,我收到错误消息:EOleException with message 'A method was called at an unexpected time'. 休息在这一行:lStorageFile:=liao_storagefile.GetResults;

我是 TTask 和 WinRT API 的新手——所以我确定我在这里遗漏了一些非常基本的东西。希望能提供任何有关导致此问题的原因或我可以采取不同措施来解决此问题的任何指示。提前致谢。

4

2 回答 2

0

我查看了您问题中链接的 Delphi 文档,以及 AFAICT 两者ITask,并且IFuture仅表示在单独线程上执行的方法(我称之为“委托任务”)。似乎不支持异步任务(我称之为“承诺任务”)。IAsyncOperation<T>是代表异步任务的 WinRT 类型,因此您遇到的问题。特别是,似乎没有任何 Delphi 支持将延续注册到他们的“任务”上。

因此,除非 Delphi 支持非线程Future/Promise ,否则您将不得不阻塞线程。

目前,您的代码正在启动一个仅启动异步操作 ( GetFileFromPathAsync) 的线程任务。线程任务等待异步操作IAsyncOperation<T>.Completed完成GetResult

因此,要解决此问题,您需要一种方法来阻止线程任务,直到异步操作完成。由于 WinRT 类型是纯异步的(不支持同步),并且 Delphi 类型是纯同步的(不支持异步),因此您必须自己桥接它。最好的解决方案可能是与ManualResetEvent 等效的Delphi。

像这样的东西应该工作:

class procedure TUtilityWin.SetWallpaper(AFileName: String);
var
  lStorageFile: IStorageFile;
  lFutureTask: IFuture<IStorageFile>;
begin
  if TUserProfile_UserProfilePersonalizationSettings.IsSupported then
  begin
    lFutureTask:=TTask.Future<IStorageFile>(
        function: IStorageFile
        var
          liao_storagefile: IAsyncOperation_1__IStorageFile;
          mre: ManualResetEvent;
          result: IStorageFile;
        begin
          mre:= // Create ManualResetEvent
          liao_storagefile:=TStorageFile.GetFileFromPathAsync(HSTRING(AFileName));
          liao_storagefile.Completed... // Add handler that will set `result` and then set the ManualResetEvent
          mre.Wait(); // Wait for the ManualResetEvent to be set
          Result:=result;
        end);
    liao_storagefile:=lFutureTask.Value;
    lStorageFile:=liao_storagefile.GetResults;
    TUserProfile_UserProfilePersonalizationSettings.Current.TrySetWallpaperImageAsync(lStorageFile);
  end;
end;

抱歉,我不了解 Delphi,但希望这会给你一个大致的方向。

于 2017-08-11T14:18:26.177 回答
0

以下(处理对 的异步调用WebAuthenticationCoreManager.FindAccountProviderAsync)对我有用,尽管它与async/await.Net 中的比较丑陋:

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
  Winapi.Winrt,
  System.Win.WinRT,
  Winapi.Security.Credentials,
  Winapi.Security,
  Winapi.Foundation.Types

...

type
  TCompleteOperationCompleted_IWebAccountProvider = reference to procedure (Value : IWebAccountProvider);
  TCompleteOperationError_IWebAccountProvider   = reference to procedure ();

  TAsyncOperationCompletedHandler_1__IWebAccountProvider = class (TInterfacedObject,
                                                                  AsyncOperationCompletedHandler_1__IWebAccountProvider_Delegate_Base,
                                                                  AsyncOperationCompletedHandler_1__IWebAccountProvider)
  private
    fOnCompleted : TCompleteOperationCompleted_IWebAccountProvider;
    fOnError : TCompleteOperationError_IWebAccountProvider;
  protected
    procedure Invoke(asyncInfo: IAsyncOperation_1__IWebAccountProvider; asyncStatus: AsyncStatus); safecall;
  public
    constructor Create(OnCompleted : TCompleteOperationCompleted_IWebAccountProvider;
                       OnError : TCompleteOperationError_IWebAccountProvider = nil);
  end;

constructor TAsyncOperationCompletedHandler_1__IWebAccountProvider.Create(
  OnCompleted: TCompleteOperationCompleted_IWebAccountProvider;
  OnError: TCompleteOperationError_IWebAccountProvider);
begin
  fOnCompleted := OnCompleted;
  fOnError   := OnError;
end;

procedure TAsyncOperationCompletedHandler_1__IWebAccountProvider.Invoke(
  asyncInfo: IAsyncOperation_1__IWebAccountProvider; asyncStatus: AsyncStatus);
begin
  case asyncStatus of
    Winapi.Foundation.Types.AsyncStatus.Completed : if Assigned(fOnCompleted) then fOnCompleted(asyncInfo.GetResults());
    Winapi.Foundation.Types.AsyncStatus.Error : if Assigned(fOnError) then fOnError();
    else ;//todo
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
const
  DefaultProviderId = 'https://login.windows.local';
  MicrosoftProviderId = 'https://login.microsoft.com';
var
  account: IAsyncOperation_1__IWebAccountProvider;
  webAccount: IWebAccountProvider;
begin
  account := TAuthentication_Web_Core_WebAuthenticationCoreManager.FindAccountProviderAsync(TWindowsString.Create(MicrosoftProviderId{DefaultProviderId}));

  account.Completed := TAsyncOperationCompletedHandler_1__IWebAccountProvider.Create(
                       procedure(Value : IWebAccountProvider)
                       begin
                         ShowMessage('Async operation completed');
                         //todo
                       end,
                       nil
                       );
end;
于 2019-10-02T20:08:03.257 回答