QuakeGod
2023-10-20 6fa60de2b0d0237054aa7571191df0f291838031
提交 | 用户 | age
0ed438 1 
Q 2 // MTerm1.cpp: 定义应用程序的类行为。
3 //
4
5 #include "pch.h"
6 #include "framework.h"
7 #include "afxwinappex.h"
8 #include "afxdialogex.h"
9 #include "MTerm1.h"
10 #include "MainFrm.h"
11
12 #include "ChildFrm.h"
13 #include "MTerm1Doc.h"
14 #include "MTerm1View.h"
418cb3 15 #include "MTerm1LdsView.h"
Q 16 #include "MTerm1BldView.h"
17 #include "MTerm1BnlView.h"
18
19 #include "MTerm1CoilView.h"
20 #include "MTerm1DataView.h"
21
22 #include "MTerm1TestView.h"
23 #include "MTerm1CtrlView.h"
24 #include "MTerm1ProgTxt.h"
25
26
27
28 #include "MTerm1CommDevView.h"
0ed438 29
Q 30 #ifdef _DEBUG
31 #define new DEBUG_NEW
32 #endif
33
34 #include <devguid.h>
35 #include <SetupAPI.h>
418cb3 36 //#include "HvSerialPort.h"
Q 37 //#include "CAnsiParser.h"
38 //#include "CDataParser1.h"
39 //#include "KLink.h"
0ed438 40
Q 41 #include <eh.h>
42 #include <dbghelp.h>
43 #pragma comment(lib,"dbghelp.lib")
44 #pragma comment(lib,"SetupAPI.lib")
418cb3 45 #pragma comment(lib,"gdiplus.lib")
0ed438 46
418cb3 47 #include "../KLink1/KLink.h"
Q 48
49 #pragma comment(lib,"KLink1.lib")
0ed438 50 MHash MyCfg1;
418cb3 51 Logger myLogger1;
0ed438 52
Q 53 // CMTerm1App
54
55 BEGIN_MESSAGE_MAP(CMTerm1App, CWinAppEx)
56     ON_COMMAND(ID_APP_ABOUT, &CMTerm1App::OnAppAbout)
57     // 基于文件的标准文档命令
58 //    ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew)
59     ON_COMMAND(ID_FILE_NEW, &OnFileNew)
60     ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen)
61     // 标准打印设置命令
62     ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinAppEx::OnFilePrintSetup)
418cb3 63 //    ON_COMMAND(ID_COMUNICATION_SET, &CMTerm1App::OnComunicationSet)
Q 64     ON_COMMAND(ID_ENV_SET, &CMTerm1App::OnEnvSet)
0ed438 65 END_MESSAGE_MAP()
Q 66
67
68 // CMTerm1App 构造
69
70 CMTerm1App::CMTerm1App() noexcept
71 {
72     m_bHiColorIcons = TRUE;
73     // 支持重新启动管理器
74     m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_ALL_ASPECTS;
75 #ifdef _MANAGED
76     // 如果应用程序是利用公共语言运行时支持(/clr)构建的,则: 
77     //     1) 必须有此附加设置,“重新启动管理器”支持才能正常工作。
78     //     2) 在您的项目中,您必须按照生成顺序向 System.Windows.Forms 添加引用。
79     System::Windows::Forms::Application::SetUnhandledExceptionMode(System::Windows::Forms::UnhandledExceptionMode::ThrowException);
80 #endif
81
82     // TODO: 将以下应用程序 ID 字符串替换为唯一的 ID 字符串;建议的字符串格式
83     //为 CompanyName.ProductName.SubProduct.VersionInformation
418cb3 84     SetAppID(_T("MultiTerminal2.AppID.NoVersion"));
0ed438 85
Q 86     // TODO: 在此处添加构造代码,
87     // 将所有重要的初始化放置在 InitInstance 中
88 }
89
90 // 唯一的 CMTerm1App 对象
91
92 CMTerm1App theApp;
93
94
95 // CMTerm1App 初始化
96
97 BOOL CMTerm1App::InitInstance()
98 {
99     // 如果一个运行在 Windows XP 上的应用程序清单指定要
100     // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
101     //则需要 InitCommonControlsEx()。  否则,将无法创建窗口。
102     INITCOMMONCONTROLSEX InitCtrls;
103     InitCtrls.dwSize = sizeof(InitCtrls);
104     // 将它设置为包括所有要在应用程序中使用的
105     // 公共控件类。
106     InitCtrls.dwICC = ICC_WIN95_CLASSES;
107     InitCommonControlsEx(&InitCtrls);
108
109     CWinAppEx::InitInstance();
110
418cb3 111     if (!AfxSocketInit())
Q 112     {
113         AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
114         return FALSE;
115     }
0ed438 116
Q 117     // 初始化 OLE 库
118     if (!AfxOleInit())
119     {
120         AfxMessageBox(IDP_OLE_INIT_FAILED);
121         return FALSE;
122     }
123
124     AfxEnableControlContainer();
125
418cb3 126     EnableTaskbarInteraction(FALSE);
0ed438 127
Q 128     // 使用 RichEdit 控件需要 AfxInitRichEdit2()
418cb3 129      AfxInitRichEdit2();
0ed438 130
Q 131     // 标准初始化
132     // 如果未使用这些功能并希望减小
133     // 最终可执行文件的大小,则应移除下列
134     // 不需要的特定初始化例程
135     // 更改用于存储设置的注册表项
136     // TODO: 应适当修改该字符串,
137     // 例如修改为公司或组织名
138     SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
139     LoadStdProfileSettings(4);  // 加载标准 INI 文件选项(包括 MRU)
140
141
142     // 注册应用程序的文档模板。  文档模板
143     // 将用作文档、框架窗口和视图之间的连接
418cb3 144 //    CMultiDocTemplate* pDocTemplate;
0ed438 145     pDocTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
Q 146         RUNTIME_CLASS(CMTerm1Doc),
147         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
148         RUNTIME_CLASS(CMTerm1View));
149     if (!pDocTemplate)
150         return FALSE;
151     AddDocTemplate(pDocTemplate);
418cb3 152 ///*
Q 153 //    CMultiDocTemplate * m_pNewDocTemplate;
154 ///*
155     m_pLdsViewTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
156         RUNTIME_CLASS(CMTerm1Doc),
157         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
158         RUNTIME_CLASS(CMTerm1LdsView));
159     if (!m_pLdsViewTemplate)
160         return FALSE;
161     AddDocTemplate(m_pLdsViewTemplate);
0ed438 162
418cb3 163     m_pBldViewTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
Q 164         RUNTIME_CLASS(CMTerm1Doc),
165         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
166         RUNTIME_CLASS(CMTerm1BldView));
167     if (!m_pBldViewTemplate)
168         return FALSE;
169     AddDocTemplate(m_pBldViewTemplate);
170
171     m_pBnlViewTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
172         RUNTIME_CLASS(CMTerm1Doc),
173         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
174         RUNTIME_CLASS(CMTerm1BnlView));
175     if (!m_pBnlViewTemplate)
176         return FALSE;
177     AddDocTemplate(m_pBnlViewTemplate);
178
179     m_pCommDevViewTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
180         RUNTIME_CLASS(CMTerm1Doc),
181         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
182         RUNTIME_CLASS(CMTerm1CommDevView));
183     if (!m_pCommDevViewTemplate)
184         return FALSE;
185     AddDocTemplate(m_pCommDevViewTemplate);
186
187
188     m_pCtrlViewTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
189         RUNTIME_CLASS(CMTerm1Doc),
190         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
191         RUNTIME_CLASS(CMTerm1CtrlView));
192     if (!m_pCtrlViewTemplate)
193         return FALSE;
194     AddDocTemplate(m_pCtrlViewTemplate);
195
196     m_pProgViewTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
197         RUNTIME_CLASS(CMTerm1Doc),
198         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
199         RUNTIME_CLASS(CMTerm1ProgTxt));
200     if (!m_pProgViewTemplate)
201         return FALSE;
202     AddDocTemplate(m_pProgViewTemplate);
203
204     m_pCoilViewTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
205         RUNTIME_CLASS(CMTerm1Doc),
206         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
207         RUNTIME_CLASS(CMTerm1CoilView));
208     if (!m_pCoilViewTemplate)
209         return FALSE;
210     AddDocTemplate(m_pCoilViewTemplate);
211
212     m_pDataViewTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
213         RUNTIME_CLASS(CMTerm1Doc),
214         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
215         RUNTIME_CLASS(CMTerm1DataView));
216     if (!m_pDataViewTemplate)
217         return FALSE;
218     AddDocTemplate(m_pDataViewTemplate);
219
220
221
222
223
224     m_pNewDocTemplate = new CMultiDocTemplate(IDR_MTerm1TYPE,
225         RUNTIME_CLASS(CMTerm1Doc),
226         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
227         RUNTIME_CLASS(CMTerm1TestView));
228     if (!m_pNewDocTemplate)
229         return FALSE;
230     AddDocTemplate(m_pNewDocTemplate);
231 //*/
0ed438 232     // 创建主 MDI 框架窗口
Q 233     CMainFrame* pMainFrame = new CMainFrame;
234     if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
235     {
236         delete pMainFrame;
237         return FALSE;
238     }
239     m_pMainWnd = pMainFrame;
240
241
242     // 分析标准 shell 命令、DDE、打开文件操作的命令行
243     CCommandLineInfo cmdInfo;
244     ParseCommandLine(cmdInfo);
245
418cb3 246     if (cmdInfo.m_nShellCommand == CCommandLineInfo::FileNew)
Q 247         cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
0ed438 248
Q 249     // 调度在命令行中指定的命令。  如果
250     // 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
251     if (!ProcessShellCommand(cmdInfo))
252         return FALSE;
253     // 主窗口已初始化,因此显示它并对其进行更新
254     pMainFrame->ShowWindow(m_nCmdShow);
255     pMainFrame->UpdateWindow();
256     return TRUE;
257 }
258
259 int CMTerm1App::ExitInstance()
260 {
261     //TODO: 处理可能已添加的附加资源
262     AfxOleTerm(FALSE);
4ed7fc 263     //this->CleanState();
0ed438 264
Q 265     return CWinAppEx::ExitInstance();
266 }
267
268 // CMTerm1App 消息处理程序
269
270
271 // 用于应用程序“关于”菜单项的 CAboutDlg 对话框
272
273 class CAboutDlg : public CDialogEx
274 {
275 public:
276     CAboutDlg() noexcept;
277
278 // 对话框数据
279 #ifdef AFX_DESIGN_TIME
280     enum { IDD = IDD_ABOUTBOX };
281 #endif
282
283 protected:
284     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
285
286 // 实现
287 protected:
288     DECLARE_MESSAGE_MAP()
289 };
290
291 CAboutDlg::CAboutDlg() noexcept : CDialogEx(IDD_ABOUTBOX)
292 {
293 }
294
295 void CAboutDlg::DoDataExchange(CDataExchange* pDX)
296 {
297     CDialogEx::DoDataExchange(pDX);
298 }
299
300 BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
301 END_MESSAGE_MAP()
302
303 // 用于运行对话框的应用程序命令
304 void CMTerm1App::OnAppAbout()
305 {
306     CAboutDlg aboutDlg;
307     aboutDlg.DoModal();
308 }
309
310 // CMTerm1App 消息处理程序
418cb3 311
Q 312 void CMTerm1App::OnFileNew()
313 {
314     // TODO: 在此添加命令处理程序代码
315     POSITION ps = m_pDocManager->GetFirstDocTemplatePosition();
316     CDocTemplate *pDocTemplate = m_pDocManager->GetNextDocTemplate(ps);
317     pDocTemplate->OpenDocumentFile(NULL);
318 }
319
320
321
322 void CMTerm1App::OnComunicationSet()
323 {
324     // TODO: 在此添加命令处理程序代码
325 }
326
327
328 void CMTerm1App::OnEnvSet()
329 {
330     // TODO: 在此添加命令处理程序代码
331 }
0ed438 332
Q 333
334 int SysLog(CString s, int channel)
335 {
418cb3 336     myLogger1.LogTxt(s, channel);
Q 337     return 1;
338 }
339
340 int DbgLog(CString s, int channel)
341 {
342     myLogger1.LogTxt(s, channel);
0ed438 343     return 1;
Q 344 }
345
346 int PopupMessage(CString Msg, int channel)
347 {
348     Msg.Trim(_T("\r\n"));
349     SysLog(_T("报警信息: ") + Msg + _T("\r\n"), channel);
350     return 0;
351 }
352
353 void DoEvents()
354 {
355     MSG msg;
356     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
357     {
358         DispatchMessage(&msg);
359         TranslateMessage(&msg);
360     }
361 }
362
363 void dump_callstack(PCONTEXT pcontext, CString & infostr)
364 {
365 #ifdef _WIN64
366
367     STACKFRAME64 sf;
368     memset(&sf, 0, sizeof(STACKFRAME64));
369
370     sf.AddrPC.Offset = pcontext->Rip;
371     //context->
372     sf.AddrPC.Mode = AddrModeFlat;
373     sf.AddrStack.Offset = pcontext->Rsp;
374     sf.AddrStack.Mode = AddrModeFlat;
375     sf.AddrFrame.Offset = pcontext->Rbp;
376     sf.AddrFrame.Mode = AddrModeFlat;
377
378     DWORD machineType = //IMAGE_FILE_MACHINE_I386;
379         IMAGE_FILE_MACHINE_AMD64;
380 #elif defined _WIN32
381     STACKFRAME sf;
382     memset(&sf, 0, sizeof(STACKFRAME));
383
384     sf.AddrPC.Offset = pcontext->Eip;
385     //context->
386     sf.AddrPC.Mode = AddrModeFlat;
387     sf.AddrStack.Offset = pcontext->Esp;
388     sf.AddrStack.Mode = AddrModeFlat;
389     sf.AddrFrame.Offset = pcontext->Ebp;
390     sf.AddrFrame.Mode = AddrModeFlat;
391
392     DWORD machineType = //IMAGE_FILE_MACHINE_I386;
393         IMAGE_FILE_MACHINE_AMD64;
394 #endif
395     HANDLE hProcess = GetCurrentProcess();
396     HANDLE hThread = GetCurrentThread();
397
398     for (;;)
399     {
400         if (!StackWalk(machineType, hProcess, hThread, &sf, pcontext, 0, SymFunctionTableAccess, SymGetModuleBase, 0))
401         {
402             break;
403         }
404
405         if (sf.AddrFrame.Offset == 0)
406         {
407             break;
408         }
409         BYTE symbolBuffer[sizeof(SYMBOL_INFO) + 1024];
410         PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
411
412         pSymbol->SizeOfStruct = sizeof(symbolBuffer);
413         pSymbol->MaxNameLen = 1024;
414
415         DWORD64 symDisplacement = 0;
416         if (SymFromAddr(hProcess, sf.AddrPC.Offset, 0, pSymbol))
417         {
418             printf("Function : %s\n", pSymbol->Name);
419             infostr.AppendFormat(_T("Function : %S \r\n"), pSymbol->Name);
420         }
421         else
422         {
423             printf("SymFromAdd failed!\n");
424             //    infostr.AppendFormat(_T("SymFromAdd failed \r\n"),pSymbol->Name);
425         }
426
427         DWORD dwLineDisplacement;
428 #ifdef _WIN64
429         IMAGEHLP_LINEW64 lineInfoW = { sizeof(IMAGEHLP_LINEW64) };
430         if (SymGetLineFromAddrW(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfoW))
431         {
432             //        printf( "[Source File : %s]\n", lineInfo.FileName ); 
433             //        printf( "[Source Line : %u]\n", lineInfo.LineNumber ); 
434             CString s1(lineInfoW.FileName);
435             infostr.AppendFormat(_T("File %s Line %d\r\n"), s1.Mid(s1.ReverseFind(_T('\\')) + 1, 99), lineInfoW.LineNumber);
436         }
437         else
438         {
439             printf("SymGetLineFromAddr failed!\n");
440             //    infostr.Append(_T("SymGetLineFromAddr failed!\r\n"));
441         }
442 #elif defined _WIN32
443         IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
444         if (SymGetLineFromAddr(hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo))
445         {
446             //        printf( "[Source File : %s]\n", lineInfo.FileName ); 
447             //        printf( "[Source Line : %u]\n", lineInfo.LineNumber ); 
448             CString s1(lineInfo.FileName);
449             infostr.AppendFormat(_T("File %s Line %d\r\n"), s1.Mid(s1.ReverseFind(_T('\\')) + 1, 99), lineInfo.LineNumber);
450         }
451         else
452         {
453             printf("SymGetLineFromAddr failed!\n");
454             //    infostr.Append(_T("SymGetLineFromAddr failed!\r\n"));
455         }
456 #endif
457
458     }
459 }
460 DWORD excep_filter(LPEXCEPTION_POINTERS lpEP)
461 {
462     /**//// init dbghelp.dll
463     CString s1;
464     if (SymInitialize(GetCurrentProcess(), NULL, TRUE))
465     {
466         printf("Init dbghelp ok.\n");
467     }
468
469     dump_callstack(lpEP->ContextRecord, s1);
470
471     if (SymCleanup(GetCurrentProcess()))
472     {
473         printf("Cleanup dbghelp ok.\n");
474     }
475
476     return EXCEPTION_EXECUTE_HANDLER;
477 }
478
479 void Trans_Tunc(unsigned int n, EXCEPTION_POINTERS* pExp)
480 {
481     //    pExp->ExceptionRecord.
482     //    throw SE_Exception(n);
483     CString s1;
484     if (SymInitialize(GetCurrentProcess(), NULL, TRUE))
485     {
486         printf("Init dbghelp ok.\n");
487         //        s1=_T("Init dbghelp ok.\r\n");
488     }
489
490     dump_callstack(pExp->ContextRecord, s1);
491
492     if (SymCleanup(GetCurrentProcess()))
493     {
494         printf("Cleanup dbghelp ok.\n");
495         //        s1.Append(_T("Cleanup dbghelp ok.\n"));
496     }
497     throw SE_Exception(n, pExp->ExceptionRecord->ExceptionAddress, s1);
498 }
499
500 int DisplayException(CString File, int Line, CString Func, CString Sentence, SE_Exception &e)
501 {
502     CString Serror;
503     CString s1;
504     s1.Format(_T("程序发生SE异常 代码 %08X 地址 %p \r\nFile: %s \r\nLine: %d \r\nFunc %s \r\n语句 %s \r\n 信息\r\n %s \r\n"), e.getSeNumber(), e.getAddress(), File, Line, Func, Sentence, e.getInfoStr());;
505     PopupMessage(s1);
506     return 0;
507 }
508 int DisplayException(CString File, int Line, CString Func, CString Sentence, CException *e)
509 {
510     CString Serror;
511     CString s1;
512     e->GetErrorMessage(Serror.GetBuffer(256), 256);
513     Serror.ReleaseBuffer();
514     s1.Format(_T("程序发生C++异常 %s 在File: %s Line: %d Func %s Sentence %s \r\n"), Serror, File, Line, Func, Sentence);
515     PopupMessage(s1);
516     return 0;
517 }
518 int DisplayException(CString File, int Line, CString Func, CString Sentence)
519 {
520     CString s1;
521     s1.Format(_T("程序发生未知错误 File: %s Line: %d Func %s Sentence %s \r\n"), File, Line, Func, Sentence);
522     PopupMessage(s1);
523     return 0;
524 }
525
526
527 int LoadMyConfig()
528 {
529     CString ConfigFilePathName;
530     ConfigFilePathName = _T("./Settings.ini");
531     int j = MyCfg1.LoadFromFile(ConfigFilePathName);
532     return j;
533 }
534
535 int SaveMyConfig()
536 {
537     CString ConfigFilePathName;
538     ConfigFilePathName = _T("./Settings.ini");
539     int j = MyCfg1.SaveToFile(ConfigFilePathName);
540     return j;
541 }
542
543
544 //得到COMx的名字
545 //namebuf:用于存放名字的缓冲区
546 //bufsize:缓冲区大小
547 //comx:要查找的COM编号.例如:COM1,COM2,COM3....
548 //返回值:0,成功找到了.
549 //       1,失败.
550 int get_com_name(CString comx, CString &namebuf)
551 {
552     HDEVINFO hdinfo;
553     int res = 0;
554     SP_DEVINFO_DATA   hddevinfo = { sizeof(SP_DEVINFO_DATA) };
555     hdinfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, 0, 0, DIGCF_PRESENT);//获取PORTS类别的已安装设备信息
556     if (hdinfo != INVALID_HANDLE_VALUE)//获取成功
557     {
558         for (int i = 0; SetupDiEnumDeviceInfo(hdinfo, i, &hddevinfo); i++)//轮询所有已安装设备
559         {
560             SetupDiGetDeviceRegistryProperty(hdinfo, &hddevinfo, SPDRP_FRIENDLYNAME, 0, (PBYTE)(namebuf.GetBuffer(2048)), 2048, 0);//获得单个装置的详细资料
561             namebuf.ReleaseBuffer();
562             if (namebuf.Find(comx) != -1) {
563                 res = 1; break;
564             }
565             //char *t;
566             //t = strstr(namebuf, comx);
567             //if (t)
568             //{
569             //    t--;
570             //    *t = '\0';//添加结束符,作用就是把"(COMX)"这段字符去掉
571             //    res = 0;
572             //    break;//成功找到了COMx的名字
573             //}
574         }
575     }
576     return res;
577 }
578
418cb3 579 CString & intToString(int num, int digit)
0ed438 580 {
Q 581     static CString Str1;
418cb3 582     if (digit != 0) {
Q 583         CString str2;
584         str2.Format(_T("%%%dd"), digit);
585         Str1.Format(str2, num);
586
587     }else {
588         Str1.Format(_T("%d"), num);
589     }
590
0ed438 591     int k = Str1.GetLength();
Q 592     int j = (k - 1) / 3;    //逗号个数
593     int l = k - j * 3; //起始位置
594     for (int i = l; i < k + j; i += 4)
595     {
596         Str1.Insert(i, _T(","));
597     }
598
599     return Str1;
600 }
601
602 CString & intToBinString(int num, int digits)
603 {
604     static CString Str1;
605     Str1.Empty();
606     int mask = 1 << (digits - 1);
607
608     for (int i = 0; i < digits; i++)
609     {
610         if (num&mask) Str1.Append(_T("*"));
611         else Str1.Append(_T("."));
612         mask >>= 1;
613     }
614     return Str1;
418cb3 615 }