Ric*_*ers 5 mfc multithreading c++11 c++-winrt visual-studio-2017
在花了一些时间使用 C++/CX 和 ++/WinRT 开发简单的 UWP 应用程序后,我开始享受针对该环境进行 Windows UI 应用程序开发的一些功能。
现在必须回到更熟悉的 MFC 应用程序开发,我想将我的方法更改为类似于 UWP 应用程序开发的方法。其想法是使用异步 C++11 线程生成内容并修改 MFC UI 中显示的内容。
我想要做的主要更改是使用 C++11 线程来卸载一些耗时的任务,并使这些线程将结果传达回主 MFC UI。
我希望卸载到 C++11 线程上的一些任务(与我在 UWP 应用中使用 C++/CX 和 C++/WinRT 的异步任务使用的任务类似)是:
我遇到的问题与我在 MFC 中可以有多个 GUI 线程吗?中描述的问题类似。但是,我正在寻找一种通用方法,而不是该问题中的具体进度条更新。
我一直在尝试使用 Visual Studio 模板对实验性 MFC 应用程序进行简单测试,该模板在左侧停靠有一个树控件,以在工作线程中构建树。
如果我有一个CViewTree显示树视图的 MFC 窗口,我想从 C++11 线程更新该窗口,那么我当前正在使用它::PostMessage()来请求更新停靠窗格中的树控件。
如果我将 lambda 与全局一起使用,std::thread例如以下代码:
std::thread t1;
void CClassView::FillClassView()
{
// ::SendMessage() seems to deadlock so ::PostMessage() is required.
t1 = std::thread([this]() { Sleep(5000); ::PostMessage(this->m_hWnd, WM_COMMAND, ID_NEW_FOLDER, 0); });
}
Run Code Online (Sandbox Code Playgroud)
MFC 停靠窗格的消息处理程序如下所示:
void CClassView::OnNewFolder()
{
t1.join(); // this join seems to deadlock if ::SendMessage() is used.
AddItemsToPane(m_wndClassView);
}
Run Code Online (Sandbox Code Playgroud)
确实使用树控件内容更新 MFC 停靠窗格,就像AddItemsToPane(m_wndClassView);在创建 C++11 线程的同一位置调用该函数一样。当使用 C++11 线程时,窗格更新会延迟 5 秒,只是为了提供线程方法实际工作的可见指示。
我的问题是我希望 C++11 线程为树控件创建内容并将其提供给停靠窗格,而不是让停靠窗格生成内容。
到目前为止,我能想到的唯一方法是开发自己的类库,该类库将为 MFC 库和控件提供 C++11 线程类似物,用于将::PostMessage()适当的 Windows 消息发送到指定的 MFC 窗口或控件。
我想知道是否可以让 C++11 线程拥有自己的影子 MFC 控件并对其进行更新,然后向 UI 发送一条消息,要求 UI 使用影子 MFC 控件的内容更新其显示的控件?或者人们正在使用其他一些方法?
我正在寻找其他一些不太困难的方法来解决从 C++11 线程更新 MFC UI 的问题。
顺便说一下,#1 ::SendMessage()似乎陷入了死锁,我认为这join()意味着CClassView::OnNewFolder()C+11 线程和 UI 线程之间的某种同步正在阻止 C++11 线程到达其一侧join()?
是的,当消息处理程序等待线程完成SendMessage()时,线程等待返回,因此存在死锁。join()根据Windows开发中心的SendMessage函数:
将指定的消息发送到一个或多个窗口。该
SendMessage函数调用指定窗口的窗口过程,并且直到窗口过程处理完消息后才返回。要发送消息并立即返回,请使用
SendMessageCallbackorSendNotifyMessage函数。要将消息发布到线程的消息队列并立即返回,请使用PostMessageorPostThreadMessage函数。
顺便说一句#2对于 C++11 线程来说,使用实际的 Window 句柄而不是thislambda 中的指针似乎会更安全。以防万一this指针因某种原因(例如控件被删除)而变得未定义?
顺便说一下#3concurrency通过 提供的Microsoft命名空间#include <ppltasks.h>是 C++11 线程的替代方案。命名concurrency空间函数比 C++11 线程具有更高的抽象级别,并且更易于使用。
例如,上面的用法std:thread可以重写为:
void CClassView::FillClassView()
{
concurrency::create_task([this]() { Sleep(5000); ::SendMessage(this->m_hWnd, WM_COMMAND, ID_NEW_FOLDER, 0); });
}
Run Code Online (Sandbox Code Playgroud)
并且这不需要使用 astd::thread join()来干净地终止线程。也SendMessage()可PostMessage()用于发送 Windows 消息,因为我们不存在与 C++11 线程相同的死锁问题。
笔记
有关 MFC 特定内容,请参阅框架中的消息和命令。
注意#2: 使用 C++ 和 MFC 进行多线程,特别是“多线程:编程技巧”。
如果您的多线程应用程序以使用 CWinThread 对象以外的方式创建线程,则无法从该线程访问其他 MFC 对象。换句话说,如果要从辅助线程访问任何 MFC 对象,则必须使用多线程:创建用户界面线程或多线程:创建工作线程中描述的方法之一创建该线程。这些方法是唯一允许类库初始化处理多线程应用程序所需的内部变量的方法。
注意#3: 可从经典桌面应用程序调用 UWP API,其中显示:
除一些值得注意的例外情况外,一般规则是可以从经典桌面应用程序调用通用 Windows 平台 (UWP) API。此一般规则的例外的两个主要 API 领域是 XAML UI API 和要求调用应用程序具有包标识的 API。UWP 应用程序具有定义明确的应用程序模型,并且具有包标识。经典桌面应用程序没有明确定义的应用程序模型,并且没有包标识。已转换为 UWP 应用的经典桌面应用确实具有包标识。
另请参阅 2012 年 9 月发布的有关使用 VS 2012 和 Windows 8 的 WinRT 的以下博客。尽管使用 VS 2017 的 C++/WinRT 似乎比使用的 Windows 运行时模板库 (WRL) 更适合 Windows 10:
注意#4: MFC 桌面应用程序是一个包含大量链接的起点。另请参阅MFC COM,这是一个起点,其中包含许多有关 MFC 和 COM 的链接以及这篇文章COM 简介。另请参见MFC 宏和全局。
至于使用AfxGetMainWnd()获取应用程序主窗口,微软开发者中心在文章AfxGetMainWnd中有这样的说法:
如果从应用程序的主线程调用 AfxGetMainWnd,它将根据上述规则返回应用程序的主窗口。如果从应用程序中的辅助线程调用该函数,则该函数返回与发出调用的线程关联的主窗口。
经过一些实验后,我提出了一些我觉得舒服的建议。
\n\nconcurrency功能比 C++11 更容易使用std:thread,并且与协程一起使用更灵活,但也std::async更容易使用和std::thread使用co_awaitco_awaitLook 的协程是一个很好的补充(有关技术说明,请参阅C++ 协程:了解运算符 co_await )concurrencyAsyncconcurrency::task<>模板作为函数的返回类型或使用来创建自己的异步函数concurrency::create_task(),您可以使用co_await这样的任务co_awaitwith std::async(),因为std::async()返回std::future<>一个具有 Awaitable 接口的 a (请参阅等待/yield:C++ 协程,尽管它的日期为 2016 年 11 月)co_awaita ,如a 的方法std::future<>提供的那样(另请参阅packaged_task 和 async 之间的区别是什么)get_future()std::packaged_task<>std::experimental::generator<type>函数返回类型和co_yield运算符来创建生成器函数,以返回生成的系列中指定类型的值winrt::apartment_context可用于捕获当前线程上下文,并在稍后恢复使用,co_await可用于捕获主 UI 线程上下文以供稍后重用(请参阅文章“使用 C++/WinRT 进行并发和异步操作进行编程”)co_await winrt::resume_background();可用于将当前线程的上下文推送到后台线程,这对于可能位于主 UI 线程上下文中的冗长任务非常有用,并且您希望确保它不是::SendMessage()发送消息并返回响应是同步的::PostMessage()异步,发送消息但不返回响应::PostMessage()注意,消息中发送的指针在接收者使用它们之前不会超出范围,因为::PostMessage()返回与接收消息的消息句柄实际对消息执行某些操作之间通常存在延迟ON_MESSAGE()可能最直接的方法是在消息映射中使用带有消息处理程序接口的宏afx_msg LRESULT OnMessageThing(WPARAM, LPARAM)WM_APP并且相同的标识符可以在不同的类中使用/stdc++17以启用 C++ 语言标准的 ISO C++17 标准,而使用协程则需要/await编译器选项这里有一个查看资源。
\n\n\n\n\nMicrosoft Build 2018\n 适用于 UWP 和 Win32 的有效 C++/WinRT \n 2018 年 5 月 6 日下午 3:27 作者:Brent Rector、Kenny Kerr
\n\nCppCon 2017:Scott Jones 和 Kenny Kerr\n C++/WinRT 和 Windows 上 C++ 的未来\n 发布于 2017 年 11 月 2 日
\n
使用 Visual Studio 2017 社区版,我使用 Visual Studio 样式创建了一个新的 MFC 单文档界面 (SDI) 项目。应用程序出现后,如下图所示。
\n\n\n\n消息辅助函数
\n\n我所做的第一个更改是提供一种将 Windows 消息发送到我想要更新的窗格(ClassView 或 OutputWindow)的方法。由于CMainFrameMainFrm.h 中的类具有这些窗口的 MFC 对象,如下所示:
protected: // control bar embedded members\n CMFCMenuBar m_wndMenuBar;\n CMFCToolBar m_wndToolBar;\n CMFCStatusBar m_wndStatusBar;\n CMFCToolBarImages m_UserImages;\n CFileView m_wndFileView;\n CClassView m_wndClassView;\n COutputWnd m_wndOutput;\n CPropertiesWnd m_wndProperties;\nRun Code Online (Sandbox Code Playgroud)\n\n我修改了该类以提供一种向这些窗口发送消息的方法。我选择使用SendMessage()而不是PostMessage()消除指针超出范围的问题。该类concurrency可以很好地使用SendMessage().
LRESULT SendMessageToFileView(UINT msgId, WPARAM wParam, LPARAM lParam) { return m_wndFileView.SendMessage(msgId, wParam, lParam); }\nLRESULT SendMessageToClassView(UINT msgId, WPARAM wParam, LPARAM lParam) { return m_wndClassView.SendMessage(msgId, wParam, lParam); }\nLRESULT SendMessageToOutputWnd(UINT msgId, WPARAM wParam, LPARAM lParam) { return m_wndOutput.SendMessage(msgId, wParam, lParam); }\nRun Code Online (Sandbox Code Playgroud)\n\n这些是用于发送消息以更新各种 MFC 窗口的原始、裸露的基础结构。我将它们放入CMainFrame类中,因为类是中心点,并且该AfxGetMainWnd()函数允许我在 MFC 应用程序中的任何位置访问该类的对象。包装这些原始函数的其他类是合适的。
BEGIN_MESSAGE_MAP然后,我将消息处理程序放入和宏中的每个类中END_MESSAGE_MAP。输出窗口更新是最简单的,如下所示:
BEGIN_MESSAGE_MAP(COutputWnd, CDockablePane)\n ON_WM_CREATE()\n ON_WM_SIZE()\n // ADD_ON: message handler for the WM_APP message containing an index as\n // to which output window to write to along with a pointer to the\n // text string to write.\n // this->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)_T("some text"));\n ON_MESSAGE(WM_APP, OnAddItemsToPane)\nEND_MESSAGE_MAP()\nRun Code Online (Sandbox Code Playgroud)\n\n消息处理程序如下所示:
\n\n// ADD_ON: message handler for the WM_APP message containing an array of the\n// struct ItemToInsert above. Uses method AddItemsToPane().\nLRESULT COutputWnd::OnAddItemsToPane(WPARAM wParam, LPARAM lParam)\n{\n switch (wParam) {\n case OutputBuild:\n m_wndOutputBuild.AddString((TCHAR *)lParam);\n break;\n case OutputDebug:\n m_wndOutputDebug.AddString((TCHAR *)lParam);\n break;\n case OutputFind:\n m_wndOutputFind.AddString((TCHAR *)lParam);\n break;\n }\n\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我将方法原型与此枚举一起添加到类中,以使功能更易于使用。
\n\nenum WindowList { OutputBuild = 1, OutputDebug = 2, OutputFind = 3 };\nRun Code Online (Sandbox Code Playgroud)\n\n通过上述更改,我能够在BOOL CMFCAppWinRTDoc::OnNewDocument()以下代码中插入“New”的消息处理程序,以将文本字符串放入“Build”输出窗口中:
CMainFrame *p = dynamic_cast <CMainFrame *> (AfxGetMainWnd());\n\nif (p) {\n p->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)_T("this is a test from OnNewDocument()."));\n}\nRun Code Online (Sandbox Code Playgroud)\n\n将 C++/WinRT 与 MFC 结合使用以及concurrency
为了测试这一点以及使用 C++/WinRT 和 MFC 进行测试,我添加了以下concurrency任务,CMainFrame::OnCreate()应用程序启动时将调用该任务。该源衍生出一个任务,然后使用SyndicationC++/WinRT 的功能来获取 RSS 源列表,并在标有“Build”的 OutputWindow 窗格中显示标题,如上面的屏幕截图所示。
concurrency::create_task([this]() {\n winrt::init_apartment();\n\n Sleep(5000);\n\n winrt::Windows::Foundation::Uri uri(L"http://kennykerr.ca/feed");\n winrt::Windows::Web::Syndication::SyndicationClient client;\n winrt::Windows::Web::Syndication::SyndicationFeed feed = client.RetrieveFeedAsync(uri).get();\n for (winrt::Windows::Web::Syndication::SyndicationItem item : feed.Items())\n {\n winrt::hstring title = item.Title().Text();\n this->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)title.c_str()); // print a string to an output window in the output pane.\n }\n winrt::uninit_apartment();\n});\nRun Code Online (Sandbox Code Playgroud)\n\n要使用concurrencyC++/WinRT 功能,我必须在 MainFrm.c 源文件顶部附近添加几个包含文件。
// ADD_ON: include files for using the concurrency namespace.\n#include <experimental\\resumable>\n#include <pplawait.h>\n\n#pragma comment(lib, "windowsapp")\n#include "winrt/Windows.Foundation.h"\n#include "winrt/Windows.Web.Syndication.h"\nRun Code Online (Sandbox Code Playgroud)\n\n此外,我必须修改解决方案的属性以指定 C++17 和其他编译器选项,其/await在下面的屏幕截图中用蓝色箭头标记。\n
co_await与 MFC 和 C++/WinRT 一起使用
根据 @IInspectable 的有用评论,我研究了 Visual Studio 2017 和 MFC 的协程。我一直对它们很好奇,但似乎我无法想出任何可以在使用时不出现错误的编译器co_await。
然而,从 @IInspectable 评论中的链接开始,我找到了此 YouTube 视频的链接,CppCon 2016: Kenny Kerr & James McNellis \xe2\x80\x9cPutting Coroutines to Work with the Windows Runtime",其中有一个源代码示例时间 10:28 我终于能够想出一些可以编译并运行的东西了。
\n\n我创建了以下函数,然后用它来替换上面的源代码,concurrency::create_task()并将 lambda 替换为对以下函数的函数调用。函数调用很简单,myTaskMain(this);替换方法concurrency::create_task([this]() {中的 lambda int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct),然后在函数体上方添加以下源代码OnCreate()。
winrt::Windows::Foundation::IAsyncAction myTaskMain(CMainFrame *p)\n{\n winrt::Windows::Foundation::Uri uri(L"http://kennykerr.ca/feed");\n winrt::Windows::Web::Syndication::SyndicationClient client;\n winrt::Windows::Web::Syndication::SyndicationFeed feed = co_await client.RetrieveFeedAsync(uri);\n\n Sleep(5000);\n for (winrt::Windows::Web::Syndication::SyndicationItem item : feed.Items())\n {\n winrt::hstring title = item.Title().Text();\n p->SendMessageToOutputWnd(WM_APP, COutputWnd::OutputBuild, (LPARAM)title.c_str()); // print a string to an output window in the output pane.\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我根据被替换的来源做了两个更改concurrency::create_task():
winrt::init_apartment();和winrt::uninit_apartment();因为使用它们触发了异常并且删除它们似乎没有什么区别Sleep(5000);after 后,co_await将其留在导致函数休眠 5 秒的位置,这意味着 UI 线程休眠了 5 秒我通过调试器发现,在myTaskMain()调用该函数时,该函数立即返回,并且 UI 线程继续运行,而协程在后台执行。UI 立即显示,大约五秒钟后,发生了其他操作,更新了输出窗口的“构建”选项卡中的类视图树和 RSS 提要列表。
注意#1:我在其他测试中遇到的另一件事是 UI 将冻结几秒钟(菜单不起作用)。这似乎是因为Sleep(5000);表明 后面的代码co_await正在主 UI 线程上运行。在我开始探索使用winrt::apartment_context ui_thread;捕获主 UI 线程上下文以便然后使用将co_await ui_thread;我的协程线程返回到主 UI 线程上下文之后,应用程序行为发生了这种变化。
可能发生的情况是client.RetrieveFeedAsync(uri)立即得到满足,没有延迟,可能来自缓存,因此不是将任务推送到另一个线程然后返回给调用者,而是co_await立即返回结果并且函数myTaskMain()能够立即继续使用当前线程哪个是主 UI 线程?
我注意到在 Visual Studio 2017 中co_await使用的 withclient.RetrieveFeedAsync(uri)是绿色的,而co_await使用的 withco_await ui_thread;是蓝色的。将鼠标悬停在绿色上,co_await我会收到一个工具提示,表明这是 的不同版本co_await。
注意#2:有一个 C++/WinRT 函数可以移动到后台线程上下文,winrt::resume_background()该函数可以与co_await. 如果我通过用以下两行代码替换调用后的myTaskMain()代码行来修改上述函数,以将线程上下文推送到后台线程,则我不会看到冻结(UI 响应菜单选择)并且大约 15 秒后,RSS 提要文本行将显示在输出窗口的“构建”选项卡中。Sleep(5000);client.RetrieveFeedAsync(uri)
co_await winrt::resume_background(); // switch context to background thread\n\nSleep(15000);\nRun Code Online (Sandbox Code Playgroud)\n\nconcurrency::task<>使用以下方法滚动异步任务co_await
我好奇的一件事是能够创建自己的异步任务,我可以将其co_await与 C++/WinRT 的异步类型函数类似地使用。我花了一些时间搜索,直到最终找到这篇文章Concurrency and asynchronous Operations with C++/WinRT ,其中有一个名为Asychronously return a non-Windows-Runtime type 的部分。
这是一个简单的演示函数,它使用 lambda 创建concurrency::task<>并返回然后与 一起使用的任务co_await。这个特定的 lambda 返回一个,int因此该函数被定义为一个返回一个的任务int,concurrency::task<int>
concurrency::task<int> mySleepTaskAsync()\n{\n return concurrency::create_task([]() {\n Sleep(15000);\n return 5;\n });\n}\nRun Code Online (Sandbox Code Playgroud)\n\n然后将上述函数与co_await语句中的运算符一起使用,例如:
int jj = co_await mySleepTaskAsync();\nRun Code Online (Sandbox Code Playgroud)\n\n这将导致变量jj在等待 15 秒后值为 5。
上面的内容用在返回 a 的函数中,winrt::Windows::Foundation::IAsyncAction例如myTaskMain()上面的函数。
如果您愿意,也可以直接将 lambda 与co_awaitas 一起使用:
int jj = co_await concurrency::create_task([]() {\n Sleep(15000);\n return 5;\n});\nRun Code Online (Sandbox Code Playgroud)\n\n或者你可以有一个正常的功能,例如:
\n\nint mySleepTaskAsyncInt()\n{\n Sleep(15000);\n return 5;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n然后将其与co_awaitusing一起使用concurrency::task<>,如下所示:
int jj = co_await concurrency::create_task(mySleepTaskAsyncInt);\nRun Code Online (Sandbox Code Playgroud)\n\nstd::async使用以下方法滚动异步任务co_await
虽然std::thread不与 一起使用co_await,会导致编译错误,但您可以std::async与 一起使用co_await。原因是运算符要求的返回值类型以及, a的返回值和, a的返回值co_await之间的差异。std::threadstd::threadstd::asyncstd::future<>
该co_await运算符要求它正在操作的变量是 a std::future<>,具有get()从线程检索结果的方法,并且是 Awaitable 。
#include <future>\n\nint mySleepTaskAsyncInt()\n{\n Sleep(7000);\n return 5;\n}\n\nwinrt::Windows::Foundation::IAsyncAction myTaskMain(CMainFrame *p)\n{\n auto t1 = co_await std::async (std::launch::async, mySleepTaskAsyncInt);\n\n // do something with the variable t1\n}\nRun Code Online (Sandbox Code Playgroud)\n\n滚动异步任务 withstd::packaged_task<>和std::future<>withco_await
由于co_await需要一个 Awaitable 对象,创建此类对象的另一种方法是创建一个任务,std::packaged_task<>然后启动该任务并使用get_future()该任务的方法来获取std::future<>,然后可以使用co_await。
例如,我们可以使用以下简单的函数来创建一个任务包,开始执行任务,然后返回一个std::future<>. 然后我们可以使用这个函数作为操作符的目标co_await来实现协程。
#include <future>\n\n\nstd::future<int> mySleepTaskStdFutureInt()\n{\n // create the task to prepare it for running.\n std::packaged_task<int()> task([]() {\n Sleep(7000);\n return 455; // return an int value\n });\n\n // start the task running and return the future\n return task(), task.get_future();\n}\nRun Code Online (Sandbox Code Playgroud)\n\n然后在我们的源代码中我们将使用类似于以下的函数:
\n\nint jkjk = co_await mySleepTaskStdFutureInt();\nRun Code Online (Sandbox Code Playgroud)\n\n该return语句使用逗号运算符引入序列点,以便我们启动任务运行,然后调用正在get_future()运行的任务上的方法。方法的结果get_future()astd::future<int>是函数实际返回的结果。
创建的任务必须从使用变量的调用等函数std::packaged_task() 开始。如果您不启动任务,则std::future<>函数返回的变量将永远不会有变量,并且co_await等待 Awaitable 完成并提供值的变量将永远不会触发。结果是 your 之后的源co_await不会被执行,因为co_await永远不会被触发。
发电机与co_yield和std::experimental::generator<type>
在调查时,co_await我发现co_yield它用于返回一个值作为一组值生成器的一部分。在 Visual Studio 2017 中使用需要包含co_yield头文件。experimental/generator这是生成一系列整数的生成器的简单示例。
#include <experimental/generator>\n\nstd::experimental::generator<int> makeSomeInts(int kCount)\n{\n for (int i = 0; i < kCount; i++) {\n co_yield i;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这个函数可以与范围 for 一起使用,如下所示:
\n\nfor (int kkk : makeSomeInts(10)) {\n // code that uses the variable kkk which contains\n // an int from the generated range 0 up to be not including 10.\n}\nRun Code Online (Sandbox Code Playgroud)\n\n上述循环将对 0 到 9 之间的每个整数值执行。
\n\n更复杂的消息:更新 ClassView pan
\n\n我还对 ClassView 树控件进行了实验,以提供一种执行最基本操作的简单方法:创建一个初始树控件并向其添加。
\n\n在CClassView文件 ClassView.h 的类中,我添加了以下数据结构。顺便说一句,完成后,我意识到这可能是放置的错误位置,因为该类CFileView使用相同的树结构,因此相同的方法适用于这两个窗格。无论如何,我添加了以下内容:
// ADD_ON: enumeration listing the various types of tree control icons which\n// correspond to the position of a control in the tree.\n// choose either classview_hc.bmp or classview.bmp for the bitmap strip that\n// contains the 7 icons we are using for the images in our tree control.\n// icons are standard size in height and width (15x15 pixels) in the order of:\n// - main root icon\n// - tree node icon which can be opened to show nodes beneath it\n// - folder icon which is used to indicate a folder\n// - method icon indicating a method of a class\n// - locked method icon\n// - member variable icon\n// - locked member variable icon\n\nenum IconList { MainRoot = 0, TreeNode = 1, FolderNode = 2, MethodNode = 3, MethodLockedNode = 4, MemberNode = 5, MemberLockedNode = 6 };\n\n// ADD_ON: struct used to contain the necessary data for a node in the tree control.\nstruct ItemToInsert {\n std::wstring label; // text to be displayed with the node.\n int nImage; // zero based offset of the node\'s icon in the image, one of enum IconList above.\n int nSelectedImage; // zero based offset of the node\'s icon in the image, one of enum IconList above.\n};\nRun Code Online (Sandbox Code Playgroud)\n\n我创建了一个消息处理程序,并将其添加到 ClassView.cpp 中的消息映射中
\n\nON_MESSAGE(WM_APP, OnAddItemsToPane)\nRun Code Online (Sandbox Code Playgroud)\n\n并添加了实际的消息处理程序本身以及执行实际处理的辅助函数。
\n\n// ADD_ON: function for filling in the ClassView pane using an array of the\n// struct ItemToInsert above. array is terminated by an entry with\n// all zeros as in { _T(""), 0, 0 }\nvoid CClassView::AddItemsToPane(CViewTree &xwndClassView, void *xrayp)\n{\n\n if (xrayp == 0) return;\n\n // the images are icons that are laid out in a line of icons within a single bitmap image.\n // see class method OnChangeVisualStyle() for when the bitmap image is loaded and then\n // divided up into sections, 0 through 6, of the single bitmap image loaded.\n // see classview.bmp and classview_hc.bmp in the ResourceFiles list.\n\n\n HTREEITEM hRoot = xwndClassView.GetRootItem();\n HTREEITEM hClass = 0;\n ItemToInsert *xray = (ItemToInsert *)xrayp;\n\n for (int i = 0; xray[i].label.size() != 0; i++) {\n switch (xray[i].nImage) {\n case MainRoot:\n hRoot = xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage);\n xwndClassView.SetItemState(hRoot, TVIS_BOLD, TVIS_BOLD);\n xwndClassView.Expand(hRoot, TVE_EXPAND);\n break;\n case TreeNode:\n hClass = xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage, hRoot);\n break;\n case FolderNode:\n hClass = xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage, hRoot);\n break;\n case MethodNode:\n case MethodLockedNode:\n case MemberNode:\n case MemberLockedNode:\n xwndClassView.InsertItem(xray[i].label.c_str(), xray[i].nImage, xray[i].nSelectedImage, hClass);\n break;\n default:\n break;\n }\n }\n}\n\n// ADD_ON: message handler for the WM_APP message containing an array of the\n// struct ItemToInsert above. Uses method AddItemsToPane().\nL
| 归档时间: |
|
| 查看次数: |
4986 次 |
| 最近记录: |