Дело о прогрессе, чей callback никогда не вызывался при фактическом ходе работ

Коллега пытался выяснить, почему его обработчик прогресса не вызывается.

Это довольно стандартная ситуация. Запускаешь операцию, подключаешь обработчик прогресса и затем ждёшь завершения операции. Но прогресса они так и не видели.

Я попросил проверить, не скачивается ли элемент настолько быстро, что они пропускают все события прогресса. Но нет — даже если загрузка идёт долго, никакого прогресса не появляется.

Я предложил пройти пошагово через метод DownloadAsync, чтобы увидеть, где он генерирует события прогресса, а затем проследить выполнение до места, где должен вызываться callback прогресса, чтобы понять, почему этого не произошло. (Честно говоря, это задача отладки через языковой барьер, поэтому она сложнее, чем кажется. Я предложил сосредоточиться только на стороне C++: дождаться, когда будет сгенерирован COM-callable wrapper и установлен как callback прогресса, а затем поставить брейкпоинт на этом wrapper. Если этот брейкпоинт срабатывает, а C# код не выполняется, значит проблема в проекции. Если брейкпоинт вообще не срабатывает, значит проблема на стороне C++.)

Мой коллега вернулся с ответом. Вот код DownloadAsync:

AggregateSource собирает элементы от нескольких провайдеров. Формат id — это provider, двоеточие и затем ID. (И provider ID, и item ID экранируются, на случай если в них самих окажется двоеточие.)

Мы находим провайдера, а затем просим провайдера скачать элемент.

DownloadAsync вовсе не генерирует отчёты о прогрессе!

Он никогда не вызывает co_await winrt::get_progress_token(), не говоря уже о вызове токена с значением прогресса, чтобы сгенерировать отчёт о прогрессе.

Становится очевидно, что код при подключении callback прогресса пытается получать обратные вызовы от внутренней операции, той, что возвращает провайдер. Однако единственный IAsyncOperationWithProgress, к которому у него есть доступ, — это тот, что возвращён методом AggregateSource::DownloadAsync.

Простое решение здесь — убрать посредника и просто возвращать IAsyncOperationWithProgress от провайдера. Тогда вызывающий код сможет подключиться к прогрессу внутренней операции.

Если вы не доверяете completed_async, можно просто написать

Я сказал, что это простое решение. Есть и сложное решение, но о нём придётся рассказывать позже, потому что я ещё не написал об этом.

Рэймонд участвует в эволюции Windows более 30 лет. В 2003 году он запустил веб‑сайт под названием The Old New Thing, который стал популярнее, чем он мог себе представить, что до сих пор вызывает у него нервозность. Сайт породил книгу с тем же названием (Addison Wesley, 2007). Иногда он появляется в Twitter-аккаунте Windows Dev Docs, чтобы рассказывать истории, не несущие полезной информации.

Обсуждение закрыто. Войдите в систему, чтобы редактировать/удалять существующие комментарии.

Разве лямбда в конце не должна использовать co_return вместо обычного return?

Вам повезло, что простое решение здесь работает. Если бы любой из шагов поиска провайдера был асинхронным, пришлось бы применять сложное решение.

Что произойдёт, если изменить GCL_CBWND_EXTRA класса окна?

Почему мы не разрешаем стеку быть разреженным, вместо того чтобы требовать, чтобы он был непрерывным?