使用Concurrency :: task从我的代码中列出一些设备时遇到问题:
去年使用Visual Studio 2015使用此代码:
Concurrency::create_task(Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(Windows::Devices::Midi::MidiInPort::GetDeviceSelector()))
.then([](Windows::Devices::Enumeration::DeviceInformationCollection ^midiDeviceCollection) {
for (Windows::Devices::Enumeration::DeviceInformation^ portInformation : midiDeviceCollection){
//do stuff with device
}
});
但几个月后,......今天,我必须更新我的应用程序(与此同时我切换到VS2017)。所以我重新打开并更新了项目,遗憾的是这段代码不再按照应有的方式运行。断点设置在"然后()"部件永远不会到达,代码继续执行而不会出现错误,就好像我的任务从未被调用过一样。
我想知道执行DeviceInformation :: FindAllAsync时是否是一个错误,它不会让" then()"部分运行。在对任务(https://msdn.microsoft.com/fr-fr/library/dd492427.aspx)进行一些研究之后,我找到了一种在任务运行时捕获异常的方法,因此我将代码转换为:
IAsyncOperation<DeviceInformationCollection^>^ deviceOp = DeviceInformation::FindAllAsync(MidiInPort::GetDeviceSelector());
Concurrency::task<DeviceInformationCollection^> deviceEnumTask = Concurrency::create_task(deviceOp);
deviceEnumTask.then([](Concurrency::task<DeviceInformationCollection^> t){
try{
DeviceInformationCollection ^midiDeviceCollection = t.get();
for (DeviceInformation^ portInformation : midiDeviceCollection) {
//do stuff with device
}
}catch (Platform::Exception^ e){
//do stuff with device
OutputDebugString(e->Message->Data());
}
});
同样的结果,应用程序永远不会进入&#34;然后()&#34;并且没有抛出异常:-(。
所以我的问题是:这里有什么问题?这段代码是列出设备最常用的方式,我已经看到它在几个地方的所有其他网页(而且它的工作在去年我的情况),所以我应该犯了一个错误或遗漏其他地方的东西。
这是捕捉任务错误的好方法吗?
更多信息:这个代码直接在MainPage构造函数中调用,就在InitializeComponent()之后; 。我正在运行VS2017,目标平台最低版本是10.0.10240.0,目标平台版本是10.0.15063.0。该应用程序是在台式机x64机器上构建和测试的。
非常感谢你的帮助。
修改:
感谢Sunteen Wu和David Pritchard(见下文),我已经能够找出问题的来源。上面的代码实际上是有效的,如答案中所述,并且在调整David提出的错误处理之后非常清楚和完整。在我的测试中,我的断点也放在了正确的位置。但实际上,在没有意识到的情况下,我在&#34; //中使用了一些阻塞代码更深入了解设备&#34; (这里不可见)。这使得MainPage构造函数无法继续执行,直到被调用的任务完成为止...显然任务仍然与主线程执行相关联,并且只要MainPage构造没有结束就没有运行,在我的情况下从未发生过 - &gt ;死锁!
答案 0 :(得分:0)
同样的结果,应用程序永远不会进入“then()”并且不会抛出异常
通过测试,在task::then
内设置断点时,其委托可以成功执行,如下所示:
应用程序确实输入了委托。如果您仅在task::then
代码行设置断点并使用F10
跳过,它将跳过委托,因为task::then
方法立即返回,并且其委托不会运行直到异步工作成功完成。详细请参考Consuming an async operation by using a task。
这是捕捉任务错误的好方法吗?
根据Handling errors in a task chain,您尝试捕捉错误的方式似乎是正确的。如果异步操作导致抛出异常,则永远不会执行继续。您将获得task::get
传输到任务的任何例外情况。
您还可以检查没有MidiInPort
是否导致“不再工作”。尝试获取设备大小如上图所示。
答案 1 :(得分:0)
我无法与你问题的MIDI部分说话。我可以使用异常处理代码。
首先 - 我怀疑你是否看到来自FindAllAsync
的异常,因为未观察到的异常通常会使应用程序崩溃(或者破解为调试程序)。
第二 - 上面的异常处理程序似乎混淆了两种类型的延续:
类型1:常规延续,一旦异步方法返回其结果,就继续工作。
例如,deviceEnumTask.then([] (DeviceInformationCollection ^) { ... })
类型2:异常处理延续,用于处理任务链中的任何异常。
例如,deviceEnumTask.then([] (Concurrency::task<void> t) { ... })
您的原始代码示例使用了类型1但未包含类型2,因此无法处理任何异常。
您修改后的代码示例使用非标准延续(类型1和类型2),并且可能永远不会被调用。
你想要的是同时使用1和2,如下所示:
using namespace Windows::Devices::Enumeration;
using namespace Windows::Devices::Midi;
Concurrency::create_task(
DeviceInformation::FindAllAsync(MidiInPort::GetDeviceSelector())
).then([](DeviceInformationCollection ^midiDeviceCollection) {
for (DeviceInformation^ portInformation : midiDeviceCollection){
//do stuff with device
}
}).then([] Concurrency::task<void> t) {
try {
t.get();
OutputDebugString(L"No exceptions seen");
}
catch (Platform::Exception ^e) {
OutputDebugString(e->Message->Data());
}
});