1

問題: 非同期で取得した受信データをレンダリングしようとすると、クラッシュします。

アプリが起動し、XAML を使用していくつかのダイアログ ボックスが表示されます。ユーザーがデータを入力してログイン ボタンをクリックすると、XAML クラスには、(IXMLHTTPRequest2 を使用して非同期的に) HTTP 処理を行うワーカー クラスのインスタンスが含まれます。アプリが Web サーバーに正常にログインすると、.then() ブロックが起動し、メインの xaml クラスへのコールバックを作成してアセットのレンダリングを行います。

ただし、デリゲート (メインの XAML クラス) で常にクラッシュが発生するため、このアプローチ (純粋な仮想クラスとコールバック) を使用して UI を更新することはできないと思います。非同期呼び出しの副産物である不適切なスレッドから、不注意に違法なことをしようとしていると思います。

メインの XAML クラスに、UI を更新する時期であることを通知するためのより良い方法または別の方法はありますか? 私は NotificationCenter を使用できる iOS の世界から来ています。

ここで、マイクロソフトには独自のデリゲート タイプのものがあることがわかりました。

独自のコールバックの代わりにこのアプローチを使用すると、クラッシュしなくなると思いますか?

さらに明確にする必要があるかどうかを教えてください。

コードの概要は次のとおりです。 public interface class ISmileServiceEvents { public: // 必要なメソッド virtual void UpdateUI(bool isValid) abstract; };

// In main XAML.cpp which inherits from an ISmileServiceEvents
void buttonClick(...){
    _myUser->LoginAndGetAssets(txtEmail->Text, txtPass->Password);
}
void UpdateUI(String^ data) // implements ISmileServiceEvents
{
    // This is where I would render my assets if I could.
    // Cannot legally do much here. Always crashes. 
    // Follow the rest of the code to get here. 
}

// In MyUser.cpp
void LoginAndGetAssets(String^ email, String^ password){
   Uri^ uri = ref new URI(MY_SERVER + "login.json");
   String^ inJSON = "some json input data here"; // serialized email and password with other data

   // make the HTTP request to login, then notify XAML that it has data to render.
   _myService->HTTPPostAsync(uri, json).then([](String^ outputJson){
      String^ assets = MyParser::Parse(outputJSON);
      // The Login has returned and we have our json output data 
      if(_delegate)
      {
         _delegate->UpdateUI(assets);
      }
   });
}

// In MyService.cpp
task<String^> MyService::HTTPPostAsync(Uri^ uri, String^ json)
{
    return _httpRequest.PostAsync(uri, 
        json->Data(),
        _cancellationTokenSource.get_token()).then([this](task<std::wstring> response)
    {
        try
        {
           if(_httpRequest.GetStatusCode() != 200) SM_LOG_WARNING("Status code=", _httpRequest.GetStatusCode());
               String^ j = ref new String(response.get().c_str());
               return j;
        }
        catch (Exception^ ex) .......;
    return ref new String(L"");
    }, task_continuation_context::use_current());
}

編集:ところで、UI を更新するときに発生するエラーは、「無効なパラメーターが致命的であると見なす関数に無効なパラメーターが渡されました」です。この場合、コールバックで実行しようとしているのは

txtBox->Text = data;
4

2 に答える 2

0

間違ったコンテキストから UI スレッドを更新しているようです。を使用task_continuation_context::use_arbitrary()して、UI を更新できるようにすることができます。このドキュメントの「実行スレッドの制御」の例を参照してください(マーシャリングの説明は下部にあります)。

于 2012-09-25T04:36:45.367 に答える
0

したがって、継続がある場合、ラムダ関数の後にコンテキストを指定しないと、デフォルトで use_arbitrary() になることがわかります。これは、私が MS ビデオで学んだことと矛盾しています。

ただし、GUI に関係するすべての .then ブロックに use_currrent() を追加すると、エラーがなくなり、すべてが適切にレンダリングされます。

私の GUI は、いくつかのタスクを生成するサービスを呼び出してから、非同期処理も行う HTTP クラスを呼び出します。HTTP クラスにさかのぼり、セカンダリ スレッドで実行できるように use_arbitrary() を使用します。これはうまくいきます。GUI に関係するものには必ず use_current() を使用してください。

元のコードを見ると、すでに use_current() が含まれていることがわかります。これは本当ですが、例を簡単にするためにラッピング関数を省略しました。そこで、use_current() を追加する必要がありました。

于 2012-09-25T19:50:54.530 に答える