5

現在、クロスプラットフォームのC ++ SDKに取り組んでおり、assertハンドラーをWinRTに移植する必要があります。プロセスの一部は、メッセージボックスを表示し、ユーザー入力を待ち、ユーザーが「デバッグ」を選択したときにブレークポイントをトリガーすることです。

すでにメッセージボックスが表示されていますが、現在の実行ポイントを離れにメッセージボックスが表示されるのを待つ方法が見つかりません。

これが私のこれまでのコードです。

// Create the message dialog factory

Microsoft::WRL::ComPtr<ABI::Windows::UI::Popups::IMessageDialogFactory> messageDialogFactory;
Microsoft::WRL::Wrappers::HStringReference messageDialogFactoryId(RuntimeClass_Windows_UI_Popups_MessageDialog);

Windows::Foundation::GetActivationFactory(messageDialogFactoryId.Get(), messageDialogFactory.GetAddressOf() );

// Setup the used strings

Microsoft::WRL::Wrappers::HString message;
Microsoft::WRL::Wrappers::HString title;
Microsoft::WRL::Wrappers::HString labelDebug;
Microsoft::WRL::Wrappers::HString labelIgnore;
Microsoft::WRL::Wrappers::HString labelExit;

message.Set( L"Test" );
title.Set( L"Assertion triggered" );
labelDebug.Set(L"Debug");
labelIgnore.Set(L"Ignore");
labelExit.Set(L"Exit");

// Create the dialog object

Microsoft::WRL::ComPtr<ABI::Windows::UI::Popups::IMessageDialog> messageDialog;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::UI::Popups::IUICommand*>> messageDialogCommands;

messageDialogFactory->CreateWithTitle( message.Get(), title.Get(), messageDialog.GetAddressOf() );
messageDialog->get_Commands(messageDialogCommands.GetAddressOf());

// Attach commands

Microsoft::WRL::ComPtr<ABI::Windows::UI::Popups::IUICommandFactory> commandFactory; 
Microsoft::WRL::Wrappers::HStringReference commandFactoryId(RuntimeClass_Windows_UI_Popups_UICommand);

Windows::Foundation::GetActivationFactory(commandFactoryId.Get(), commandFactory.GetAddressOf() );

CInvokeHandler commandListener;
commandFactory->CreateWithHandler(labelDebug.Get(), &commandListener, commandListener.m_DebugCmd.GetAddressOf() );
commandFactory->CreateWithHandler(labelIgnore.Get(), &commandListener, commandListener.m_IgnoreCmd.GetAddressOf() );
commandFactory->CreateWithHandler(labelExit.Get(), &commandListener, commandListener.m_ExitCmd.GetAddressOf() );

messageDialogCommands->Append( commandListener.m_DebugCmd.Get() );
messageDialogCommands->Append( commandListener.m_IgnoreCmd.Get() );
messageDialogCommands->Append( commandListener.m_ExitCmd.Get() );

// Show dialog

Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::UI::Popups::IUICommand*>> showOperation;
messageDialog->ShowAsync( showOperation.GetAddressOf() );

// ... and wait for the user to choose ...?

そして今、私はここで立ち往生しています。コールバックがトリガーされるのをスピン待機すると、無限ループに入り、メッセージボックスがまったく表示されません(少なくともUIスレッドから呼び出している場合)。実行を続けると、正しい位置でブレークポイントをトリガーする可能性が失われます。

したがって、私が探しているのは、再描画を強制するか、非同期呼び出しが終了するまで「ビジーウェイト」する方法です(たとえば、「await messadeDialog-> ShowAsync()」など)。Managed-C ++を使用できることはわかっていますが、避けたいと思います:)

4

2 に答える 2

2

