Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

793 lines
20 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. // Sample of monitoring an application for compatibility problems
  4. // and automatically correcting them.
  5. //
  6. // Copyright (C) Microsoft Corporation, 2000-2001.
  7. //
  8. //----------------------------------------------------------------------------
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <stdarg.h>
  12. #include <windows.h>
  13. #include <dbgeng.h>
  14. PSTR g_SymbolPath;
  15. char g_CommandLine[8 * MAX_PATH];
  16. BOOL g_Verbose;
  17. BOOL g_NeedVersionBps;
  18. IDebugClient* g_Client;
  19. IDebugControl* g_Control;
  20. IDebugDataSpaces* g_Data;
  21. IDebugRegisters* g_Registers;
  22. IDebugSymbols* g_Symbols;
  23. struct BREAKPOINT
  24. {
  25. IDebugBreakpoint* Bp;
  26. ULONG Id;
  27. };
  28. BREAKPOINT g_GetVersionBp;
  29. BREAKPOINT g_GetVersionRetBp;
  30. BREAKPOINT g_GetVersionExBp;
  31. BREAKPOINT g_GetVersionExRetBp;
  32. ULONG g_EaxIndex = DEBUG_ANY_ID;
  33. OSVERSIONINFO g_OsVer;
  34. DWORD g_VersionNumber;
  35. ULONG64 g_OsVerOffset;
  36. //----------------------------------------------------------------------------
  37. //
  38. // Utility routines.
  39. //
  40. //----------------------------------------------------------------------------
  41. void
  42. Exit(int Code, PCSTR Format, ...)
  43. {
  44. // Clean up any resources.
  45. if (g_Control != NULL)
  46. {
  47. g_Control->Release();
  48. }
  49. if (g_Data != NULL)
  50. {
  51. g_Data->Release();
  52. }
  53. if (g_Registers != NULL)
  54. {
  55. g_Registers->Release();
  56. }
  57. if (g_Symbols != NULL)
  58. {
  59. g_Symbols->Release();
  60. }
  61. if (g_Client != NULL)
  62. {
  63. //
  64. // Request a simple end to any current session.
  65. // This may or may not do anything but it isn't
  66. // harmful to call it.
  67. //
  68. g_Client->EndSession(DEBUG_END_PASSIVE);
  69. g_Client->Release();
  70. }
  71. // Output an error message if given.
  72. if (Format != NULL)
  73. {
  74. va_list Args;
  75. va_start(Args, Format);
  76. vfprintf(stderr, Format, Args);
  77. va_end(Args);
  78. }
  79. exit(Code);
  80. }
  81. void
  82. Print(PCSTR Format, ...)
  83. {
  84. va_list Args;
  85. printf("HEALER: ");
  86. va_start(Args, Format);
  87. vprintf(Format, Args);
  88. va_end(Args);
  89. }
  90. HRESULT
  91. AddBp(BREAKPOINT* Bp, PCSTR Expr)
  92. {
  93. HRESULT Status;
  94. if ((Status = g_Control->AddBreakpoint(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID,
  95. &Bp->Bp)) != S_OK)
  96. {
  97. Bp->Id = DEBUG_ANY_ID;
  98. return Status;
  99. }
  100. if ((Status = Bp->Bp->GetId(&Bp->Id)) != S_OK ||
  101. (Status = Bp->Bp->SetOffsetExpression(Expr)) != S_OK ||
  102. (Status = Bp->Bp->AddFlags(DEBUG_BREAKPOINT_ENABLED)) != S_OK)
  103. {
  104. Bp->Bp->Release();
  105. Bp->Id = DEBUG_ANY_ID;
  106. return Status;
  107. }
  108. return S_OK;
  109. }
  110. //----------------------------------------------------------------------------
  111. //
  112. // Healing routines.
  113. //
  114. //----------------------------------------------------------------------------
  115. void
  116. ApplyExePatches(PCSTR ImageName, ULONG64 BaseOffset)
  117. {
  118. if (ImageName == NULL)
  119. {
  120. ImageName = "<Unknown>";
  121. }
  122. // This would be where any executable image patching would go.
  123. Print("Executable '%s' loaded at %I64x\n", ImageName, BaseOffset);
  124. }
  125. void
  126. ApplyDllPatches(PCSTR ImageName, ULONG64 BaseOffset)
  127. {
  128. if (ImageName == NULL)
  129. {
  130. ImageName = "<Unknown>";
  131. }
  132. // Any DLL-specific image patching goes here.
  133. Print("DLL '%s' loaded at %I64x\n", ImageName, BaseOffset);
  134. }
  135. void
  136. AddVersionBps(void)
  137. {
  138. //
  139. // Put breakpoints on GetVersion and GetVersionEx.
  140. //
  141. if (AddBp(&g_GetVersionBp, "kernel32!GetVersion") != S_OK ||
  142. AddBp(&g_GetVersionExBp, "kernel32!GetVersionEx") != S_OK)
  143. {
  144. Exit(1, "Unable to set version breakpoints\n");
  145. }
  146. //
  147. // Create the return breakpoints but leave them disabled
  148. // until they're needed.
  149. //
  150. if (g_Control->AddBreakpoint(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID,
  151. &g_GetVersionRetBp.Bp) != S_OK ||
  152. g_GetVersionRetBp.Bp->GetId(&g_GetVersionRetBp.Id) != S_OK ||
  153. g_Control->AddBreakpoint(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID,
  154. &g_GetVersionExRetBp.Bp) != S_OK ||
  155. g_GetVersionExRetBp.Bp->GetId(&g_GetVersionExRetBp.Id) != S_OK)
  156. {
  157. Exit(1, "Unable to set version breakpoints\n");
  158. }
  159. }
  160. //----------------------------------------------------------------------------
  161. //
  162. // Event callbacks.
  163. //
  164. //----------------------------------------------------------------------------
  165. class EventCallbacks : public DebugBaseEventCallbacks
  166. {
  167. public:
  168. // IUnknown.
  169. STDMETHOD_(ULONG, AddRef)(
  170. THIS
  171. );
  172. STDMETHOD_(ULONG, Release)(
  173. THIS
  174. );
  175. // IDebugEventCallbacks.
  176. STDMETHOD(GetInterestMask)(
  177. THIS_
  178. OUT PULONG Mask
  179. );
  180. STDMETHOD(Breakpoint)(
  181. THIS_
  182. IN PDEBUG_BREAKPOINT Bp
  183. );
  184. STDMETHOD(Exception)(
  185. THIS_
  186. IN PEXCEPTION_RECORD64 Exception,
  187. IN ULONG FirstChance
  188. );
  189. STDMETHOD(CreateProcess)(
  190. THIS_
  191. IN ULONG64 ImageFileHandle,
  192. IN ULONG64 Handle,
  193. IN ULONG64 BaseOffset,
  194. IN ULONG ModuleSize,
  195. IN PCSTR ModuleName,
  196. IN PCSTR ImageName,
  197. IN ULONG CheckSum,
  198. IN ULONG TimeDateStamp,
  199. IN ULONG64 InitialThreadHandle,
  200. IN ULONG64 ThreadDataOffset,
  201. IN ULONG64 StartOffset
  202. );
  203. STDMETHOD(LoadModule)(
  204. THIS_
  205. IN ULONG64 ImageFileHandle,
  206. IN ULONG64 BaseOffset,
  207. IN ULONG ModuleSize,
  208. IN PCSTR ModuleName,
  209. IN PCSTR ImageName,
  210. IN ULONG CheckSum,
  211. IN ULONG TimeDateStamp
  212. );
  213. STDMETHOD(SessionStatus)(
  214. THIS_
  215. IN ULONG Status
  216. );
  217. };
  218. STDMETHODIMP_(ULONG)
  219. EventCallbacks::AddRef(
  220. THIS
  221. )
  222. {
  223. // This class is designed to be static so
  224. // there's no true refcount.
  225. return 1;
  226. }
  227. STDMETHODIMP_(ULONG)
  228. EventCallbacks::Release(
  229. THIS
  230. )
  231. {
  232. // This class is designed to be static so
  233. // there's no true refcount.
  234. return 0;
  235. }
  236. STDMETHODIMP
  237. EventCallbacks::GetInterestMask(
  238. THIS_
  239. OUT PULONG Mask
  240. )
  241. {
  242. *Mask =
  243. DEBUG_EVENT_BREAKPOINT |
  244. DEBUG_EVENT_EXCEPTION |
  245. DEBUG_EVENT_CREATE_PROCESS |
  246. DEBUG_EVENT_LOAD_MODULE |
  247. DEBUG_EVENT_SESSION_STATUS;
  248. return S_OK;
  249. }
  250. STDMETHODIMP
  251. EventCallbacks::Breakpoint(
  252. THIS_
  253. IN PDEBUG_BREAKPOINT Bp
  254. )
  255. {
  256. ULONG Id;
  257. ULONG64 ReturnOffset;
  258. if (Bp->GetId(&Id) != S_OK)
  259. {
  260. return DEBUG_STATUS_BREAK;
  261. }
  262. if (Id == g_GetVersionBp.Id)
  263. {
  264. // Set a breakpoint on the return address of the call
  265. // so that we can patch up any returned information.
  266. if (g_Control->GetReturnOffset(&ReturnOffset) != S_OK ||
  267. g_GetVersionRetBp.Bp->SetOffset(ReturnOffset) != S_OK ||
  268. g_GetVersionRetBp.Bp->AddFlags(DEBUG_BREAKPOINT_ENABLED) != S_OK)
  269. {
  270. return DEBUG_STATUS_BREAK;
  271. }
  272. }
  273. else if (Id == g_GetVersionExBp.Id)
  274. {
  275. ULONG64 StackOffset;
  276. // Remember the OSVERSIONINFO structure pointer.
  277. if (g_Registers->GetStackOffset(&StackOffset) != S_OK ||
  278. g_Data->ReadPointersVirtual(1, StackOffset + 4,
  279. &g_OsVerOffset) != S_OK)
  280. {
  281. return DEBUG_STATUS_BREAK;
  282. }
  283. // Set a breakpoint on the return address of the call
  284. // so that we can patch up any returned information.
  285. if (g_Control->GetReturnOffset(&ReturnOffset) != S_OK ||
  286. g_GetVersionExRetBp.Bp->SetOffset(ReturnOffset) != S_OK ||
  287. g_GetVersionExRetBp.Bp->AddFlags(DEBUG_BREAKPOINT_ENABLED) != S_OK)
  288. {
  289. return DEBUG_STATUS_BREAK;
  290. }
  291. }
  292. else if (Id == g_GetVersionRetBp.Id)
  293. {
  294. // Turn off the breakpoint.
  295. if (g_GetVersionRetBp.Bp->
  296. RemoveFlags(DEBUG_BREAKPOINT_ENABLED) != S_OK)
  297. {
  298. return DEBUG_STATUS_BREAK;
  299. }
  300. DEBUG_VALUE Val;
  301. // Change eax to alter the returned version value.
  302. Val.Type = DEBUG_VALUE_INT32;
  303. Val.I32 = g_VersionNumber;
  304. if (g_Registers->SetValue(g_EaxIndex, &Val) != S_OK)
  305. {
  306. return DEBUG_STATUS_BREAK;
  307. }
  308. Print("GetVersion returns %08X\n", g_VersionNumber);
  309. }
  310. else if (Id == g_GetVersionExRetBp.Id)
  311. {
  312. ULONG Done;
  313. // Turn off the breakpoint.
  314. if (g_GetVersionExRetBp.Bp->
  315. RemoveFlags(DEBUG_BREAKPOINT_ENABLED) != S_OK)
  316. {
  317. return DEBUG_STATUS_BREAK;
  318. }
  319. // Change the returned OSVERSIONINFO structure.
  320. if (g_Data->WriteVirtual(g_OsVerOffset, &g_OsVer, sizeof(g_OsVer),
  321. &Done) != S_OK ||
  322. Done != sizeof(g_OsVer))
  323. {
  324. return DEBUG_STATUS_BREAK;
  325. }
  326. Print("GetVersionEx returns %08X\n", g_VersionNumber);
  327. }
  328. else
  329. {
  330. return DEBUG_STATUS_NO_CHANGE;
  331. }
  332. return DEBUG_STATUS_GO;
  333. }
  334. STDMETHODIMP
  335. EventCallbacks::Exception(
  336. THIS_
  337. IN PEXCEPTION_RECORD64 Exception,
  338. IN ULONG FirstChance
  339. )
  340. {
  341. // We want to handle these exceptions on the first
  342. // chance to make it look like no exception ever
  343. // happened. Handling them on the second chance would
  344. // allow an exception handler somewhere in the app
  345. // to be hit on the first chance.
  346. if (!FirstChance)
  347. {
  348. return DEBUG_STATUS_NO_CHANGE;
  349. }
  350. //
  351. // Check and see if the instruction causing the exception
  352. // is a cli or sti. These are not allowed in user-mode
  353. // programs on NT so if they're present just nop them.
  354. //
  355. // sti/cli will generate privileged instruction faults.
  356. if (Exception->ExceptionCode != STATUS_PRIVILEGED_INSTRUCTION)
  357. {
  358. return DEBUG_STATUS_NO_CHANGE;
  359. }
  360. UCHAR Instr;
  361. ULONG Done;
  362. // It's a privileged instruction, so check the code for sti/cli.
  363. if (g_Data->ReadVirtual(Exception->ExceptionAddress, &Instr,
  364. sizeof(Instr), &Done) != S_OK ||
  365. Done != sizeof(Instr) ||
  366. (Instr != 0xfb && Instr != 0xfa))
  367. {
  368. return DEBUG_STATUS_NO_CHANGE;
  369. }
  370. // It's a sti/cli, so nop it out and continue.
  371. Instr = 0x90;
  372. if (g_Data->WriteVirtual(Exception->ExceptionAddress, &Instr,
  373. sizeof(Instr), &Done) != S_OK ||
  374. Done != sizeof(Instr))
  375. {
  376. return DEBUG_STATUS_NO_CHANGE;
  377. }
  378. // Fixed.
  379. if (g_Verbose)
  380. {
  381. Print("Removed sti/cli at %I64x\n", Exception->ExceptionAddress);
  382. }
  383. return DEBUG_STATUS_GO_HANDLED;
  384. }
  385. STDMETHODIMP
  386. EventCallbacks::CreateProcess(
  387. THIS_
  388. IN ULONG64 ImageFileHandle,
  389. IN ULONG64 Handle,
  390. IN ULONG64 BaseOffset,
  391. IN ULONG ModuleSize,
  392. IN PCSTR ModuleName,
  393. IN PCSTR ImageName,
  394. IN ULONG CheckSum,
  395. IN ULONG TimeDateStamp,
  396. IN ULONG64 InitialThreadHandle,
  397. IN ULONG64 ThreadDataOffset,
  398. IN ULONG64 StartOffset
  399. )
  400. {
  401. UNREFERENCED_PARAMETER(ImageFileHandle);
  402. UNREFERENCED_PARAMETER(Handle);
  403. UNREFERENCED_PARAMETER(ModuleSize);
  404. UNREFERENCED_PARAMETER(ModuleName);
  405. UNREFERENCED_PARAMETER(CheckSum);
  406. UNREFERENCED_PARAMETER(TimeDateStamp);
  407. UNREFERENCED_PARAMETER(InitialThreadHandle);
  408. UNREFERENCED_PARAMETER(ThreadDataOffset);
  409. UNREFERENCED_PARAMETER(StartOffset);
  410. // The process is now available for manipulation.
  411. // Perform any initial code patches on the executable.
  412. ApplyExePatches(ImageName, BaseOffset);
  413. // If the user requested that version calls be fixed up
  414. // register breakpoints to do so.
  415. if (g_NeedVersionBps)
  416. {
  417. AddVersionBps();
  418. }
  419. return DEBUG_STATUS_GO;
  420. }
  421. STDMETHODIMP
  422. EventCallbacks::LoadModule(
  423. THIS_
  424. IN ULONG64 ImageFileHandle,
  425. IN ULONG64 BaseOffset,
  426. IN ULONG ModuleSize,
  427. IN PCSTR ModuleName,
  428. IN PCSTR ImageName,
  429. IN ULONG CheckSum,
  430. IN ULONG TimeDateStamp
  431. )
  432. {
  433. UNREFERENCED_PARAMETER(ImageFileHandle);
  434. UNREFERENCED_PARAMETER(ModuleSize);
  435. UNREFERENCED_PARAMETER(ModuleName);
  436. UNREFERENCED_PARAMETER(CheckSum);
  437. UNREFERENCED_PARAMETER(TimeDateStamp);
  438. ApplyDllPatches(ImageName, BaseOffset);
  439. return DEBUG_STATUS_GO;
  440. }
  441. STDMETHODIMP
  442. EventCallbacks::SessionStatus(
  443. THIS_
  444. IN ULONG SessionStatus
  445. )
  446. {
  447. // A session isn't fully active until WaitForEvent
  448. // has been called and has processed the initial
  449. // debug events. We need to wait for activation
  450. // before we query information about the session
  451. // as not all information is available until the
  452. // session is fully active. We could put these
  453. // queries into CreateProcess as that happens
  454. // early and when the session is fully active, but
  455. // for example purposes we'll wait for an
  456. // active SessionStatus callback.
  457. // In non-callback applications this work can just
  458. // be done after the first successful WaitForEvent.
  459. if (SessionStatus != DEBUG_SESSION_ACTIVE)
  460. {
  461. return S_OK;
  462. }
  463. HRESULT Status;
  464. //
  465. // Find the register index for eax as we'll need
  466. // to access eax.
  467. //
  468. if ((Status = g_Registers->GetIndexByName("eax", &g_EaxIndex)) != S_OK)
  469. {
  470. Exit(1, "GetIndexByName failed, 0x%X\n", Status);
  471. }
  472. return S_OK;
  473. }
  474. EventCallbacks g_EventCb;
  475. //----------------------------------------------------------------------------
  476. //
  477. // Initialization and main event loop.
  478. //
  479. //----------------------------------------------------------------------------
  480. void
  481. CreateInterfaces(void)
  482. {
  483. SYSTEM_INFO SysInfo;
  484. // For purposes of keeping this example simple the
  485. // code only works on x86 machines. There's no reason
  486. // that it couldn't be made to work on all processors, though.
  487. GetSystemInfo(&SysInfo);
  488. if (SysInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL)
  489. {
  490. Exit(1, "This program only runs on x86 machines.\n");
  491. }
  492. // Get default version information.
  493. g_OsVer.dwOSVersionInfoSize = sizeof(g_OsVer);
  494. if (!GetVersionEx(&g_OsVer))
  495. {
  496. Exit(1, "GetVersionEx failed, %d\n", GetLastError());
  497. }
  498. HRESULT Status;
  499. // Start things off by getting an initial interface from
  500. // the engine. This can be any engine interface but is
  501. // generally IDebugClient as the client interface is
  502. // where sessions are started.
  503. if ((Status = DebugCreate(__uuidof(IDebugClient),
  504. (void**)&g_Client)) != S_OK)
  505. {
  506. Exit(1, "DebugCreate failed, 0x%X\n", Status);
  507. }
  508. // Query for some other interfaces that we'll need.
  509. if ((Status = g_Client->QueryInterface(__uuidof(IDebugControl),
  510. (void**)&g_Control)) != S_OK ||
  511. (Status = g_Client->QueryInterface(__uuidof(IDebugDataSpaces),
  512. (void**)&g_Data)) != S_OK ||
  513. (Status = g_Client->QueryInterface(__uuidof(IDebugRegisters),
  514. (void**)&g_Registers)) != S_OK ||
  515. (Status = g_Client->QueryInterface(__uuidof(IDebugSymbols),
  516. (void**)&g_Symbols)) != S_OK)
  517. {
  518. Exit(1, "QueryInterface failed, 0x%X\n", Status);
  519. }
  520. }
  521. void
  522. ParseCommandLine(int Argc, char** Argv)
  523. {
  524. while (--Argc > 0)
  525. {
  526. Argv++;
  527. if (!strcmp(*Argv, "-plat"))
  528. {
  529. if (Argc < 2)
  530. {
  531. Exit(1, "-plat missing argument\n");
  532. }
  533. Argv++;
  534. Argc--;
  535. sscanf(*Argv, "%i", &g_OsVer.dwPlatformId);
  536. g_NeedVersionBps = TRUE;
  537. }
  538. else if (!strcmp(*Argv, "-v"))
  539. {
  540. g_Verbose = TRUE;
  541. }
  542. else if (!strcmp(*Argv, "-ver"))
  543. {
  544. if (Argc < 2)
  545. {
  546. Exit(1, "-ver missing argument\n");
  547. }
  548. Argv++;
  549. Argc--;
  550. sscanf(*Argv, "%i.%i.%i",
  551. &g_OsVer.dwMajorVersion, &g_OsVer.dwMinorVersion,
  552. &g_OsVer.dwBuildNumber);
  553. g_NeedVersionBps = TRUE;
  554. }
  555. else if (!strcmp(*Argv, "-y"))
  556. {
  557. if (Argc < 2)
  558. {
  559. Exit(1, "-y missing argument\n");
  560. }
  561. Argv++;
  562. Argc--;
  563. g_SymbolPath = *Argv;
  564. }
  565. else
  566. {
  567. // Assume process arguments begin.
  568. break;
  569. }
  570. }
  571. //
  572. // Concatenate remaining arguments into a command line.
  573. //
  574. PSTR CmdLine = g_CommandLine;
  575. while (Argc > 0)
  576. {
  577. BOOL Quote = FALSE;
  578. ULONG Len;
  579. // Quote arguments with spaces.
  580. if (strchr(*Argv, ' ') != NULL || strchr(*Argv, '\t') != NULL)
  581. {
  582. *CmdLine++ = '"';
  583. Quote = TRUE;
  584. }
  585. Len = strlen(*Argv);
  586. if (Len + (CmdLine - g_CommandLine) >= sizeof(g_CommandLine))
  587. {
  588. Exit(1, "Command line too long\n");
  589. }
  590. memcpy(CmdLine, *Argv, Len + 1);
  591. CmdLine += Len;
  592. if (Quote)
  593. {
  594. *CmdLine++ = '"';
  595. }
  596. *CmdLine++ = ' ';
  597. Argv++;
  598. Argc--;
  599. }
  600. *CmdLine = 0;
  601. if (strlen(g_CommandLine) == 0)
  602. {
  603. Exit(1, "No application command line given\n");
  604. }
  605. }
  606. void
  607. ApplyCommandLineArguments(void)
  608. {
  609. HRESULT Status;
  610. if (g_SymbolPath != NULL)
  611. {
  612. if ((Status = g_Symbols->SetSymbolPath(g_SymbolPath)) != S_OK)
  613. {
  614. Exit(1, "SetSymbolPath failed, 0x%X\n", Status);
  615. }
  616. }
  617. // Register our event callbacks.
  618. if ((Status = g_Client->SetEventCallbacks(&g_EventCb)) != S_OK)
  619. {
  620. Exit(1, "SetEventCallbacks failed, 0x%X\n", Status);
  621. }
  622. // Everything's set up so start the app.
  623. if ((Status = g_Client->CreateProcess(0, g_CommandLine,
  624. DEBUG_ONLY_THIS_PROCESS)) != S_OK)
  625. {
  626. Exit(1, "CreateProcess failed, 0x%X\n", Status);
  627. }
  628. // Compute the GetVersion value from the OSVERSIONINFO.
  629. g_VersionNumber = (g_OsVer.dwMajorVersion & 0xff) |
  630. ((g_OsVer.dwMinorVersion & 0xff) << 8);
  631. if (g_OsVer.dwPlatformId == VER_PLATFORM_WIN32_NT)
  632. {
  633. g_VersionNumber |= (g_OsVer.dwBuildNumber & 0x7fff) << 16;
  634. }
  635. else
  636. {
  637. g_VersionNumber |= 0x80000000;
  638. }
  639. }
  640. void
  641. EventLoop(void)
  642. {
  643. HRESULT Status;
  644. for (;;)
  645. {
  646. if ((Status = g_Control->WaitForEvent(DEBUG_WAIT_DEFAULT,
  647. INFINITE)) != S_OK)
  648. {
  649. ULONG ExecStatus;
  650. // Check and see whether the session is running or not.
  651. if (g_Control->GetExecutionStatus(&ExecStatus) == S_OK &&
  652. ExecStatus == DEBUG_STATUS_NO_DEBUGGEE)
  653. {
  654. // The session ended so we can quit.
  655. break;
  656. }
  657. // There was a real error.
  658. Exit(1, "WaitForEvent failed, 0x%X\n", Status);
  659. }
  660. // Our event callbacks asked to break in. This
  661. // only occurs in situations when the callback
  662. // couldn't handle the event. See if the user cares.
  663. if (MessageBox(GetDesktopWindow(),
  664. "An unusual event occurred. Ignore it?",
  665. "Unhandled Event", MB_YESNO) == IDNO)
  666. {
  667. Exit(1, "Unhandled event\n");
  668. }
  669. // User chose to ignore so restart things.
  670. if ((Status = g_Control->
  671. SetExecutionStatus(DEBUG_STATUS_GO_HANDLED)) != S_OK)
  672. {
  673. Exit(1, "SetExecutionStatus failed, 0x%X\n", Status);
  674. }
  675. }
  676. }
  677. void __cdecl
  678. main(int Argc, char** Argv)
  679. {
  680. CreateInterfaces();
  681. ParseCommandLine(Argc, Argv);
  682. ApplyCommandLineArguments();
  683. EventLoop();
  684. Exit(0, NULL);
  685. }