【开发环境】:MSCE-U14 + VS2017
我们在开发一些需要长时间运行的程序时希望给出一个进度条表示运行的进度,这样不至于让使用者等待得太焦虑。那么MDL中存在有现成的completionBar相关函数,当然还有busyBar和trackBar两种进度条。busyBar会一直显示一个不断变换的条状图形表示程序在运行,trackBar能实现同时两个进度条(一个表示子任务,一个表示总任务)。本文集中展示completionBar的使用。
请看下面的代码:
void completionBarSingleThreadTest() { MSDialogP cmplDbP = mdlDialog_completionBarOpen(L"completionBar Test..."); mdlDialog_completionBarUpdate(cmplDbP, NULL, 0); for (int percent = 0; percent<101; percent++) { WString tmpStr; for (long i = 0; i < 1000000L; i++) tmpStr.Sprintf(L"Single Thread Demo: my rand string = %d", rand()); mdlDialog_completionBarUpdate(cmplDbP, tmpStr.GetWCharCP(), percent); } mdlDialog_completionBarClose(cmplDbP); }
这段代码一开始调用函数mdlDialog_completionBarOpen打开一个内置的对话框,然后在for循环中调用mdlDialog_completionBarUpdate去更新进度条的百分比。最后执行完后调用mdlDialog_completionBarClose关闭进度条对话框。
在许多情况下,这段代码都会出现卡死在某个进度的地方不继续更新,直到for循环彻底结束后进度条对话框就直接关闭了。
下面是一个利用多线程加上定时器的版本,效果上非常好,避免了卡死情况的发生。在我们的进度条工作过程中甚至您还可以继续操作MicroStation软件。
#include <Bentley\BeThread.h> int g_percent = 0; long g_rand = 0; MSDialogP g_completeBarDbP; unsigned _stdcall myWorkThread(void* arg) { for (g_percent = 0; g_percent < 101; g_percent++) { WString tmpStr; for (long i = 0; i < 1000000L; i++) { g_rand = g_rand + (long)rand(); tmpStr.Sprintf(L"my rand string = %d", rand()); } } return 0; } void timerFunc(intptr_t timerArg, int timerHandle) { if (g_percent < 100) { if (::GetAsyncKeyState(0x1B)) //Press ESC to interrupt the bar { mdlSystem_cancelTimer(timerHandle); mdlDialog_completionBarClose(g_completeBarDbP); } WPrintfString msg(L"MultiThread Demo: Cumulative random = %d", g_rand); mdlDialog_completionBarUpdate(g_completeBarDbP, msg.GetWCharCP(), g_percent); } else { mdlSystem_cancelTimer(timerHandle); mdlDialog_completionBarClose(g_completeBarDbP); } } void completionBarMultiThreadTest() { BeThreadUtilities::StartNewThread(50 * 1024, myWorkThread, NULL); g_completeBarDbP = mdlDialog_completionBarOpen(L"completionBar Test..."); mdlDialog_completionBarUpdate(g_completeBarDbP, NULL, 0); mdlSystem_setTimerFunction(NULL, 16, timerFunc, 0, true); }
下面对这段代码做一些解释:
- 声明了三个全局变量用来在多个函数间共享数据。您当然可以封装成一个类,这样避免全局变量的使用。本例为了简化,并未采用C++类的设计方法;
- myWorkThread为您的工作线程,长时间的循环工作放入该函数中执行即可;
- timerFunc为一个时钟回调函数,我们每隔一秒钟调用该函数一次。在该函数中首先判断用户是否按了ESC键,如果按了的话就取消时钟并关闭进度条对话框,否则就更新进度条。如果进度条完成,就取消时钟并关闭进度条对话框;
- 在主函数completionBarMultiThreadTest中,首先调用BeThreadUtilities对象下的StartNewThread启动我们的工作进程myWorkThread。然后调用函数mdlDialog_completionBarOpen启动进度条功能。最后调用mdlSystem_setTimerFunction启动时钟函数。
程序运行效果如下图所示: