In Windows Store Apps, C++(C# is similar though), doing something like
IAsyncAction^ Action = CurrentAppSimulator::ReloadSimulatorAsync(proxyFile);
create_ta
In general you should use the continuations (.then(...)), like Adam's answer says, and not block. But lets say you want to do a wait for some reason (for testing some code?), you can trigger an event from the last continuation (to use C# parlance):
TEST_METHOD(AsyncOnThreadPoolUsingEvent)
{
std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();
int i;
auto workItem = ref new WorkItemHandler(
[_completed, &i](Windows::Foundation::IAsyncAction^ workItem)
{
Windows::Storage::StorageFolder^ _picturesLibrary = Windows::Storage::KnownFolders::PicturesLibrary;
Concurrency::task<Windows::Storage::StorageFile^> _getFileObjectTask(_picturesLibrary->GetFileAsync(L"art.bmp"));
auto _task2 = _getFileObjectTask.then([_completed, &i](Windows::Storage::StorageFile^ file)
{
i = 90210;
_completed->set();
});
});
auto asyncAction = ThreadPool::RunAsync(workItem);
_completed->wait();
int j = i;
}
By the way, for some reason this method causes an exception when after it is over in visual studio tests. I've tested it in an app too though and it worked with no problem. I'm not quite sure what the problem is with the test.
If anybody wants a C++/Wrl example then I have that too.
Update 07/08/2017: As requested here is a C++/Wrl example. I have just run this in a Universal Windows (10) Test project in Visual Studio 2017. The key thing here is the weird part Callback<Implements< RuntimeClassFlags<ClassicCom >, IWorkItemHandler , FtmBase >>
, as opposed to just Callback<IWorkItemHandler>
. When I had the latter, the program jammmed except for when it was in a .exe project. I found this solution here: https://social.msdn.microsoft.com/Forums/windowsapps/en-US/ef6f84f6-ad4d-44f0-a107-3922d56662e6/thread-pool-task-blocking-ui-thread . See "agile objects" for more information.
#include "pch.h"
#include "CppUnitTest.h"
#include <Windows.Foundation.h>
#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <wrl/event.h>
#include <memory>
#include "concrt.h"
#include <Windows.System.Threading.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace Windows::System::Threading;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;
namespace TestWinRtAsync10
{
TEST_CLASS(UnitTest1)
{
public:
TEST_METHOD(AsyncOnThreadPoolUsingEvent10Wrl)
{
HRESULT hr = BasicThreadpoolTestWithAgileCallback();
Assert::AreEqual(hr, S_OK);
}
HRESULT BasicThreadpoolTestWithAgileCallback()
{
std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();
ComPtr<ABI::Windows::System::Threading::IThreadPoolStatics> _threadPool;
HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &_threadPool);
ComPtr<IAsyncAction> asyncAction;
hr = _threadPool->RunAsync(Callback<Implements<RuntimeClassFlags<ClassicCom>, IWorkItemHandler, FtmBase>>([&_completed](IAsyncAction* asyncAction) -> HRESULT
{
// Prints a message in debug run of this test
std::ostringstream ss;
ss << "Threadpool work item running.\n";
std::string _string = ss.str();
std::wstring stemp = std::wstring(_string.begin(), _string.end());
OutputDebugString(stemp.c_str());
//
_completed->set();
return S_OK;
}).Get(), &asyncAction);
_completed->wait();
return S_OK;
}
};
}
Update 08/08/2017: More example, per the comments.
#include "pch.h"
#include "CppUnitTest.h"
#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <wrl/event.h>
#include <memory>
#include "concrt.h"
#include <Windows.System.Threading.h>
#include <Windows.ApplicationModel.Core.h>
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
namespace TestWinRtAsync10
{
TEST_CLASS(TestWinRtAsync_WrlAsyncTesting)
{
public:
TEST_METHOD(PackageClassTest)
{
ComPtr<ABI::Windows::ApplicationModel::IPackageStatics> _pPackageStatics;
HRESULT hr = GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_ApplicationModel_Package).Get(), &_pPackageStatics);
ComPtr<ABI::Windows::ApplicationModel::IPackage> _pIPackage;
hr = _pPackageStatics->get_Current(&_pIPackage);
ComPtr<ABI::Windows::ApplicationModel::IPackage3> _pIPackage3;
hr = _pIPackage->QueryInterface(__uuidof(ABI::Windows::ApplicationModel::IPackage3), &_pIPackage3);
ComPtr<__FIAsyncOperation_1___FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry> _pAsyncOperation;
hr = _pIPackage3->GetAppListEntriesAsync(&_pAsyncOperation);
std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();
_pAsyncOperation->put_Completed(Microsoft::WRL::Callback<Implements<RuntimeClassFlags<ClassicCom>, ABI::Windows::Foundation::IAsyncOperationCompletedHandler <__FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry*>, FtmBase >>
([&_completed](ABI::Windows::Foundation::IAsyncOperation<__FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry*>* pHandler, AsyncStatus status)
{
__FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry* _pResults = nullptr;
HRESULT hr = pHandler->GetResults(&_pResults);
ComPtr<ABI::Windows::ApplicationModel::Core::IAppListEntry> _pIAppListEntry;
unsigned int _actual;
hr = _pResults->GetMany(0, 1, &_pIAppListEntry, &_actual);
ComPtr<ABI::Windows::ApplicationModel::IAppDisplayInfo> _pDisplayInfo;
hr = _pIAppListEntry->get_DisplayInfo(&_pDisplayInfo);
Microsoft::WRL::Wrappers::HString _HStrDisplayName;
hr = _pDisplayInfo->get_DisplayName(_HStrDisplayName.GetAddressOf());
const wchar_t * _pWchar_displayName = _HStrDisplayName.GetRawBuffer(&_actual);
OutputDebugString(_pWchar_displayName);
_completed->set();
return hr;
}).Get());
_completed->wait();
};
};
}
This outputted:
TestWinRtAsync10
Just in case anyone needs here is solution in C++/WinRT. Say you have function ProcessFeedAsync()
that would return IAsyncAction
, just need following simple code:
winrt::init_apartment();
auto processOp{ ProcessFeedAsync() };
// do other work while the feed is being printed.
processOp.get(); // no more work to do; call get() so that we see the printout before the application exits.
source
Calling wait
on the concurrency::task
after you create it completely defeats the point of having tasks in the first place.
What you have to realize is that in the Windows Runtime, there are many asynchronous operations that cannot (or should not) be run on (or waited for on) the UI thread; you've found one of them, and now you're trying to wait on it. Instead of potentially causing a deadlock, you're getting an exception.
To remedy this, you need to use a continuation. You're most of the way there; you're already defining a continuation function:
IAsyncAction^ Action = CurrentAppSimulator::ReloadSimulatorAsync(proxyFile);
create_task( Action ).then([this]()
{
}).wait();
// do important things once the simulator has reloaded
important_things();
...but you're not using it. The purpose of the function you pass into then
is to be called off the UI thread once the task is complete. So, instead, you should do this:
IAsyncAction^ Action = CurrentAppSimulator::ReloadSimulatorAsync(proxyFile);
create_task( Action ).then([this]()
{
// do important things once the simulator has reloaded
important_things();
});
Your important post-reload code won't run until the task is complete, and it will run on a background thread so it doesn't block or deadlock the UI.
This is the magical fix that gets the job done :
void WaitForAsync( IAsyncAction ^A )
{
while(A->Status == Windows::Foundation::AsyncStatus::Started)
{
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
}
Windows::Foundation::AsyncStatus S = A->Status;
}