ポップアップを表示するために呼び出すShowAsync()と、タスクはUIスレッドで実行されるようにスケジュールされます。このタスクを実行するには、UIスレッドが自由に実行できる必要があります(つまり、他のコードを実行することはできません)。コードがUIスレッドで実行されているときに呼び出すとShowAsync()、完了するまでブロックするShowAsync()と、アプリケーションがデッドロックします。ポップアップを表示するタスクは、コードがUIスレッドで実行を停止するまで待機する必要がありますが、コードは実行を停止しません。タスクが完了します。

イベントが発生するか、非同期操作が完了するまでUIスレッドを待機する場合は、UIスレッドをブロックしないように、キューをポンプする同期関数の1つを呼び出す必要があります。たとえば、非同期操作の同期を可能にするHiloプロジェクトのコードを見てください。

残念ながら、WindowsストアアプリのUIはアプリケーションシングルスレッドアパートメント(ASTA)で実行され、再入可能性が制限されるため、これでも役に立ちません。予期しないCOMの再入可能性が、最も恐ろしいバグの多くの原因であるため、これは良いことです。関数が待機している間に「ポップアップを表示」タスクを実行する方法はないと思います。

ただし、これがデバッグ専用の場合は、を呼び出すだけMessageBoxで通常のメッセージボックスを表示できます。デスクトップに表示されますが、プログラムは呼び出しが完了するのを確実に待ってから実行を続行します。アプリは、呼び出しを行ってもストア認定に合格しませんがMessageBox、デバッグコードの場合は、正常に機能するはずです。

の宣言はMessageBox#ifdefWindowsストアアプリをビルドするときにデフォルトで出力されますが、関数は自分で宣言できます。これを行う方法を説明する記事「Metroスタイルアプリでの「printf」デバッグ」を作成しました。


最後に、簡単に説明します。Windowsランタイムには「マネージC++」はありません。C ++ / CX言語拡張機能は、構文的には.NETFrameworkとCLIを対象とするC++ / CLIに似ていますが、意味的には異なります。C ++ / CXを使用する場合、マネージコードはまったくなく、CLRは実行時に読み込まれません。コンパイラは、C ++ /CXコードを同等のC++コードに変換してから、そのコードをコンパイルします。それはすべて100%ネイティブです。

于 2012-11-09T18:32:51.387 に答える
0

私が最終的に行ったことの簡単なフォローアップ(Jamesの回答に感謝します)。

UIスレッドからアサーションがトリガーされた場合(およびアプリがSTAで実行された場合)、メッセージを中断してデバッグアウトに入れます。メトロアプリケーションからデスクトップウィンドウをトリガーするのは間違っているように思えました。ただし、非UIスレッドからアサートがトリガーされた場合、「モーダル」ボックスは完全に機能します。

#include <ppltasks.h>
using namespace concurrency;

// ...

auto UIDispatcher = Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher;

try
{
    auto uiTask = UIDispatcher->RunAsync( CoreDispatcherPriority::Normal, 
        ref new DispatchedHandler( [&messagePopup, cmds, &result]() 
    {
        try
        {
            create_task(messagePopup->ShowAsync()).then([cmds, &result](IUICommand^ selected) {
                // result is changed depending on which command was selected
            });
        }
        catch (...)
        {
        }
    }));

    // Wait for the user to click

    create_task(uiTask).wait();

    // Sleep until result has been changed
}
catch ( invalid_operation )
{               
    // STA, debugout & break
}

// test on result, etc.

それが実際にこれを行うための最良の方法であるかどうかはわかりませんが、それは機能します:)

ShowAsyncをUIスレッドにディスパッチする必要があります。そうしないと、UI以外のスレッドから呼び出されたときにCOM例外がスローされます。

私は怠惰な^^そしてユーザーの相互作用をチェックするために最初のcreate_task()。then()を使用しました。
create_task(uiTask).wait()は、STAから呼び出されたときに無効な操作をスローします(したがって、MTAは問題なく機能すると思います)。
その場合、ディスパッチされたShowAsyncもCOM例外をスローすることによって失敗するため、何も表示されません。最後に、ボックスがトリガーされるのを待つのに忙しいだけです。

于 2012-12-19T09:07:44.737 に答える