После некоторых экспериментов есть несколько рекомендаций, которые мне удобно делать.
- Функциональность задачи
concurrency
проще в использовании, чем C ++ 11 std:thread
, и она более гибкая в использовании.использование с сопрограммами, однако, std::async
проще в использовании, чем std::thread
, и работает с co_await
, а также - сопрограммы с использованием
co_await
выглядят как отличное дополнение к concurrency
при использовании C ++ / WinRT ифункции типа Async
в WinRT (см. Подпрограммы C ++: понимание оператора co_await для технического объяснения) - вы можете создавать свои собственные асинхронные функции, используя шаблон
concurrency::task<>
в качестве возвращаемого типафункции или используйте concurrency::create_task()
, и вы можете использовать co_await
с такой задачей - вы также можете использовать
co_await
с std::async()
, поскольку std::async()
возвращает std::future<>
, который имеет интерфейс Awaitable(см. await / yield: сопрограммы C ++ , хотя он датирован ноябрем-2016 г.) - , вы также можете использовать
co_await
с std::future<>
, как предусмотрено методом get_future()
std::packaged_task<>
(см. Также Что яРазница между packaged_task и async ) - позволяет создавать функции-генераторы, используя
std::experimental::generator<type>
в качестве возвращаемого типа функции, а также оператор co_yield
для возврата значения указанного типа в сгенерированной серии - для обновления пользовательского интерфейса MFC требуется, чтобы любой код выполнялся в потоке пользовательского интерфейса MFC, в котором был создан объект MFC, поэтому сообщения Windows необходимы для связи с окнами MFC и объектами класса окна из других потоков или необходимо переключить потокконтекст / сходство с контекстом потока пользовательского интерфейса для этого объекта
winrt::apartment_context
может использоваться для захвата текущего контекста потока, а затем возобновляется с использованием co_await
, который может использоваться для захвата контекста основного потока пользовательского интерфейса, который будет использоваться повторнопозже (см. Программирование с учетом сходства потоков в статье Параллельные и асинхронные операции с C ++ / WinRT) co_await winrt::resume_background();
можно использовать для передачи контекста текущего потока в фоновый поток, которыйможет быть полезным для длительной задачи, которая может быть в маеn Контекст потока пользовательского интерфейса, и вы хотите убедиться, что при отправке сообщений в окно это не - , убедитесь, что окно действительно было создано и существует, во время запуска приложения приложение должно создать окна, прежде чем вы сможетеиспользуй их; только то, что класс MFC существует, еще не означает, что базовое окно создано.
::SendMessage()
синхронно отправляет сообщение и возвращает ответ ::PostMessage()
является асинхронным, при котором сообщение отправляется, а ответ не возвращается - с использованием
::PostMessage()
будьте осторожны, чтобы указатели, отправленные в сообщениях, не выходили за рамки, пока их не использовал получатель, поскольку обычнозадержка между возвращением ::PostMessage()
и обработчиком сообщения, получающим сообщение, на самом деле что-то делает с сообщением - , вероятно, самый простой подход - использовать макрос
ON_MESSAGE()
в карте сообщений с интерфейсом обработчика сообщений afx_msg LRESULT OnMessageThing(WPARAM, LPARAM)
- вы можете использовать идентификаторы сообщений Windows в пространстве, начинающемся с определенной константы
WM_APP
, и один и тот же идентификатор можно использовать в разных классах - вы можете использовать большую часть C ++ / WinRTдовольно легко с MFC с небольшим вниманием, хотя по общему признанию, я только попробовал несколько вещейи существуют некоторые ограничения, такие как не использование XAML в соответствии с документацией
- , если вы используете C ++ / WinRT в приложении MFC, вы ограничиваете свое приложение версиями Windows с Windows Runtime , что в значительной степени означает Windows 10 (это исключает использование C ++ / WinRT с Windows 7, POS Ready 7 и т. Д.)
- с использованием C ++ / WinRT требует добавления опции компилятора
/stdc++17
для включения ISO C ++17 Standard для C ++ Language Standard и использование сопрограмм требует опции компилятора /await
Вот ресурсы просмотра.
Microsoft Build 2018 Effective C ++/ WinRT для UWP и Win32 06 мая 2018 года в 15:27 от Брента Ректора, Кенни Керра
CppCon 2017: Скотт Джонс и Кенни Керр C ++ / WinRT и будущее C ++ для Windows Опубликовано 2 ноября 2017 г.
Использование Visual Studio 2017 Community Edition, Iсоздал новый проект MFC Single Document Interface (SDI), используя стиль Visual Studio.После запуска приложения оно выглядит следующим образом.
Вспомогательные функции для сообщений
Первым изменением, которое я сделал, было предоставление способа отправить сообщение Windows на одну из панелей (ClassView или OutputWindow), которую я хотел бы обновить.Так как класс CMainFrame
в MainFrm.h имел объекты MFC для этих окон, как в:
protected: // control bar embedded members
CMFCMenuBar m_wndMenuBar;
CMFCToolBar m_wndToolBar;
CMFCStatusBar m_wndStatusBar;
CMFCToolBarImages m_UserImages;
CFileView m_wndFileView;
CClassView m_wndClassView;
COutputWnd m_wndOutput;
CPropertiesWnd m_wndProperties;
Я изменил класс, чтобы обеспечить способ отправки сообщения в эти окна.Я решил использовать SendMessage()
вместо PostMessage()
для устранения указателя, выходящего из области видимости.Класс concurrency
отлично работает с SendMessage()
.
LRESULT SendMessageToFileView(UINT msgId, WPARAM wParam, LPARAM lParam) { return m_wndFileView.SendMessage(msgId, wParam, lParam); }
LRESULT SendMessageToClassView(UINT msgId, WPARAM wParam, LPARAM lParam) { return m_wndClassView.SendMessage(msgId, wParam, lParam); }
LRESULT SendMessageToOutputWnd(UINT msgId, WPARAM wParam, LPARAM lParam) { return m_wndOutput.SendMessage(msgId, wParam, lParam); }
Это необработанная инфраструктура для отправки сообщений для обновления различных окон MFC.Я поместил их в класс CMainFrame
, так как это центральная точка, а функция AfxGetMainWnd()
позволяет мне получить доступ к объекту этого класса в любом месте приложения MFC.Было бы целесообразно использовать дополнительные классы для переноса этих необработанных функций.
Затем я поместил обработчики сообщений в каждый из классов в макросах BEGIN_MESSAGE_MAP
и END_MESSAGE_MAP
.Обновление окна вывода было самым простым и простым и выглядело так:
BEGIN_MESSAGE_MAP(COutputWnd, CDockablePane)
ON_WM_CREATE()
ON_WM_SIZE()
// ADD_ON: message handler for the WM_APP message containing an index as
// to which output window to write to along with a pointer to the
// text string to write.
// this->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)_T("some text"));
ON_MESSAGE(WM_APP, OnAddItemsToPane)
END_MESSAGE_MAP()
с обработчиком сообщений, похожим на:
// ADD_ON: message handler for the WM_APP message containing an array of the
// struct ItemToInsert above. Uses method AddItemsToPane().
LRESULT COutputWnd::OnAddItemsToPane(WPARAM wParam, LPARAM lParam)
{
switch (wParam) {
case OutputBuild:
m_wndOutputBuild.AddString((TCHAR *)lParam);
break;
case OutputDebug:
m_wndOutputDebug.AddString((TCHAR *)lParam);
break;
case OutputFind:
m_wndOutputFind.AddString((TCHAR *)lParam);
break;
}
return 0;
}
Я добавил прототип метода в класс вместе с этим перечислениемчтобы сделать функционал немного проще в использовании.
enum WindowList { OutputBuild = 1, OutputDebug = 2, OutputFind = 3 };
С учетом вышеупомянутых изменений я смог вставить в обработчик сообщений для "New" в BOOL CMFCAppWinRTDoc::OnNewDocument()
следующий код для помещения текстовой строки вОкно вывода «Build»:
CMainFrame *p = dynamic_cast <CMainFrame *> (AfxGetMainWnd());
if (p) {
p->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)_T("this is a test from OnNewDocument()."));
}
Использование C ++ / WinRT с MFC и concurrency
Чтобы проверить это вместе с тестированием с использованием C ++ / WinRT с MFC,Я добавил следующую задачу concurrency
в CMainFrame::OnCreate()
, которая вызывается при запуске приложения.Этот источник приводит к выполнению задачи, которая затем использует функциональность Syndication
C ++ / WinRT для получения списка каналов RSS и отображает заголовки на панели OutputWindow, помеченные как «Build», как показано на снимке экрана выше.
concurrency::create_task([this]() {
winrt::init_apartment();
Sleep(5000);
winrt::Windows::Foundation::Uri uri(L"http://kennykerr.ca/feed");
winrt::Windows::Web::Syndication::SyndicationClient client;
winrt::Windows::Web::Syndication::SyndicationFeed feed = client.RetrieveFeedAsync(uri).get();
for (winrt::Windows::Web::Syndication::SyndicationItem item : feed.Items())
{
winrt::hstring title = item.Title().Text();
this->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)title.c_str()); // print a string to an output window in the output pane.
}
winrt::uninit_apartment();
});
Чтобы использовать функции concurrency
и C ++ / WinRT, мне пришлось добавить пару включаемых файлов в верхней части исходного файла MainFrm.c.
// ADD_ON: include files for using the concurrency namespace.
#include <experimental\resumable>
#include <pplawait.h>
#pragma comment(lib, "windowsapp")
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Web.Syndication.h"
Кроме того, мне пришлось изменитьСвойства решения для указания C ++ 17 и дополнительной опции компилятора /await
, которые отмечены синими стрелками на снимке экрана ниже.
Использование co_await
с MFC и C ++ / WinRT
Из полезного комментария от @IInspectable я взглянул на сопрограммыс Visual Studio 2017 и MFC.Мне было любопытно узнать о них, однако мне показалось, что я не могу придумать ничего, что могло бы скомпилировать без ошибок при использовании co_await
.
Однако, начиная со ссылки в комментарии от @IInspectable, я нашел ссылку наэто видео на YouTube, CppCon 2016: Кенни Керр и Джеймс Макнеллис «Использование сопрограмм для работы со средой выполнения Windows» , в котором был пример исходного кода во время 10:28, и, наконец, я смог что-то придуматьэто скомпилировало бы и работало.
Я создал следующую функцию, которую я затем использовал, чтобы заменить вышеупомянутый источник на concurrency::create_task()
, а лямбда-функцию - вызовом следующей функции. Вызов функции был простым, myTaskMain(this);
заменив concurrency::create_task([this]() {
лямбду в методе int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
, а затем добавив следующий исходный код над телом функции OnCreate()
.
winrt::Windows::Foundation::IAsyncAction myTaskMain(CMainFrame *p)
{
winrt::Windows::Foundation::Uri uri(L"http://kennykerr.ca/feed");
winrt::Windows::Web::Syndication::SyndicationClient client;
winrt::Windows::Web::Syndication::SyndicationFeed feed = co_await client.RetrieveFeedAsync(uri);
Sleep(5000);
for (winrt::Windows::Web::Syndication::SyndicationItem item : feed.Items())
{
winrt::hstring title = item.Title().Text();
p->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)title.c_str()); // print a string to an output window in the output pane.
}
}
Есть два изменения, которые я сделал из источника concurrency::create_task()
заменяется:
- удалены
winrt::init_apartment();
и winrt::uninit_apartment();
, так как их использование вызвало исключениеПохоже, что их удаление и удаление не имеют значения - переместил
Sleep(5000);
после co_await
после того, как оставил его там, где он был вызван, чтобы функция спала в течение 5 секунд, что означало, что поток пользовательского интерфейса спал в течение 5 секунд
Что я нашел сОтладчик состоял в том, что в тот момент, когда была вызвана функция myTaskMain()
, был немедленный возврат из функции, и поток пользовательского интерфейса продолжал работать, пока сопрограмма выполнялась в фоновом режиме.Пользовательский интерфейс быстро отобразил, а затем через пять секунд произошли дополнительные действия, обновив дерево представления класса и список RSS-каналов на вкладке «Построение» в окне вывода.
Примечание № 1: Еще одна вещь, с которой я столкнулся в ходе другого тестирования, заключается в том, что пользовательский интерфейс будет зависать на несколько секунд (меню не работает).Похоже, это связано с тем, что Sleep(5000);
указывает на то, что код после co_await
выполняется в основном потоке пользовательского интерфейса.Это изменение в поведении приложения началось после того, как я начал исследовать использование winrt::apartment_context ui_thread;
для захвата контекста основного потока пользовательского интерфейса, чтобы затем использовать co_await ui_thread;
для возврата моего потока сопрограммы в контекст основного потока пользовательского интерфейса.
Что можетпроисходит то, что client.RetrieveFeedAsync(uri)
немедленно выполняется без задержки, возможно, из кеша, поэтому вместо того, чтобы переносить задачу в другой поток и затем возвращаться к вызывающей стороне, co_await
получает немедленный результат, и функция myTaskMain()
может немедленно продолжить использование текущего потока, который является основным потоком пользовательского интерфейса?
Я заметил, что в Visual Studio 2017 co_await
, используемый с client.RetrieveFeedAsync(uri)
, окрашен в зеленый цвет, в то время как co_await
используется сco_await ui_thread;
синий.При наведении курсора мыши на зеленый цвет co_await
я получаю подсказку, указывающую, что это другая версия co_await
.
Примечание # 2: Существует функция C ++ / WinRT для перемещения в контекст фонового потока, winrt::resume_background()
, которую можно использовать с co_await
.Если я изменю вышеуказанную функцию myTaskMain()
, заменив строку кода Sleep(5000);
после вызова client.RetrieveFeedAsync(uri)
следующими двумя строками кода, чтобы перенести контекст потока в фоновый поток, я не вижу зависания (пользовательский интерфейс реагирует на выбранные пункты меню), а текстовые строки RSS-канала отображаются на вкладке «Построение» в окне «Вывод» через 15 секунд.
co_await winrt::resume_background(); // switch context to background thread
Sleep(15000);
Выполнение асинхронной задачи с использованием concurrency::task<>
который работает с co_await
Одна вещь, которая меня интересовала, была возможность создания моей собственной асинхронной задачи, которую я мог бы использовать с co_await
, аналогично функциям асинхронного типа в C ++ / WinRT.Я потратил некоторое время на поиски, пока, наконец, не нашел эту статью, Параллелизм и асинхронные операции с C ++ / WinRT , с разделом под названием Асинхронно возвращает тип не-Windows-Runtime .
Вот простая демонстрационная функция, которая создает concurrency::task<>
с лямбда-выражением и возвращает задание, которое затем используется с co_await
.Эта конкретная лямбда возвращает int
, поэтому функция определяется как задача, которая возвращает int
, concurrency::task<int>
concurrency::task<int> mySleepTaskAsync()
{
return concurrency::create_task([]() {
Sleep(15000);
return 5;
});
}
. Вышеупомянутая функция затем используется с оператором co_await
воператор, такой как:
int jj = co_await mySleepTaskAsync();
, который приведет к тому, что переменная jj
будет иметь значение 5 после ожидания 15 секунд.
Вышеупомянутое используется в функции, которая возвращаетwinrt::Windows::Foundation::IAsyncAction
, например, функция myTaskMain()
выше.
Если хотите, вы также можете просто напрямую использовать лямбду с co_await
, как в:
int jj = co_await concurrency::create_task([]() {
Sleep(15000);
return 5;
});
Или вы можете иметьобычная функция, такая как:
int mySleepTaskAsyncInt()
{
Sleep(15000);
return 5;
}
, а затем использовать ее с co_await
, используя concurrency::task<>
, как в:
int jj = co_await concurrency::create_task(mySleepTaskAsyncInt);
Выполнение асинхронной задачи с использованием std::async
работает с co_await
Хотя std::thread
не работает с co_await
, что приводит к ошибке компиляции, вы можете использовать std::async
с co_await
.Причиной является тип возвращаемого значения, который требуется оператору co_await
, и разница в возвращаемом значении std::thread
, std::thread
и возвращаемом значении std::async
, std::future<>
.
Оператор co_await
требует, чтобы переменная, с которой он работает, была std::future<>
, имела метод get()
для получения результата из потока и была ожидаемой.
#include <future>
int mySleepTaskAsyncInt()
{
Sleep(7000);
return 5;
}
winrt::Windows::Foundation::IAsyncAction myTaskMain(CMainFrame *p)
{
auto t1 = co_await std::async (std::launch::async, mySleepTaskAsyncInt);
// do something with the variable t1
}
Выполнение асинхронной задачи с std::packaged_task<>
и std::future<>
с co_await
Поскольку co_await
требует объекта Awaitable, другой способ создания такого объекта - создать задачу сstd::packaged_task<>
затем запустите задачу и используйте метод get_future()
, чтобы получить std::future<>
, который затем можно использовать с co_await
.
Например, у нас может быть следующая простая функция, которая создастпакет задач, запустите выполнение задачи и верните std::future<>
.Затем мы можем использовать эту функцию в качестве цели для оператора co_await
для реализации сопрограммы.
#include <future>
std::future<int> mySleepTaskStdFutureInt()
{
// create the task to prepare it for running.
std::packaged_task<int()> task([]() {
Sleep(7000);
return 455; // return an int value
});
// start the task running and return the future
return task(), task.get_future();
}
, а затем в нашем исходном коде мы будем использовать эту функцию, подобную:
int jkjk = co_await mySleepTaskStdFutureInt();
Оператор return
использует оператор запятой, чтобы ввести точку последовательности, чтобы мы запустили выполнение задачи, а затем вызвали метод get_future()
в выполняющейся задаче.Результат get_future()
метода, std::future<int>
- это то, что фактически возвращается функцией.
Задача, созданная с помощью std::packaged_task()
, должна быть запущена с помощью функции, подобнойвызов с использованием переменной.Если вы не запустите задание, то std::future<>
, возвращаемое функцией, никогда не будет иметь переменную, а co_await
, ожидающий завершения Awaitable и предоставления значения, никогда не сработает.В результате источник после вашего co_await
не будет выполнен, потому что co_await
никогда не будет запущен.
Генератор с co_yield
и std::experimental::generator<type>
При расследовании co_await
я наткнулся на co_yield
, который используется для возврата значения как часть генератора набора значений.В Visual Studio 2017 для использования co_yield
необходимо включить заголовочный файл experimental/generator
.Вот простой пример генератора, который генерирует серию целых чисел.
#include <experimental/generator>
std::experimental::generator<int> makeSomeInts(int kCount)
{
for (int i = 0; i < kCount; i++) {
co_yield i;
}
}
И эту функцию можно использовать с ранжированием для, как в:
for (int kkk : makeSomeInts(10)) {
// code that uses the variable kkk which contains
// an int from the generated range 0 up to be not including 10.
}
Вышеприведенный цикл будетвыполняться для каждого целого значения от 0 до 9. Включая 9
Более сложное сообщение: обновление панорамы ClassView
Я также провел эксперимент с элементом управления ClassView Tree, чтобы обеспечитьпростой способ выполнения самых элементарных действий: создать начальный элемент управления деревом и добавить к нему.
В CClassView
классе в файле ClassView.h я добавил следующие структуры данных.Кстати, после того, как я это сделал, я понял, что, возможно, это неправильное место, поскольку класс CFileView
использует одну и ту же древовидную структуру, поэтому одинаковый подход будет работать для обеих этих панелей.В любом случае, я добавил следующее:
// ADD_ON: enumeration listing the various types of tree control icons which
// correspond to the position of a control in the tree.
// choose either classview_hc.bmp or classview.bmp for the bitmap strip that
// contains the 7 icons we are using for the images in our tree control.
// icons are standard size in height and width (15x15 pixels) in the order of:
// - main root icon
// - tree node icon which can be opened to show nodes beneath it
// - folder icon which is used to indicate a folder
// - method icon indicating a method of a class
// - locked method icon
// - member variable icon
// - locked member variable icon
enum IconList { MainRoot = 0, TreeNode = 1, FolderNode = 2, MethodNode = 3, MethodLockedNode = 4, MemberNode = 5, MemberLockedNode = 6 };
// ADD_ON: struct used to contain the necessary data for a node in the tree control.
struct ItemToInsert {
std::wstring label; // text to be displayed with the node.
int nImage; // zero based offset of the node's icon in the image, one of enum IconList above.
int nSelectedImage; // zero based offset of the node's icon in the image, one of enum IconList above.
};
Я создал обработчик сообщений и добавил его к карте сообщений в ClassView.cpp
ON_MESSAGE(WM_APP, OnAddItemsToPane)
и добавил сам обработчик сообщений.с помощью вспомогательной функции, которая выполняет фактическую обработку.
// ADD_ON: function for filling in the ClassView pane using an array of the
// struct ItemToInsert above. array is terminated by an entry with
// all zeros as in { _T(""), 0, 0 }
void CClassView::AddItemsToPane(CViewTree &xwndClassView, void *xrayp)
{
if (xrayp == 0) return;
// the images are icons that are laid out in a line of icons within a single bitmap image.
// see class method OnChangeVisualStyle() for when the bitmap image is loaded and then
// divided up into sections, 0 through 6, of the single bitmap image loaded.
// see classview.bmp and classview_hc.bmp in the ResourceFiles list.
HTREEITEM hRoot = xwndClassView.GetRootItem();
HTREEITEM hClass = 0;
ItemToInsert *xray = (ItemToInsert *)xrayp;
for (int i = 0; xray[i].label.size() != 0; i++) {
switch (xray[i].nImage) {
case MainRoot:
hRoot = xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage);
xwndClassView.SetItemState(hRoot, TVIS_BOLD, TVIS_BOLD);
xwndClassView.Expand(hRoot, TVE_EXPAND);
break;
case TreeNode:
hClass = xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage, hRoot);
break;
case FolderNode:
hClass = xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage, hRoot);
break;
case MethodNode:
case MethodLockedNode:
case MemberNode:
case MemberLockedNode:
xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage, hClass);
break;
default:
break;
}
}
}
// ADD_ON: message handler for the WM_APP message containing an array of the
// struct ItemToInsert above. Uses method AddItemsToPane().
LRESULT CClassView::OnAddItemsToPane(WPARAM wParam, LPARAM lParam)
{
switch (wParam) {
case 1:
AddItemsToPane(m_wndClassView, (void *)lParam);
break;
}
return 0;
}
Затем я создал несколько примеров данных для исходного дерева и затем добавил узел.
// ADD_ON: this is the content to be put into the ClassView tree pane.
// this is a tree structure.
CClassView::ItemToInsert xray2[] = {
{ _T("CFakeMainProject"), CClassView::MainRoot, CClassView::MainRoot },
{ _T("CFakeAboutDlg"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeAboutDlg()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("CFakeApp"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeApp()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("InitInstance()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("OnAppAbout()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("CFakeAppDoc"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeAppDoc()"), CClassView::MethodLockedNode, CClassView::MethodLockedNode },
{ _T("~CFakeAppDoc()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("OnNewDocument()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("CFakeAppView"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeAppView()"), CClassView::MethodLockedNode, CClassView::MethodLockedNode },
{ _T("~CFakeAppView()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("GetDocument()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("CFakeAppFrame"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CFakeAppFrame()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("~CFakeAppFrame()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T("m_wndMenuBar"), CClassView::MemberLockedNode, CClassView::MemberLockedNode },
{ _T("m_wndToolBar"), CClassView::MemberLockedNode, CClassView::MemberLockedNode },
{ _T("m_wndStatusBar"), CClassView::MemberLockedNode, CClassView::MemberLockedNode },
{ _T("Globals"), CClassView::FolderNode, CClassView::FolderNode },
{ _T("theFakeApp"), CClassView::MemberNode, CClassView::MemberNode },
{ _T(""), 0, 0 }
};
CClassView::ItemToInsert xray3[] = {
{ _T("CAdditionalDelay"), CClassView::TreeNode, CClassView::TreeNode },
{ _T("CAdditionalDelayMethod()"), CClassView::MethodNode, CClassView::MethodNode },
{ _T(""), 0, 0 }
};
Затем я выполняю это сообщениеобработчик, выделив две concurrency
задачи в методе CMainFrame::OnCreate()
, который сделал задержку, а затем обновил содержимое дерева окон ClassView.
concurrency::create_task([this]() { Sleep(5000); this->SendMessageToClassView(WM_APP, 1, (LPARAM)xray2); });
concurrency::create_task([this]() { Sleep(10000); this->SendMessageToClassView(WM_APP, 1, (LPARAM)xray3); });