Exception “从不执行并发任务”;然后();nor引发异常(C+;+;/CX UWP)

Exception “从不执行并发任务”;然后();nor引发异常(C+;+;/CX UWP),exception,concurrency,uwp,task,c++-cx,Exception,Concurrency,Uwp,Task,C++ Cx,使用Concurrency::task从代码中列出一些设备时出现问题: 去年在visual studio 2015中使用此代码的用法: Concurrency::create_task(Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(Windows::Devices::Midi::MidiInPort::GetDeviceSelector())) .then([](Windows::Devices::Enume

使用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)。所以我重新打开并更新了项目,遗憾的是,这段代码不再按它应该的方式工作。“then()”部分中设置的断点永远不会到达,代码将继续执行,不会出错,就好像从未调用过我的任务一样

我想知道在执行DeviceInformation::FindAllAsync时是否有错误,该错误不允许“then()”部分运行。在对任务()进行了一些研究之后,我找到了一种在任务运行时捕获异常的方法,因此我将代码转换为:

    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());
        }

    });
IAsyncOperation^deviceOp=DeviceInformation::findalsync(midinport::GetDeviceSelector());
并发::任务设备枚举任务=并发::创建任务(deviceOp);
deviceEnumTask.then([](并发::任务t){
试一试{
DeviceInformation集合^midiDeviceCollection=t.get();
for(设备信息^portInformation:midiDeviceCollection){
//用这个设备做事
}
}捕获(平台::异常^e){
//用这个设备做事
OutputDebugString(e->Message->Data());
}
});
同样的结果是,应用程序从不进入“then()”,也不会引发异常:-(

所以我的问题是:这里的问题是什么?这段代码是列出设备的最常见的方式,我在网络上的其他地方都看到过它(而且它去年在我的案例中起了作用),所以我应该在其他地方犯了错误或遗漏了一些东西

这是捕获任务中错误的好方法吗

更多信息:此代码直接在主页构造函数中调用,就在InitializeComponent()之后。我正在运行VS2017,目标平台最小版本为10.0.10240.0,目标平台版本为10.0.15063.0。该应用程序是在桌面x64机器上构建和测试的

非常感谢你的帮助

编辑:

感谢吴荪婷和大卫·普里查德(见下文)我已经能够找出问题的根源。正如答案中所述,上面的代码实际上是有效的,并且在按照David的建议调整错误处理后非常清晰和完整。在我的测试中,我的断点也放在了正确的位置。但事实上,在没有意识到这一点的情况下,我在测试中使用了一些更深层次的阻塞代码“//使用设备执行任务”(此处不可见)。这会阻塞主页构造函数,使其无法继续执行,直到调用的任务完成……显然,只要主页构造未结束,任务仍与主线程执行相链接,并且不会运行,在我的情况下,从未发生过这种情况->死锁

同样的结果是,应用程序从不进入“then()”,也不会引发异常

通过在我这边的测试,当在
task::then
内部设置断点时,其委托可以成功执行,如下所示:

应用程序确实输入了委托。如果您仅在
任务::然后
代码行设置断点并使用
F10
跳过,它将跳过委托,因为
任务::然后
方法立即返回,并且其委托在异步工作成功完成之前不会运行。详细信息请参考

这是捕获任务中错误的好方法吗

根据,您试图捕获错误的方式似乎是正确的。如果异步操作导致引发异常,则继续将永远不会执行。您将获得通过
task::get
传输到任务的任何异常


您还可以检查“不再工作”是否是由于没有
midinport
造成的。请尝试获取上图所示的设备大小。

我无法回答您问题的MIDI部分。我可以回答异常处理代码

首先-我怀疑您是否在
findalsync
中看到异常,因为未观察到的异常通常会导致应用程序崩溃(或中断到调试器)

第二,上面的异常处理程序似乎混淆了两种类型的延续:

  • 类型1:常规继续,在异步方法返回其结果后继续工作

    e、 例如,
    deviceEnumTask.then([](DeviceInformation Collection^){…})

  • 类型2:异常处理延续,用于处理任务链中的任何异常

    e、 例如,
    deviceEnumTask.then([](并发::任务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());
   }
});
使用命名空间Windows::Devices::Enumeration;
使用名称空间Windows::Devices::Midi;
并发::创建任务(
DeviceInformation::FindAllAsync(MIDINPort::GetDeviceSelector())
)。然后([](设备信息集合^midiDeviceCollection){
for(设备信息^portInformation:midiDeviceCollection){
//用这个设备做事
}
}).然后([]并发::任务t){
试一试{
t、 get();
OutputDebugString(L“未发现异常”);
}
捕获(平台::异常^e){
OutputDebugString(e->Message->Data());
}
});

关于错误处理的解释非常清楚,这是我错过的。