簡體   English   中英

如何使用本機C ++在WinRT中創建模態消息框

[英]How to create a modal messagebox in WinRT using native C++

我目前正在開發一個跨平台的C ++ SDK,我必須將斷言處理程序移植到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-Thread調用時)。 如果我繼續執行,我將失去在正確位置觸發斷點的可能性。

所以我正在尋找的是強制重繪或“忙等待”異步調用完成的某種方式(比如“await messadeDialog-> ShowAsync()”)。 我知道我可以使用托管C ++,但我想避免它:)

當您調用ShowAsync()以顯示彈出窗口時,該任務將安排在UI線程上執行。 為了運行此任務,UI線程必須可以自由執行它(即,它不能執行其他代碼)。 如果您的代碼在UI線程上執行並且您調用ShowAsync() ,那么您將阻塞直到ShowAsync()完成,您的應用程序將死鎖:顯示彈出窗口的任務必須等到您的代碼在UI線程上停止運行,但是在任務完成之前,代碼不會停止運行。

如果要在UI線程上等待事件發生或要完成異步操作,則需要調用其中一個同步函數來抽取隊列,這樣就不會阻止UI線程。 例如,查看Hilo項目中允許同步異步操作的代碼。

不幸的是,這仍然無法幫助您,因為Windows應用商店應用UI在Application Single-Threaded Apartment(ASTA)中運行 ,這限制了重入。 這是一件好事,因為意想不到的COM重入是導致許多最恐怖的可怕錯誤的原因。 我不認為有一種方法可以在你的函數等待時運行“show the popup”任務。

但是,如果這僅用於調試,則只需調用MessageBox即可顯示普通消息框。 它將顯示在桌面上,但您的程序肯定會在繼續執行之前等待調用完成。 您的應用程序不會通過調用MessageBox來傳遞商店認證,但同樣,對於調試代碼,它應該可以正常工作。

在構建Windows應用商店應用時, MessageBox的聲明默認為#ifdef ,但您可以自己聲明該功能。 我寫了一篇文章, “在Metro風格的應用程序中''printf'調試” ,解釋了如何做到這一點。


最后,快速澄清一下:Windows運行時沒有“托管C ++”。 C ++ / CX語言擴展在語法上類似於C ++ / CLI,它以.NET Framework和CLI為目標,但它們在語義上是不同的。 使用C ++ / CX時,根本沒有托管代碼,CLR將不會在運行時加載。 編譯器將C ++ / CX代碼轉換為等效的C ++代碼,然后編譯該代碼。 這都是100%原生的。

快速跟進我最后做的事情(感謝James的回答)。

如果從UI線程觸發斷言(並且應用程序在STA中運行),我只需打破並將消息放入調試中。 從Metro應用程序觸發桌面窗口我似乎不對。 如果從非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(),因為我懶得^^並檢查用戶交互。
當從STA調用時,create_task(uiTask).wait()將拋出無效操作(所以我猜MTA會正常工作)。
在這種情況下,調度的ShowAsync也會因拋出COM異常而失敗,因此不會顯示任何內容。 最后我只是忙着等待盒子被觸發。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM