『 2017年03月 』

≒ 【未解決】UWP(C++/CX)アプリでのダブルタップイベント不具合

結局解決できなかったので後日改めてのための覚え書き。ヘッダファイルとか諸々は省略&コード内容も大分簡略化。
(一応動くように出来たので最後に追記しました。でもおまじないみたいな実装で、とても「解決」と言えるレベルのものではないのでタイトルはそのままw)
[code language=”cpp”]
void MyApp::MainPage::OnDoubleTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs^ e){
e->Handled = true;

// Windows::Foundation::Collections::IVector<MyClass^>^ classesがあって、それを操作する処理
//各MyClassはMainPage.xaml内でGridやその中のRelativePanel、TextBlockとx:Bindで繋げてある。
change_classes();

Update_bindings();
}

void MyApp::MainPage::Update_bindings()
{
try
{
if (this != nullptr &&this->Bindings != nullptr) {
this->Bindings->Update();
}

}
catch (Platform::Exception^ e)
{
OutPrintln(e->Message, true);
}
}

void MyApp::MainPage::OutPrintln(Platform::String ^ s, Platform::Boolean flg)
{
Platform::String^ tmp = s;
if (flg) {
tmp = tmp + L"\r\n";
}
OutputDebugString(tmp->Data());
}
[/code]
で、ダブルタップすると落ちる。
「ハンドルされない例外が 0x023CE76B (Windows.UI.Xaml.dll) で発生しました(MyApp.exe 内): 0xC000041D: ユーザー コールバック中に未処理の例外が発生しました。。 」
「。」が二つあるのも気になるけど、とにかく落ちる。
メッセージが「例外がスローされました:読み取りアクセス違反。**this** が nullptr でした。 が発生しました」って時もあって、UWPが未だに良く分かってない俺にはどうしようもない。
IVectorへのアクセスミスかなと思ってそこ見直しても違いは無い。何が原因か突き止めるのに一日。
[code language=”cpp”]
void MyApp::MainPage::OnDoubleTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs^ e){
e->Handled = true;
change_classes();

//Update_bindings();
}
[/code]
コメントアウトすると大丈夫。更に変だと思ったので
[code language=”cpp”]
void MyApp::MainPage::OnDoubleTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs^ e){
e->Handled = true;
change_classes();

OutPrintln("AAA", true);
Update_bindings();
OutPrintln("CCC", true);
}

void MyApp::MainPage::Update_bindings()
{
OutPrintln("BBB", true);
try
{
if (this != nullptr &&this->Bindings != nullptr) {
this->Bindings->Update();
}

}
catch (Platform::Exception^ e)
{
OutPrintln(e->Message, true);
}
}
[/code]
として走らせると、
[code language=”bash”]
AAA
BBB
CCC
[/code]
と出力してから落ちる。
ダブルタップで発生する処理は、ドラッグ&ドロップでも発生する処理なので、少し手直ししてメソッドを共通化してもダブルタップだけ落ちる。
そもそも落ちるときにソースの表示がされないで「automaticdraghelper.cppが見つかりません」と出る。なお「automaticdraghelper」でググると珍しく「automaticdraghelper に一致する情報は見つかりませんでした。」とでる。ほんとにUWPは情報少ない。
「呼び出し履歴」ってウィンドウを見ると全然俺のコードとは関係ないところで落ちてるようだし、今回はあきらめてこのタップイベント拾うのやめるか(他の部分でのダブルタップ実装は何の問題もない)と考えて、コードを削る前最後に単純にOnDoubleTappedのまま(DoubleTappedRoutedEventArgsのまま)、シングルタップイベントに付け替えたらちゃんと動く。あきれたーw 要はUWPのダブルタップイベントの不具合は俺には突き止められないことが今回は分かった。
その後はシングルタップイベントハンドラを工夫して「疑似ダブルタップ」みたいなこともやろうとして、Windows::UI::Xaml::DispatcherTimerとか仕込んで、いざ動作確認!って走らせたらなんか変。すごく長い間隔の2回タップは当然「2回のシングルタップ」として認識して、ちょっと早めの(いわゆるダブルタップより微妙に長い間隔の)2回タップを「ダブルタップ」として認識する。そしてすごく早い2回タップ(いわゆるダブルタップ)は全部「1回のシングルタップ」になっちゃう。何で? 要するにシングルタップイベントの発生にはインターバルが必要なのか。なるほどねー。というオチも付いたということで、今回は以上。

[20時追記]
MSのフォーラムに情報あり。UWPアプリにはダブルタップで処理が2回走ってエラーになる場合があるらしい。この例とは違うんだけど、同じダブルタップということで処理の途中にインターバル入れてみる。
[code language=”cpp”]
//我ながらページのコンストラクタでタイマーの設定やるのもどうかと思う。定石はOnNavigatedToでやるみたい。
MainPage::MainPage()
{
InitializeComponent();
mTimer = ref new Windows::UI::Xaml::DispatcherTimer();
TimeSpan span;
span.Duration = (1LL * 1000000000) / 1000;//1/10秒
mTimer->Interval = span;
mTimer->Tick += ref new Windows::Foundation::EventHandler<Platform::Object ^>(this, &MyApp::MainPage::mTimer_OnTick);
}

void MyApp::MainPage::OnDoubleTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs^ e){
e->Handled = true;
mTimer->Start();
}

void MyApp::MainPage::mTimer_OnTick(Platform::Object ^sender, Platform::Object ^args)
{
mTimer->Stop();
change_classes();
Update_bindings();
}
[/code]
ダブルタップされてから1/10秒後に処理をするようにしただけ。これで落ちなくなった。なんか馬鹿々々しいな。今度こそ以上。