DebugProgramDlg.cpp
上传用户:yll456
上传日期:2022-03-08
资源大小:19064k
文件大小:15k
源码类别:

钩子与API截获

开发平台:

Visual C++

  1. // DebugProgramDlg.cpp : 实现文件
  2. //
  3. #include "stdafx.h"
  4. #include "DebugProgram.h"
  5. #include "DebugProgramDlg.h"
  6. #include "tchar.h"
  7. #include "PSAPI.h"
  8. #include "pe.h"
  9. #pragma comment(lib,"psapi.lib");
  10. CDebugProgramDlg  *tdlg;
  11. #define MAX_MODULE  256
  12. typedef struct _ModuleInfo
  13. {
  14. DWORD dwFileSize;
  15. TCHAR strFilePath[MAX_PATH];
  16. TCHAR strFileTime[32];
  17. } MODULE_INFO;
  18. TCHAR * PrintProcessNameAndID( DWORD processID )
  19. {
  20.     TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
  21.     // Get a handle to the process.
  22.     HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
  23.                                    PROCESS_VM_READ,
  24.                                    FALSE, processID );
  25.     // Get the process name.
  26.     if (NULL != hProcess )
  27.     {
  28.         HMODULE hMod;
  29.         DWORD cbNeeded;
  30.         if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), 
  31.              &cbNeeded) )
  32.         {
  33.             GetModuleBaseName( hProcess, hMod, szProcessName, 
  34.                                sizeof(szProcessName)/sizeof(TCHAR) );
  35.         }
  36.     }
  37.     // Print the process name and identifier.
  38.   //  _tprintf( TEXT("%s  (PID: %u)n"), szProcessName, processID );
  39.     CloseHandle( hProcess );
  40. return szProcessName;
  41. }
  42. #ifdef _DEBUG
  43. #define new DEBUG_NEW
  44. #endif
  45. // 用于应用程序“关于”菜单项的 CAboutDlg 对话框
  46. class CAboutDlg : public CDialog
  47. {
  48. public:
  49. CAboutDlg();
  50. // 对话框数据
  51. enum { IDD = IDD_ABOUTBOX };
  52. protected:
  53. virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
  54. // 实现
  55. protected:
  56. DECLARE_MESSAGE_MAP()
  57. };
  58. CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
  59. {
  60. }
  61. void CAboutDlg::DoDataExchange(CDataExchange* pDX)
  62. {
  63. CDialog::DoDataExchange(pDX);
  64. }
  65. BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
  66. END_MESSAGE_MAP()
  67. // CDebugProgramDlg 对话框
  68. CDebugProgramDlg::CDebugProgramDlg(CWnd* pParent /*=NULL*/)
  69. : CDialog(CDebugProgramDlg::IDD, pParent)
  70. , m_ifChecked(FALSE)
  71. {
  72. m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  73. }
  74. void CDebugProgramDlg::DoDataExchange(CDataExchange* pDX)
  75. {
  76. CDialog::DoDataExchange(pDX);
  77. DDX_Control(pDX, IDC_COMBO_PROCESSLIST, m_ProcessList);
  78. DDX_Control(pDX, IDC_OPENFILENAME, m_OpenFileName);
  79. DDX_Control(pDX, IDC_LIST_BREAKPOINT, m_BreakPointList);
  80. DDX_Check(pDX, IDC_IFATTACHPROCESS, m_ifChecked);
  81. DDX_Control(pDX, IDC_IFATTACHPROCESS, m_ctlCheckAttach);
  82. }
  83. BEGIN_MESSAGE_MAP(CDebugProgramDlg, CDialog)
  84. ON_WM_SYSCOMMAND()
  85. ON_WM_PAINT()
  86. ON_WM_QUERYDRAGICON()
  87. //}}AFX_MSG_MAP
  88. //ON_BN_CLICKED(IDC_BUTTON1, &CDebugProgramDlg::OnBnClickedButton1)
  89. // ON_BN_CLICKED(IDC_BUTTON2, &CDebugProgramDlg::OnBnClickedButton2)
  90. ON_BN_CLICKED(IDC_BUTTON3, &CDebugProgramDlg::OnBnClickedButton3)
  91. ON_BN_CLICKED(IDC_SELECTBUTTON, &CDebugProgramDlg::OnBnClickedSelectbutton)
  92. ON_BN_CLICKED(IDC_REFRESHBUTTON, &CDebugProgramDlg::OnBnClickedRefreshbutton)
  93. ON_BN_CLICKED(IDC_IFATTACHPROCESS, &CDebugProgramDlg::OnBnClickedIfattachprocess)
  94. ON_BN_CLICKED(IDC_STARTBUTTON, &CDebugProgramDlg::OnBnClickedStartbutton)
  95. END_MESSAGE_MAP()
  96. // CDebugProgramDlg 消息处理程序
  97. BOOL CDebugProgramDlg::OnInitDialog()
  98. {
  99. CDialog::OnInitDialog();
  100. // 将“关于...”菜单项添加到系统菜单中。
  101. // IDM_ABOUTBOX 必须在系统命令范围内。
  102. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  103. ASSERT(IDM_ABOUTBOX < 0xF000);
  104. CMenu* pSysMenu = GetSystemMenu(FALSE);
  105. if (pSysMenu != NULL)
  106. {
  107. CString strAboutMenu;
  108. strAboutMenu.LoadString(IDS_ABOUTBOX);
  109. if (!strAboutMenu.IsEmpty())
  110. {
  111. pSysMenu->AppendMenu(MF_SEPARATOR);
  112. pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  113. }
  114. }
  115. // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
  116. //  执行此操作
  117. SetIcon(m_hIcon, TRUE); // 设置大图标
  118. SetIcon(m_hIcon, FALSE); // 设置小图标
  119. DWORD aProcesses[1024], cbNeeded, cProcesses;
  120. TCHAR *ProcessName=new TCHAR[MAX_PATH];
  121. unsigned int i;
  122. unsigned int j=0;
  123. if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
  124. return -1;
  125. // Calculate how many process identifiers were returned.
  126. cProcesses = cbNeeded / sizeof(DWORD);
  127. // Print the name and process identifier for each process.
  128. for ( i = 0; i < cProcesses; i++ )
  129. {
  130. if( aProcesses[i] != 0 )
  131. {
  132. _stprintf(ProcessName,"%-32s",PrintProcessNameAndID( aProcesses[i] ) );
  133. if( _tcsstr(ProcessName,_T("<unknown>") ) == NULL )
  134. {
  135.      m_ProcessList.AddString(ProcessName);
  136. }
  137. }
  138. }
  139. m_ProcessList.SetCurSel(0);
  140. // TODO: 在此添加额外的初始化代码
  141.     ObjectExe="";
  142. /////////////////////////////////////////
  143. m_ifChecked=FALSE;
  144.     CWnd *pWnd1=GetDlgItem(IDC_COMBO_PROCESSLIST);
  145. pWnd1->ShowWindow(SW_HIDE);
  146. CWnd *pWnd2=GetDlgItem(IDC_REFRESHBUTTON);
  147. pWnd2->ShowWindow(SW_HIDE);
  148. CWnd *pWnd3=GetDlgItem(IDC_OPENFILENAME);
  149. pWnd3->ShowWindow(SW_SHOW);
  150. CWnd *pWnd4=GetDlgItem(IDC_SELECTBUTTON);
  151. pWnd4->ShowWindow(SW_SHOW);
  152. m_ctlCheckAttach.SetCheck(0);
  153. /////////////////////////////////////////
  154. return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
  155. }
  156. void CDebugProgramDlg::OnSysCommand(UINT nID, LPARAM lParam)
  157. {
  158. if ((nID & 0xFFF0) == IDM_ABOUTBOX)
  159. {
  160. CAboutDlg dlgAbout;
  161. dlgAbout.DoModal();
  162. }
  163. else
  164. {
  165. CDialog::OnSysCommand(nID, lParam);
  166. }
  167. }
  168. // 如果向对话框添加最小化按钮,则需要下面的代码
  169. //  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
  170. //  这将由框架自动完成。
  171. void CDebugProgramDlg::OnPaint()
  172. {
  173. if (IsIconic())
  174. {
  175. CPaintDC dc(this); // 用于绘制的设备上下文
  176. SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
  177. // 使图标在工作区矩形中居中
  178. int cxIcon = GetSystemMetrics(SM_CXICON);
  179. int cyIcon = GetSystemMetrics(SM_CYICON);
  180. CRect rect;
  181. GetClientRect(&rect);
  182. int x = (rect.Width() - cxIcon + 1) / 2;
  183. int y = (rect.Height() - cyIcon + 1) / 2;
  184. // 绘制图标
  185. dc.DrawIcon(x, y, m_hIcon);
  186. }
  187. else
  188. {
  189. CDialog::OnPaint();
  190. }
  191. }
  192. //当用户拖动最小化窗口时系统调用此函数取得光标
  193. //显示。
  194. HCURSOR CDebugProgramDlg::OnQueryDragIcon()
  195. {
  196. return static_cast<HCURSOR>(m_hIcon);
  197. }
  198. void CDebugProgramDlg::OnBnClickedButton1()//刷新进程
  199. {
  200. // TODO: 在此添加控件通知处理程序代码
  201. m_ProcessList.ResetContent();
  202. DWORD aProcesses[1024], cbNeeded, cProcesses;
  203. TCHAR *ProcessName=new TCHAR[MAX_PATH];
  204. unsigned int i;
  205. unsigned int j=0;
  206. if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
  207. return ;
  208. // Calculate how many process identifiers were returned.
  209. cProcesses = cbNeeded / sizeof(DWORD);
  210. // Print the name and process identifier for each process.
  211. for ( i = 0; i < cProcesses; i++ )
  212. {
  213. if( aProcesses[i] != 0 )
  214. {
  215. _stprintf(ProcessName,"%-32s",PrintProcessNameAndID( aProcesses[i] ) );
  216. if( _tcsstr(ProcessName,_T("<unknown>") ) == NULL )
  217. {
  218. m_ProcessList.AddString(ProcessName);
  219. }
  220. }
  221. }
  222. m_ProcessList.SetCurSel(0);
  223. }
  224. void CDebugProgramDlg::OnBnClickedButton2()//选择执行文件
  225. {
  226. // TODO: 在此添加控件通知处理程序代码
  227. char buffer[120480];
  228. CFileDialog OpenFile(TRUE,NULL,NULL,/*OFN_ALLOWMULTISELECT | */OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT);
  229.     
  230. buffer[0]=0;
  231. OpenFile.m_ofn.lpstrFile=buffer;
  232. OpenFile.m_ofn.nMaxFile=120480;
  233. OpenFile.m_ofn.lpstrFilter="可执行文件(*.exe)";/*可执行文件(*.exe)|*/
  234. OpenFile.m_ofn.lpstrDefExt="exe";
  235. if( OpenFile.DoModal() == IDCANCEL) return;
  236. ObjectExe=OpenFile.GetPathName();
  237.     //MessageBox(ObjectExe,0,0);
  238. }
  239. void CDebugProgramDlg::OnBnClickedButton3()//添加自定义断点
  240. {
  241. // TODO: 在此添加控件通知处理程序代码
  242. }
  243. void CDebugProgramDlg::OnBnClickedSelectbutton()
  244. {
  245. // TODO: 在此添加控件通知处理程序代码
  246. char buffer[120480];
  247. CFileDialog OpenFile(TRUE,NULL,NULL,/*OFN_ALLOWMULTISELECT | */OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT);
  248.     
  249. buffer[0]=0;
  250. OpenFile.m_ofn.lpstrFile=buffer;
  251. OpenFile.m_ofn.nMaxFile=120480;
  252. OpenFile.m_ofn.lpstrFilter="可执行文件(*.exe)";/*可执行文件(*.exe)|*/
  253. OpenFile.m_ofn.lpstrDefExt="exe";
  254. if( OpenFile.DoModal() == IDCANCEL) return;
  255. ObjectExe=OpenFile.GetPathName();
  256. m_OpenFileName.SetWindowTextA(ObjectExe);
  257. }
  258. void CDebugProgramDlg::OnBnClickedRefreshbutton()
  259. {
  260. // TODO: 在此添加控件通知处理程序代码
  261. m_ProcessList.ResetContent();
  262. DWORD aProcesses[1024], cbNeeded, cProcesses;
  263. TCHAR *ProcessName=new TCHAR[MAX_PATH];
  264. unsigned int i;
  265. unsigned int j=0;
  266. if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
  267. return ;
  268. // Calculate how many process identifiers were returned.
  269. cProcesses = cbNeeded / sizeof(DWORD);
  270. // Print the name and process identifier for each process.
  271. for ( i = 0; i < cProcesses; i++ )
  272. {
  273. if( aProcesses[i] != 0 )
  274. {
  275. _stprintf(ProcessName,"%-32s",PrintProcessNameAndID( aProcesses[i] ) );
  276. if( _tcsstr(ProcessName,_T("<unknown>") ) == NULL )
  277. {
  278. m_ProcessList.AddString(ProcessName);
  279. }
  280. }
  281. }
  282. m_ProcessList.SetCurSel(0);
  283. }
  284. void CDebugProgramDlg::OnBnClickedIfattachprocess()
  285. {
  286. // TODO: 在此添加控件通知处理程序代码
  287. //m_ifChecked ^= TRUE;
  288. int CheckStatus=m_ctlCheckAttach.GetCheck();
  289. if(CheckStatus==1)
  290. {
  291. CWnd *pWnd1=GetDlgItem(IDC_COMBO_PROCESSLIST);
  292. pWnd1->ShowWindow(SW_SHOW);
  293. CWnd *pWnd2=GetDlgItem(IDC_REFRESHBUTTON);
  294. pWnd2->ShowWindow(SW_SHOW);
  295. CWnd *pWnd3=GetDlgItem(IDC_OPENFILENAME);
  296. pWnd3->ShowWindow(SW_HIDE);
  297. CWnd *pWnd4=GetDlgItem(IDC_SELECTBUTTON);
  298. pWnd4->ShowWindow(SW_HIDE);
  299. m_ifChecked=FALSE;
  300. }
  301. if(CheckStatus==0)
  302. {
  303. CWnd *pWnd1=GetDlgItem(IDC_COMBO_PROCESSLIST);
  304. pWnd1->ShowWindow(SW_HIDE);
  305. CWnd *pWnd2=GetDlgItem(IDC_REFRESHBUTTON);
  306. pWnd2->ShowWindow(SW_HIDE);
  307. CWnd *pWnd3=GetDlgItem(IDC_OPENFILENAME);
  308. pWnd3->ShowWindow(SW_SHOW);
  309. CWnd *pWnd4=GetDlgItem(IDC_SELECTBUTTON);
  310. pWnd4->ShowWindow(SW_SHOW);
  311. m_ifChecked=TRUE;
  312. }
  313. }
  314. UINT ThreadRun(LPVOID PARAM)
  315. {
  316.     
  317.     unsigned char Index=0;
  318. char strtemp[256];
  319. //char ObjectExe[256];
  320. CString ObjectExe;
  321. tdlg->m_OpenFileName.GetWindowTextA(ObjectExe);
  322. if(ObjectExe.IsEmpty())
  323. {
  324.     tdlg->m_BreakPointList.InsertString(Index++,"请选择目标程序!!!");
  325.     return 0;
  326. sprintf(strtemp,"已经选择文件%s",ObjectExe);
  327. tdlg->m_BreakPointList.InsertString(Index++,strtemp);
  328. MAP_FILE_STRUCT stMapFile;
  329. char FileName[256];
  330. sprintf(FileName,"%s",ObjectExe);
  331. LoadFileR(FileName,&stMapFile);
  332. PIMAGE_OPTIONAL_HEADER  pOH=GetOptionalHeader(stMapFile.ImageBase);
  333. if(!pOH)
  334. {
  335.     tdlg->m_BreakPointList.InsertString(Index++,"Can't get Optional Header !");
  336. return 0;
  337. }
  338. tdlg->m_BreakPointList.InsertString(Index++,"成功获取执行文件Optional Header!!!");
  339. DWORD dwBreakAddr = pOH->ImageBase+pOH->AddressOfEntryPoint;
  340. sprintf(strtemp,"执行文件的入口点地址为%08x",dwBreakAddr);
  341. tdlg->m_BreakPointList.InsertString(Index++,strtemp);
  342. sprintf(strtemp,"设置的断点地址为%08x",dwBreakAddr);
  343. tdlg->m_BreakPointList.InsertString(Index++,strtemp);
  344. STARTUPINFO sif ;
  345. PROCESS_INFORMATION pi  ;
  346. ::ZeroMemory(&sif, sizeof(STARTUPINFO)) ;
  347. ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)) ;
  348. sif.cb = sizeof(STARTUPINFO) ;
  349. bool hRes ;
  350. bool STOP ;
  351. hRes = ::CreateProcess (_T(FileName), NULL, NULL, NULL, NULL, DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS, 
  352. NULL, NULL, &sif, &pi) ;
  353. if (hRes != TRUE)
  354. {
  355. ::MessageBox(NULL, _T("建立进程出错"), _T("错误"), MB_OK) ;
  356. ::ExitProcess(-1) ;
  357. }
  358. DEBUG_EVENT DBEvent ;
  359. CONTEXT Regs ;
  360. DWORD dwState, dwBpCnt, dwSSCnt, dwAddrProc ;
  361. //static const DWORD dwBreakAddr = 0x401000 ;
  362. unsigned int iTotalCommandNum ;
  363. TCHAR tBuffer[256] ;
  364. dwBpCnt = dwSSCnt = 0 ;
  365. iTotalCommandNum = 0 ;
  366. STOP = false ;
  367. Regs.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS ;
  368. do
  369. {
  370. ::WaitForDebugEvent (&DBEvent, INFINITE) ;
  371. dwState = DBG_EXCEPTION_NOT_HANDLED ;
  372. switch (DBEvent.dwDebugEventCode)
  373. {
  374. case EXCEPTION_DEBUG_EVENT:
  375. {
  376. switch (DBEvent.u.Exception.ExceptionRecord.ExceptionCode)
  377. {
  378. case EXCEPTION_BREAKPOINT:
  379. {
  380. ++dwBpCnt ;
  381. if (dwBpCnt == 1)
  382. {
  383. ::GetThreadContext(pi.hThread, &Regs) ;
  384. Regs.Dr0 = (DWORD)(::GetProcAddress(::GetModuleHandle(_T("ntdll.dll")), _T("NtContinue")) );
  385. Regs.Dr7 = 0x101 ;
  386. ::SetThreadContext(pi.hThread, &Regs) ;
  387. dwState = DBG_CONTINUE ;
  388. }
  389. break ;
  390. }
  391. case EXCEPTION_SINGLE_STEP :
  392. {
  393. ++dwSSCnt ;
  394. if (dwSSCnt == 1)
  395. {
  396. ::GetThreadContext(pi.hThread, &Regs) ;
  397. Regs.Dr0 = Regs.Dr7 = 0 ;
  398. ::SetThreadContext(pi.hThread, &Regs) ;
  399. ::ReadProcessMemory(pi.hProcess, (LPCVOID)(Regs.Esp+4), &dwAddrProc, sizeof(DWORD), NULL) ;
  400. ::ReadProcessMemory(pi.hProcess, (LPCVOID)dwAddrProc, &Regs, sizeof(CONTEXT), NULL) ;
  401. Regs.Dr0 = dwBreakAddr ;
  402. Regs.Dr7 = 0x101 ;
  403. ::WriteProcessMemory(pi.hProcess, (LPVOID)dwAddrProc, &Regs, sizeof(CONTEXT), NULL) ;
  404. dwState = DBG_CONTINUE ;
  405. }
  406. else if (dwSSCnt == 2)
  407. {
  408. ::GetThreadContext(pi.hThread, &Regs) ;
  409. char buffer[2600];
  410. sprintf(buffer,"EAX=%08xnEBX=%08xnECX=%08xnEDX=%08xnESP=%08xnEIP=%08xn",Regs.Eax,Regs.Ebx,Regs.Ecx,Regs.Edx,Regs.Esp,Regs.Eip);
  411. MessageBox(NULL,buffer,"ok",MB_OK);
  412. Regs.Dr0 = Regs.Dr7 = 0 ;
  413. Regs.EFlags |= 0x100 ;//此处不屏蔽则会进入下一次单步运算
  414. ::SetThreadContext(pi.hThread, &Regs) ;
  415. ++iTotalCommandNum ;
  416. dwState = DBG_CONTINUE ;
  417. }
  418. else if (dwSSCnt == 3)
  419. {
  420. ::GetThreadContext(pi.hThread, &Regs) ;
  421. char buffer[2600];
  422. sprintf(buffer,"ESP=%08xn",Regs.Esp);
  423. MessageBox(NULL,buffer,"ok",MB_OK);
  424. unsigned char strbuf[256];
  425. unsigned char newstr[256]={"程序已经被成功修改,谢谢欣赏!!!"};
  426. for(int i=31;i<256;i++)
  427. newstr[i]=0;
  428. ::ReadProcessMemory(pi.hProcess, (LPCVOID)(Regs.Esp), &dwAddrProc, sizeof(DWORD), NULL) ;  
  429. ::ReadProcessMemory(pi.hProcess, (LPCVOID)dwAddrProc, &strbuf, 256, NULL) ;
  430. ::WriteProcessMemory(pi.hProcess,(LPVOID)dwAddrProc, &newstr,256,NULL);
  431. Regs.Dr0 = Regs.Dr7 = 0 ;
  432. //Regs.EFlags |= 0x100 ;//此处不屏蔽则会进入下一次单步运算
  433. ::SetThreadContext(pi.hThread, &Regs) ;
  434. ++iTotalCommandNum ;
  435. dwState = DBG_CONTINUE ;
  436. }//*/
  437. break ;
  438. }
  439. }
  440. break ;
  441. }
  442. case EXIT_PROCESS_DEBUG_EVENT :
  443. {
  444. iTotalCommandNum ;
  445. STOP = TRUE ;
  446. ::sprintf(tBuffer, _T("程序总指令数:  %08lX"), iTotalCommandNum) ;
  447. ::MessageBox(NULL, tBuffer, _T("结束"), MB_OK) ;
  448. //::ExitProcess(-1) ;
  449. break ;
  450. }
  451. }
  452. if (!STOP)
  453. {
  454. ::ContinueDebugEvent(pi.dwProcessId, pi.dwThreadId, dwState) ;
  455. }
  456. } while (!STOP) ;
  457. ::CloseHandle(pi.hProcess) ;
  458. ::CloseHandle(pi.hThread)  ;
  459. //::ExitProcess(0) ;//*/
  460. return 0;
  461. }
  462. void CDebugProgramDlg::OnBnClickedStartbutton()
  463. {
  464. // TODO: 在此添加控件通知处理程序代码
  465. void *psa=NULL;
  466. unsigned cbStackSize=0;
  467. //_ptiddata ptd;
  468. unsigned dwCreateFlags=0;
  469. unsigned *pdwThreadID=new unsigned[5];
  470.     tdlg=this;
  471. //HANDLE hThread=CreateThread((LPSECURITY_ATTRIBUTES)psa,cbStackSize,(LPTHREAD_START_ROUTINE)ThreadRun,NULL,dwCreateFlags,(LPDWORD)pdwThreadID);
  472.     HANDLE hThread=AfxBeginThread(ThreadRun,NULL);
  473. WaitForSingleObject(hThread,INFINITE);
  474. }