AppModeDebug.h 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894
  1. #pragma once
  2. /////////////////////////////////////////////////////////////////////////////
  3. // AppModeDebug.h: Declaration of the CAppModeDebug class creator.
  4. //
  5. #if defined(NOT_REALLY_INCLUDING_THESE)
  6. #include "pch.h"
  7. #include "MSRGuard.h"
  8. #endif
  9. #include "AppMode.h"
  10. #include "LoadedModules.h"
  11. #include "ToolHelp.h"
  12. /////////////////////////////////////////////////////////////////////////////
  13. // Macro useful for writing a ZString to an IStream pointer.
  14. //
  15. #define WRITE_STRING_TO_STREAM(p, s) \
  16. RETURN_FAILED(p->Write((LPCTSTR)s, s.GetLength() * sizeof(TCHAR), NULL))
  17. /////////////////////////////////////////////////////////////////////////////
  18. //
  19. //
  20. template <class T>
  21. class CAppModeDebug :
  22. public IAppMode
  23. {
  24. // Construction
  25. public:
  26. CAppModeDebug();
  27. // IAppMode Interface Methods
  28. public:
  29. STDMETHODIMP Run(int argc, TCHAR* argv[]);
  30. // Debug Event Handlers
  31. private:
  32. DWORD HandleException (bool* pbExit);
  33. DWORD HandleCreateThread (bool* pbExit);
  34. DWORD HandleCreateProcess (bool* pbExit);
  35. DWORD HandleExitThread (bool* pbExit);
  36. DWORD HandleExitProcess (bool* pbExit);
  37. DWORD HandleLoadDLL (bool* pbExit);
  38. DWORD HandleUnloadDLL (bool* pbExit);
  39. DWORD HandleOutputDebugString(bool* pbExit);
  40. DWORD HandleRIP (bool* pbExit);
  41. DWORD HandleOther (bool* pbExit);
  42. // Implementation
  43. private:
  44. DWORD ProcessDebugEvent(DEBUG_EVENT* pde, bool* pbExit);
  45. HRESULT ReportException();
  46. HRESULT ComposeExceptionReportParams();
  47. HRESULT CanonicalizeParamText(ZString strIn, ZString& strOut);
  48. HRESULT GetExceptionState(ZString& strOut);
  49. HRESULT GetMachineText(ZString& strOut);
  50. ZString ReadProcessString(void* pvAddr, bool fUnicode, int cch = -1);
  51. static LPCTSTR GetExceptionName(DWORD dwExceptionCode);
  52. ZString GetAddressModule(void* pvAddr);
  53. HRESULT FormatThreadState(IStream* pStm, DWORD dwThreadId, bool bFull);
  54. HRESULT FormatStackFrame(IStream* pStm, STACKFRAME& stk);
  55. HRESULT FormatModuleInfo(IStream* pStm, CLoadedModuleIt it);
  56. HRESULT FormatProcessesInfo(IStream* pStm);
  57. HRESULT FormatProcessInfo(IStream* pStm, PROCESSENTRY32* ppe);
  58. HRESULT FormatProcessInfo(IStream* pStm, LPCTSTR pszEXE);
  59. ZString GetSymbolPath();
  60. ZString GetSearchPath();
  61. ZString GetModuleWithPath(LPCTSTR pszModule);
  62. static BOOL CALLBACK ReadProcessMemoryProc(HANDLE hProcess, DWORD lpBaseAddress,
  63. PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead);
  64. static PVOID CALLBACK FunctionTableAccessProc(HANDLE hProcess, DWORD AddrBase);
  65. static DWORD CALLBACK GetModuleBaseProc(HANDLE hProcess, DWORD Address);
  66. static DWORD CALLBACK TranslateAddressProc(HANDLE hProcess, HANDLE hThread,
  67. LPADDRESS lpaddr);
  68. // Overrides
  69. public:
  70. DWORD OnException (bool* pbExit);
  71. DWORD OnCreateThread (bool* pbExit);
  72. DWORD OnCreateProcess (bool* pbExit);
  73. DWORD OnExitThread (bool* pbExit);
  74. DWORD OnExitProcess (bool* pbExit);
  75. DWORD OnLoadDLL (bool* pbExit);
  76. DWORD OnUnloadDLL (bool* pbExit);
  77. DWORD OnOutputDebugString(bool* pbExit);
  78. DWORD OnRIP (bool* pbExit);
  79. DWORD OnOther (bool* pbExit);
  80. // Types
  81. protected:
  82. typedef CAppModeDebug<T> CAppModeDebug_Base;
  83. typedef std::map<DWORD, CREATE_THREAD_DEBUG_INFO> XThreads;
  84. typedef XThreads::iterator XThreadIt;
  85. typedef std::map<ZString, ZString> XParams;
  86. typedef XParams::iterator XParamIt;
  87. // Data Members
  88. protected:
  89. XThreads m_Threads;
  90. XParams m_Params;
  91. CLoadedModules m_Images;
  92. CREATE_PROCESS_DEBUG_INFO m_cpdi;
  93. DWORD m_dwProcessId;
  94. HANDLE m_hProcessSym;
  95. DEBUG_EVENT* m_pde;
  96. ZString m_strBrowser;
  97. ZString m_strRegGUID;
  98. ZString m_strAppGUID;
  99. ZString m_strSearchPath;
  100. TCHandle m_shEvent;
  101. CToolHelp m_libToolHelp;
  102. };
  103. /////////////////////////////////////////////////////////////////////////////
  104. #include "MSRGuard.h"
  105. #include "resource.h"
  106. /////////////////////////////////////////////////////////////////////////////
  107. // Construction
  108. template <class T>
  109. inline CAppModeDebug<T>::CAppModeDebug() :
  110. m_dwProcessId(0),
  111. m_hProcessSym(NULL),
  112. m_pde(NULL)
  113. {
  114. ZeroMemory(&m_cpdi, sizeof(m_cpdi));
  115. }
  116. /////////////////////////////////////////////////////////////////////////////
  117. // IAppMode Interface Methods
  118. template <class T>
  119. STDMETHODIMP CAppModeDebug<T>::Run(int argc, TCHAR* argv[])
  120. {
  121. // Determine the default Web browser
  122. {
  123. CRegKey key;
  124. long lr = key.Open(HKEY_CLASSES_ROOT, TEXT("http\\shell\\open\\command"),
  125. KEY_READ);
  126. if (ERROR_SUCCESS != lr)
  127. return g.HandleError(HRESULT_FROM_WIN32(lr), IDS_E_NODEFBROWSER);
  128. HRESULT hr = LoadRegString(key, NULL, m_strBrowser);
  129. if (FAILED(hr))
  130. return g.HandleError(hr, IDS_E_NODEFBROWSER);
  131. if (m_strBrowser.IsEmpty())
  132. return g.HandleError(E_UNEXPECTED, IDS_E_NODEFBROWSER);
  133. // If the Web browser command doesn't have a %1, concatenate one onto it
  134. if (!_tcsstr(m_strBrowser, TEXT("%1")))
  135. m_strBrowser += TEXT(" \"%1\"");
  136. }
  137. // Read the registry to determine if a GUID already exists in the registry
  138. HRESULT hr = S_OK;
  139. HKEY hkeyRoot = g.HKEYFromString(g.GetConfigString("RegRoot"));
  140. ZString strKey(g.GetConfigString("RegKey"));
  141. {
  142. CRegKey key;
  143. long lr = key.Open(hkeyRoot, strKey, KEY_READ);
  144. if (SUCCEEDED(hr = (ERROR_SUCCESS == lr) ? S_OK : HRESULT_FROM_WIN32(lr)))
  145. hr = LoadRegString(key, TEXT("GUID"), m_strRegGUID);
  146. }
  147. // Create a GUID and enter it into the registry
  148. if (FAILED(hr))
  149. {
  150. // Create/open the registry key for writing
  151. CRegKey key;
  152. long lr = key.Create(hkeyRoot, strKey);
  153. if (ERROR_SUCCESS == lr)
  154. {
  155. // Create a GUID to uniquely identify this machine
  156. GUID guidReg;
  157. if (FAILED(hr = ::CoCreateGuid(&guidReg)))
  158. return g.HandleError(hr, IDS_E_CREATEGUID);
  159. // Convert the GUID to a string
  160. OLECHAR szGUIDReg[64];
  161. StringFromGUID2(guidReg, szGUIDReg, sizeofArray(szGUIDReg));
  162. // Save the GUID string
  163. USES_CONVERSION;
  164. m_strRegGUID = OLE2CT(szGUIDReg);
  165. // Save the GUID string to the registry
  166. lr = key.SetValue(m_strRegGUID, TEXT("GUID"));
  167. if (ERROR_SUCCESS != lr)
  168. m_strRegGUID.SetEmpty();
  169. }
  170. }
  171. // Create a GUID to uniquely identify this session
  172. GUID guid;
  173. if (FAILED(hr = ::CoCreateGuid(&guid)))
  174. return g.HandleError(hr, IDS_E_CREATEGUID);
  175. // Convert the GUID to a string
  176. OLECHAR szGUID[64];
  177. StringFromGUID2(guid, szGUID, sizeofArray(szGUID));
  178. // Save the GUID string
  179. USES_CONVERSION;
  180. m_strAppGUID = OLE2CT(szGUID);
  181. // Loop on Debug events
  182. bool bExit;
  183. do
  184. {
  185. // Wait for the next debug event
  186. DEBUG_EVENT de;
  187. if (!::WaitForDebugEvent(&de, INFINITE))
  188. return g.HandleError(HRESULT_FROM_WIN32(::GetLastError()),
  189. IDS_E_FMT_WAITFORDEBUGEVENT);
  190. // Process the debug event
  191. bExit = false;
  192. DWORD dwStatus = ProcessDebugEvent(&de, &bExit);
  193. // Continue the debug event
  194. if (!::ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwStatus))
  195. return g.HandleError(HRESULT_FROM_WIN32(::GetLastError()),
  196. IDS_E_FMT_CONTINUEDEBUGEVENT);
  197. } while (!bExit);
  198. // Indicate success
  199. return S_OK;
  200. }
  201. /////////////////////////////////////////////////////////////////////////////
  202. // Debug Event Handlers
  203. /////////////////////////////////////////////////////////////////////////////
  204. //
  205. template <class T>
  206. DWORD CAppModeDebug<T>::HandleException(bool* pbExit)
  207. {
  208. // Delegate to most-derived class
  209. T* pThis = static_cast<T*>(this);
  210. return pThis->OnException(pbExit);
  211. }
  212. /////////////////////////////////////////////////////////////////////////////
  213. //
  214. template <class T>
  215. DWORD CAppModeDebug<T>::HandleCreateThread(bool* pbExit)
  216. {
  217. // Save the thread information in the map
  218. m_Threads[m_pde->dwThreadId] = m_pde->u.CreateThread;
  219. // Delegate to most-derived class
  220. T* pThis = static_cast<T*>(this);
  221. DWORD dw = pThis->OnCreateThread(pbExit);
  222. // Return the result of the most-derived class
  223. return dw;
  224. }
  225. /////////////////////////////////////////////////////////////////////////////
  226. //
  227. template <class T>
  228. DWORD CAppModeDebug<T>::HandleCreateProcess(bool* pbExit)
  229. {
  230. // Save the process information
  231. m_dwProcessId = m_pde->dwProcessId;
  232. CopyMemory(&m_cpdi, &m_pde->u.CreateProcessInfo, sizeof(m_cpdi));
  233. // Save the thread information
  234. CREATE_THREAD_DEBUG_INFO ctdi =
  235. {
  236. m_cpdi.hThread,
  237. m_cpdi.lpThreadLocalBase,
  238. m_cpdi.lpStartAddress
  239. };
  240. m_Threads[m_pde->dwThreadId] = ctdi;
  241. // Save the process handle or id to be used for symbol table lookups
  242. m_hProcessSym = IsWin9x() ?
  243. reinterpret_cast<HANDLE>(m_dwProcessId) : m_cpdi.hProcess;
  244. // Resolve the image name, if available
  245. ZString strImageName;
  246. if (m_cpdi.lpImageName)
  247. {
  248. void* pvImageName = NULL;
  249. if (::ReadProcessMemory(m_cpdi.hProcess, m_cpdi.lpImageName,
  250. &pvImageName, sizeof(pvImageName), NULL) && pvImageName)
  251. strImageName = ReadProcessString(pvImageName, !!m_cpdi.fUnicode);
  252. }
  253. debugf("%08X - %s\n", m_cpdi.lpBaseOfImage, (LPCTSTR)strImageName);
  254. // Map the loaded address to the module name
  255. m_Images.Add(m_cpdi.lpBaseOfImage, strImageName);
  256. // Format an event name using the debuggee process ID
  257. TCHAR szEvent[24];
  258. _stprintf(szEvent, TEXT("MSRGuard_%08X"), m_pde->dwProcessId);
  259. // Create the named event to indicate that we're debugging the process
  260. m_shEvent = ::CreateEvent(NULL, false, false, szEvent);
  261. if (m_shEvent.IsNull())
  262. {
  263. g.HandleError(HRESULT_FROM_WIN32(::GetLastError()), IDS_E_CREATEEVENT);
  264. *pbExit = true;
  265. return DBG_CONTINUE;
  266. }
  267. // Delegate to most-derived class
  268. T* pThis = static_cast<T*>(this);
  269. DWORD dw = pThis->OnCreateProcess(pbExit);
  270. // Close the file handle, since we don't use it
  271. ::CloseHandle(m_cpdi.hFile);
  272. // Return the result of the most-derived class
  273. return dw;
  274. }
  275. /////////////////////////////////////////////////////////////////////////////
  276. //
  277. template <class T>
  278. DWORD CAppModeDebug<T>::HandleExitThread(bool* pbExit)
  279. {
  280. // Remove thread information from the map
  281. XThreadIt it = m_Threads.find(m_pde->dwThreadId);
  282. if (it != m_Threads.end())
  283. m_Threads.erase(it);
  284. // Delegate to most-derived class
  285. T* pThis = static_cast<T*>(this);
  286. return pThis->OnExitThread(pbExit);
  287. }
  288. /////////////////////////////////////////////////////////////////////////////
  289. //
  290. template <class T>
  291. DWORD CAppModeDebug<T>::HandleExitProcess(bool* pbExit)
  292. {
  293. // Destroy the named event to indicate that we're finished debugging
  294. m_shEvent = NULL;
  295. // Delegate to most-derived class
  296. T* pThis = static_cast<T*>(this);
  297. return pThis->OnExitProcess(pbExit);
  298. }
  299. /////////////////////////////////////////////////////////////////////////////
  300. //
  301. template <class T>
  302. DWORD CAppModeDebug<T>::HandleLoadDLL(bool* pbExit)
  303. {
  304. LOAD_DLL_DEBUG_INFO& lddi = m_pde->u.LoadDll;
  305. // Resolve the image name, if available
  306. ZString strImageName;
  307. if (lddi.lpImageName)
  308. {
  309. void* pvImageName = NULL;
  310. if (::ReadProcessMemory(m_cpdi.hProcess, lddi.lpImageName, &pvImageName,
  311. sizeof(pvImageName), NULL) && pvImageName)
  312. strImageName = ReadProcessString(pvImageName, !!lddi.fUnicode);
  313. }
  314. debugf("%08X - %s\n", lddi.lpBaseOfDll, (LPCTSTR)strImageName);
  315. // Map the loaded address to the module name
  316. m_Images.Add(lddi.lpBaseOfDll, strImageName);
  317. // Delegate to most-derived class
  318. T* pThis = static_cast<T*>(this);
  319. DWORD dw = pThis->OnLoadDLL(pbExit);
  320. // Close the file handle, since we don't use it
  321. ::CloseHandle(lddi.hFile);
  322. // Return the result of the most-derived class
  323. return dw;
  324. }
  325. /////////////////////////////////////////////////////////////////////////////
  326. //
  327. template <class T>
  328. DWORD CAppModeDebug<T>::HandleUnloadDLL(bool* pbExit)
  329. {
  330. // Remove the specified image from the map
  331. m_Images.Remove(m_pde->u.LoadDll.lpBaseOfDll);
  332. // Delegate to most-derived class
  333. T* pThis = static_cast<T*>(this);
  334. return pThis->OnUnloadDLL(pbExit);
  335. }
  336. /////////////////////////////////////////////////////////////////////////////
  337. //
  338. template <class T>
  339. DWORD CAppModeDebug<T>::HandleOutputDebugString(bool* pbExit)
  340. {
  341. OUTPUT_DEBUG_STRING_INFO& odsi = m_pde->u.DebugString;
  342. // Read the text string
  343. ZString str(ReadProcessString(odsi.lpDebugStringData, !!odsi.fUnicode,
  344. odsi.nDebugStringLength));
  345. debugf(str);
  346. // Determine if this is a message to MSRGuard
  347. const static TCHAR szMSRGuard[] = TEXT("$$MSRGuard:");
  348. const static int cchMSRGuard = sizeofArray(szMSRGuard) - 1;
  349. if (0 == _strnicmp(str, szMSRGuard, cchMSRGuard))
  350. {
  351. int iFind = str.Find(':', cchMSRGuard);
  352. if (-1 != iFind)
  353. {
  354. ZString strKeyword(LPCTSTR(str) + cchMSRGuard, iFind - cchMSRGuard);
  355. ZString strText(LPCTSTR(str) + iFind + 1);
  356. if (strText.Right(1) == "\n")
  357. strText = strText.Left(strText.GetLength() - 1);
  358. if (0 == _stricmp(strKeyword, "set"))
  359. {
  360. ZString strValue;
  361. if (-1 != (iFind = strText.Find('=')))
  362. {
  363. strValue = strText.RightOf(iFind + 1);
  364. strText = strText.Left(iFind);
  365. }
  366. m_Params[strText] = strValue;
  367. }
  368. else if (0 == _stricmp(strKeyword, "remove"))
  369. {
  370. XParamIt it = m_Params.find(strText);
  371. if (it != m_Params.end())
  372. m_Params.erase(it);
  373. }
  374. }
  375. }
  376. // Delegate to most-derived class
  377. T* pThis = static_cast<T*>(this);
  378. return pThis->OnOutputDebugString(pbExit);
  379. }
  380. /////////////////////////////////////////////////////////////////////////////
  381. //
  382. template <class T>
  383. DWORD CAppModeDebug<T>::HandleRIP(bool* pbExit)
  384. {
  385. // Delegate to most-derived class
  386. T* pThis = static_cast<T*>(this);
  387. return pThis->OnRIP(pbExit);
  388. }
  389. /////////////////////////////////////////////////////////////////////////////
  390. //
  391. template <class T>
  392. DWORD CAppModeDebug<T>::HandleOther(bool* pbExit)
  393. {
  394. debugf("CAppModeDebug<%hs>::HandleOther(): dwDebugEventCode = %d (0x%08X)\n",
  395. TCTypeName(T), m_pde->dwDebugEventCode, m_pde->dwDebugEventCode);
  396. // Delegate to most-derived class
  397. T* pThis = static_cast<T*>(this);
  398. return pThis->OnOther(pbExit);
  399. }
  400. /////////////////////////////////////////////////////////////////////////////
  401. // Implementation
  402. /////////////////////////////////////////////////////////////////////////////
  403. //
  404. template <class T>
  405. DWORD CAppModeDebug<T>::ProcessDebugEvent(DEBUG_EVENT* pde, bool* pbExit)
  406. {
  407. // Save the current debug event
  408. m_pde = pde;
  409. // Types
  410. typedef DWORD (CAppModeDebug<T>::*XEventHandlerProc)(bool*);
  411. struct XEventHandler
  412. {
  413. DWORD m_dwDebugEventCode;
  414. XEventHandlerProc m_pfn;
  415. LPCSTR m_pszFn;
  416. };
  417. typedef const XEventHandler* XEventHandlerIt;
  418. // Macros
  419. #define CAppModeDebug_Handler(ev, handler) {ev, Handle##handler, #handler},
  420. // Static Initialization
  421. const static XEventHandler s_EventHandlers[] =
  422. {
  423. CAppModeDebug_Handler(EXCEPTION_DEBUG_EVENT , Exception )
  424. CAppModeDebug_Handler(CREATE_THREAD_DEBUG_EVENT , CreateThread )
  425. CAppModeDebug_Handler(CREATE_PROCESS_DEBUG_EVENT, CreateProcess )
  426. CAppModeDebug_Handler(EXIT_THREAD_DEBUG_EVENT , ExitThread )
  427. CAppModeDebug_Handler(EXIT_PROCESS_DEBUG_EVENT , ExitProcess )
  428. CAppModeDebug_Handler(LOAD_DLL_DEBUG_EVENT , LoadDLL )
  429. CAppModeDebug_Handler(UNLOAD_DLL_DEBUG_EVENT , UnloadDLL )
  430. CAppModeDebug_Handler(OUTPUT_DEBUG_STRING_EVENT , OutputDebugString)
  431. CAppModeDebug_Handler(RIP_EVENT , RIP )
  432. };
  433. const static XEventHandlerIt itEnd =
  434. s_EventHandlers + sizeofArray(s_EventHandlers);
  435. // Find the handler for the specified debug event
  436. for (XEventHandlerIt it = s_EventHandlers; it != itEnd; ++it)
  437. {
  438. if (it->m_dwDebugEventCode == pde->dwDebugEventCode)
  439. {
  440. debugf("CAppModeDebug<%hs>::Handle%hs()\n", TCTypeName(T), it->m_pszFn);
  441. return (this->*it->m_pfn)(pbExit);
  442. }
  443. }
  444. return HandleOther(pbExit);
  445. }
  446. /////////////////////////////////////////////////////////////////////////////
  447. //
  448. template <class T>
  449. HRESULT CAppModeDebug<T>::ReportException()
  450. {
  451. // Initialize the symbol engine options
  452. DWORD dwOptions = SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
  453. // Initialize the symbol engine
  454. ZString strSymbolPath(GetSymbolPath());
  455. int cch = strSymbolPath.GetLength() + 1;
  456. LPTSTR pszSymbolPath = (LPTSTR)_alloca(cch * sizeof(TCHAR));
  457. lstrcpy(pszSymbolPath, strSymbolPath);
  458. if (!::SymInitialize(m_hProcessSym, pszSymbolPath, true))
  459. return HRESULT_FROM_WIN32(::GetLastError());
  460. // Enumerate the loaded modules
  461. if (m_Images.Enumerate(m_cpdi.hProcess, m_hProcessSym))
  462. {
  463. m_Images.Dump();
  464. }
  465. else
  466. {
  467. DWORD dwLastError = ::GetLastError();
  468. debugf("CLoadedModules::Enumerate failed, LastError = 0x%08X\n", dwLastError);
  469. }
  470. // Compose the exception report parameters
  471. HRESULT hr = ComposeExceptionReportParams();
  472. // Terminate the symbol engine
  473. ::SymCleanup(m_hProcessSym);
  474. // Terminate the debugee process
  475. ::TerminateProcess(m_cpdi.hProcess, UINT(-1));
  476. // Return if last call failed
  477. RETURN_FAILED(hr);
  478. // Create the HiddenFields string
  479. ZString strHiddenFields;
  480. for (XParamIt it = m_Params.begin(); it != m_Params.end(); ++it)
  481. {
  482. ZString str;
  483. RETURN_FAILED(g.FormatString(str, IDS_FMT_HIDDEN_FIELD,
  484. (LPCTSTR)it->first, (LPCTSTR)it->second));
  485. strHiddenFields += str;
  486. }
  487. // Get the HTML text
  488. ZString strHTML;
  489. ZSucceeded(g.LoadHTML(strHTML));
  490. // Replace %Title% in the HTML with the title
  491. cch = 1 + ::TCReplaceTextNoCase(strHTML, TEXT("%Title%"), g.GetTitle(), NULL, 0);
  492. LPTSTR psz = new TCHAR[cch];
  493. ::TCReplaceTextNoCase(strHTML, TEXT("%Title%"), g.GetTitle(), psz, cch);
  494. // Replace %URLBase% in the HTML with the URLBase
  495. cch = 1 + ::TCReplaceTextNoCase(psz, TEXT("%URLBase%"),
  496. g.GetConfigString("URLBase"), NULL, 0);
  497. LPTSTR psz2 = new TCHAR[cch];
  498. ::TCReplaceTextNoCase(psz, TEXT("%URLBase%"), g.GetConfigString("URLBase"), psz2, cch);
  499. delete [] psz;
  500. // Replace %HiddenFields% with the hidden fields
  501. cch = 1 + ::TCReplaceTextNoCase(psz2, TEXT("%HiddenFields%"), strHiddenFields, NULL, 0);
  502. LPTSTR psz3 = new TCHAR[cch];
  503. ::TCReplaceTextNoCase(psz2, TEXT("%HiddenFields%"), strHiddenFields, psz3, cch);
  504. delete [] psz2;
  505. // Save the converted HTML back to the ZString
  506. strHTML = psz3;
  507. delete [] psz3;
  508. // Create a temporary filename
  509. TCHAR szTempPath[_MAX_PATH * 2];
  510. if (!::GetTempPath(sizeofArray(szTempPath), szTempPath))
  511. return HRESULT_FROM_WIN32(::GetLastError());
  512. lstrcat(szTempPath, TEXT("MSRGReq.htm"));
  513. // Open the temporary file for writing
  514. DWORD dwFlags = FILE_ATTRIBUTE_TEMPORARY;
  515. TCHandle shFile = ::CreateFile(szTempPath, GENERIC_WRITE, FILE_SHARE_READ,
  516. NULL, CREATE_ALWAYS, dwFlags, NULL);
  517. if (INVALID_HANDLE_VALUE == shFile.GetHandle())
  518. return HRESULT_FROM_WIN32(::GetLastError());
  519. // Write the converted HTML to the file
  520. DWORD dwWritten;
  521. if (!::WriteFile(shFile, strHTML, strHTML.GetLength() * sizeof(TCHAR),
  522. &dwWritten, NULL))
  523. return HRESULT_FROM_WIN32(::GetLastError());
  524. // Close the file
  525. shFile = NULL;
  526. // Send the report to the web site, if specified
  527. if (!g.GetConfigBool("LogExceptionToURL"))
  528. return S_OK;
  529. // Format the browser URL command line string
  530. dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
  531. FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY;
  532. LPTSTR pszTempURL = szTempPath;
  533. LPTSTR pszCommand = NULL;
  534. ::FormatMessage(dwFlags, (PCC)m_strBrowser, 0, 0,
  535. reinterpret_cast<LPTSTR>(&pszCommand), 1, &pszTempURL);
  536. // Create the Web browser process
  537. hr = S_OK;
  538. PROCESS_INFORMATION pi;
  539. STARTUPINFO si = {sizeof(si)};
  540. if (!::CreateProcess(NULL, pszCommand, NULL, NULL, false, 0, NULL, NULL, &si, &pi))
  541. hr = HRESULT_FROM_WIN32(::GetLastError());
  542. // Free the formatted URL command line string
  543. ::LocalFree(pszCommand);
  544. // Return the last HRESULT
  545. return hr;
  546. }
  547. /////////////////////////////////////////////////////////////////////////////
  548. //
  549. template <class T>
  550. HRESULT CAppModeDebug<T>::ComposeExceptionReportParams()
  551. {
  552. // General parameters
  553. m_Params["Title" ] = g.GetConfigString("Title");
  554. m_Params["AppName"] = g.GetConfigString("ApplicationName");
  555. m_Params["RegGUID"] = m_strRegGUID;
  556. m_Params["AppGUID"] = m_strAppGUID;
  557. m_Params["Action" ] = "Exception";
  558. // Get the full path and version of the this guard application
  559. TCHAR szGuardPath[_MAX_PATH * 2];
  560. ::GetModuleFileName(NULL, szGuardPath, sizeofArray(szGuardPath));
  561. m_Params["GuardPath"] = szGuardPath;
  562. // Get the VersionInfo of this guard application
  563. ZVersionInfo vi(szGuardPath);
  564. m_Params["GuardVer"] = vi.GetFileVersionString();
  565. // Get the full path and version of the debuggee application
  566. CLoadedModuleIt itApp = m_Images.find(m_cpdi.lpBaseOfImage);
  567. if (itApp != m_Images.end())
  568. {
  569. ZString strAppPath(itApp->second.m_strModuleName);
  570. m_Params["AppPath"] = strAppPath;
  571. // Fix the AppName, if needed
  572. if (m_Params["AppName"].IsEmpty())
  573. {
  574. TCHAR szAppName[_MAX_PATH], szFName[_MAX_FNAME], szExt[_MAX_EXT];
  575. _tsplitpath(strAppPath, NULL, NULL, szFName, szExt);
  576. _tmakepath(szAppName, NULL, NULL, szFName, szExt);
  577. m_Params["AppName"] = szAppName;
  578. }
  579. // Get the VersionInfo of the debuggee application
  580. ZVersionInfo vi(strAppPath);
  581. m_Params["AppVer"] = vi.GetFileVersionString();
  582. }
  583. // Format the exception code and address
  584. TCHAR szBuf[16];
  585. _stprintf(szBuf, TEXT("%08X"), m_pde->u.Exception.ExceptionRecord.ExceptionCode);
  586. m_Params["XCode"] = szBuf;
  587. _stprintf(szBuf, TEXT("%08X"), m_pde->u.Exception.ExceptionRecord.ExceptionAddress);
  588. m_Params["XAddr"] = szBuf;
  589. // Find the module of the exception address
  590. m_Params["XModule"] = GetAddressModule(m_pde->u.Exception.ExceptionRecord.ExceptionAddress);
  591. // Get the exception name
  592. m_Params["XName"] = GetExceptionName(m_pde->u.Exception.ExceptionRecord.ExceptionCode);
  593. // Get the entire exception state
  594. ZString strExceptionState;
  595. RETURN_FAILED(GetExceptionState(strExceptionState));
  596. m_Params["XState"] = strExceptionState;
  597. // Get the machine.txt
  598. ZString strMachineText;
  599. HRESULT hr = GetMachineText(strMachineText);
  600. if (S_OK != hr)
  601. {
  602. strMachineText += "\r\n\r\n";
  603. strMachineText += g.GetErrorString(hr);
  604. }
  605. m_Params["Machine"] = strMachineText;
  606. // Canonicalize each value
  607. for (XParamIt it = m_Params.begin(); it != m_Params.end(); ++it)
  608. {
  609. ZString str;
  610. RETURN_FAILED(CanonicalizeParamText(it->second, str));
  611. it->second = str;
  612. }
  613. // Indicate success
  614. return S_OK;
  615. }
  616. /////////////////////////////////////////////////////////////////////////////
  617. //
  618. template <class T>
  619. HRESULT CAppModeDebug<T>::CanonicalizeParamText(ZString strIn, ZString& strOut)
  620. {
  621. // Create an output stream
  622. IStreamPtr spStmOut;
  623. HGLOBAL hGlobal = ::GlobalAlloc(GHND, 0);
  624. RETURN_FAILED(::CreateStreamOnHGlobal(hGlobal, true, &spStmOut));
  625. // Loop through the input string, copying/transforming text to the output
  626. const LPCTSTR pszTokens = TEXT("\"&<>");
  627. const LPCTSTR pszEnd = (PCC)strIn + strIn.GetLength();
  628. for (LPCTSTR psz = strIn; psz != pszEnd; ++psz)
  629. {
  630. if (_istprint(*psz) && !_tcschr(pszTokens, *psz))
  631. {
  632. RETURN_FAILED(spStmOut->Write(psz, sizeof(TCHAR), NULL));
  633. }
  634. else
  635. {
  636. TCHAR szBuf[16];
  637. int cch = _stprintf(szBuf, TEXT("&#%02u;"), *psz);
  638. RETURN_FAILED(spStmOut->Write(szBuf, cch * sizeof(TCHAR), NULL));
  639. }
  640. }
  641. // Null-terminate the data in the stream
  642. TCHAR szNull = TEXT('\0');
  643. RETURN_FAILED(spStmOut->Write(&szNull, sizeof(szNull), NULL));
  644. // Get the HGLOBAL from the stream
  645. RETURN_FAILED(GetHGlobalFromStream(spStmOut, &hGlobal));
  646. // Lock the HGLOBAL into memory
  647. TCGlobalPtr spvStream = ::GlobalLock(hGlobal);
  648. // Create a ZString from the stream
  649. strOut = reinterpret_cast<LPCTSTR>(spvStream.GetHandle());
  650. // Indicate success
  651. return S_OK;
  652. }
  653. /////////////////////////////////////////////////////////////////////////////
  654. //
  655. template <class T>
  656. HRESULT CAppModeDebug<T>::GetExceptionState(ZString& strOut)
  657. {
  658. ZString str;
  659. // Create a memory stream
  660. IStreamPtr spStm;
  661. RETURN_FAILED(::CreateStreamOnHGlobal(NULL, true, &spStm));
  662. // Format the <State> XML tag
  663. RETURN_FAILED(g.LoadString(str, IDS_STATE_BEGIN));
  664. WRITE_STRING_TO_STREAM(spStm, str);
  665. // Format the <Threads> XML tag
  666. RETURN_FAILED(g.LoadString(str, IDS_THREADS_BEGIN));
  667. WRITE_STRING_TO_STREAM(spStm, str);
  668. // Format the full exception state for the exception thread first
  669. RETURN_FAILED(FormatThreadState(spStm, m_pde->dwThreadId, true));
  670. // Format exception state for the rest of the threads
  671. bool bFull = g.GetConfigBool("LogFullStateOfAllThreads");
  672. for (XThreadIt itTh = m_Threads.begin(); itTh != m_Threads.end(); ++itTh)
  673. if (itTh->first != m_pde->dwThreadId)
  674. RETURN_FAILED(FormatThreadState(spStm, itTh->first, bFull));
  675. // Format the </Threads> XML tag
  676. RETURN_FAILED(g.LoadString(str, IDS_THREADS_END));
  677. WRITE_STRING_TO_STREAM(spStm, str);
  678. // Format the <Modules> XML tag
  679. RETURN_FAILED(g.LoadString(str, IDS_MODULES_BEGIN));
  680. WRITE_STRING_TO_STREAM(spStm, str);
  681. // Format the module info for each loaded module
  682. for (CLoadedModuleIt it = m_Images.begin(); it != m_Images.end(); ++it)
  683. RETURN_FAILED(FormatModuleInfo(spStm, it));
  684. // Format the </Modules> XML tag
  685. RETURN_FAILED(g.LoadString(str, IDS_MODULES_END));
  686. WRITE_STRING_TO_STREAM(spStm, str);
  687. // Format the <Processes> XML tag
  688. RETURN_FAILED(g.LoadString(str, IDS_PROCESSES_BEGIN));
  689. WRITE_STRING_TO_STREAM(spStm, str);
  690. // Format the process infor for each process in the system
  691. RETURN_FAILED(FormatProcessesInfo(spStm));
  692. // Format the </Processes> XML tag
  693. RETURN_FAILED(g.LoadString(str, IDS_PROCESSES_END));
  694. WRITE_STRING_TO_STREAM(spStm, str);
  695. // Format the </State> XML tag
  696. RETURN_FAILED(g.LoadString(str, IDS_STATE_END));
  697. WRITE_STRING_TO_STREAM(spStm, str);
  698. // Get the size of the IStream
  699. LARGE_INTEGER li = {0};
  700. ULARGE_INTEGER uli;
  701. RETURN_FAILED(spStm->Seek(li, STREAM_SEEK_END, &uli));
  702. assert(0 == uli.HighPart);
  703. // Get the HGLOBAL from the stream
  704. HGLOBAL hGlobal = NULL;
  705. RETURN_FAILED(::GetHGlobalFromStream(spStm, &hGlobal));
  706. // Lock the HGLOBAL
  707. LPCTSTR psz = reinterpret_cast<LPCTSTR>(::GlobalLock(hGlobal));
  708. assert(psz);
  709. // Create a string from the stream
  710. ZString strFromStream(psz, (int)uli.LowPart);
  711. // Unlock the global
  712. ::GlobalUnlock(hGlobal);
  713. // Copy the string to the [out] parameters
  714. strOut = strFromStream;
  715. // Write the XML to a file, if specified (usually for debugging)
  716. ZString strXMLFile(g.GetConfigString("LogXStateToFile"));
  717. if (!strXMLFile.IsEmpty() || "0" == strXMLFile)
  718. {
  719. TCHandle shFile = ::CreateFile(strXMLFile, GENERIC_WRITE,
  720. FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
  721. if (!shFile.IsNull() && INVALID_HANDLE_VALUE != shFile.GetHandle())
  722. {
  723. DWORD dwWritten;
  724. ::WriteFile(shFile, (LPCTSTR)strOut, strOut.GetLength(), &dwWritten, NULL);
  725. }
  726. }
  727. // Indicate success
  728. return S_OK;
  729. }
  730. /////////////////////////////////////////////////////////////////////////////
  731. //
  732. template <class T>
  733. HRESULT CAppModeDebug<T>::GetMachineText(ZString& strOut)
  734. {
  735. // Load the MINI resource
  736. ZString strMINI;
  737. RETURN_FAILED(g.LoadMINI(strMINI));
  738. // Get the temporary pathname
  739. TCHAR szTempPath[_MAX_PATH * 2];
  740. if (!::GetTempPath(sizeofArray(szTempPath), szTempPath))
  741. {
  742. HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
  743. strOut = "GetTempPath";
  744. return hr;
  745. }
  746. // Create temporary filenames for Machine.exe, Machine.ini, and Machine.txt
  747. TCHAR szMachineExe[_MAX_PATH * 2];
  748. lstrcpy(szMachineExe, szTempPath);
  749. lstrcat(szMachineExe, TEXT("MSRGMach.exe"));
  750. TCHAR szMachineIni[_MAX_PATH * 2];
  751. lstrcpy(szMachineIni, szTempPath);
  752. lstrcat(szMachineIni, TEXT("Machine.ini"));
  753. TCHAR szMachineTxt[_MAX_PATH * 2];
  754. lstrcpy(szMachineTxt, szTempPath);
  755. lstrcat(szMachineTxt, TEXT("Machine.txt"));
  756. // Get the current module pathname
  757. TCHAR szModulePath[_MAX_PATH];
  758. ::GetModuleFileName(NULL, szModulePath, sizeofArray(szModulePath));
  759. // Attempt to find machine.exe in the same path as the current module
  760. TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR];
  761. _tsplitpath(szModulePath, szDrive, szDir, NULL, NULL);
  762. _tmakepath(szModulePath, szDrive, szDir, TEXT("machine"), TEXT(".exe"));
  763. // Load the 'Machine not available' prefix string, in case we need it
  764. ZString strMachineNA;
  765. RETURN_FAILED(g.LoadString(strMachineNA, IDS_E_MACHINE_NA));
  766. LPCTSTR pszMachineNA = strMachineNA;
  767. // Attempt to copy the machine.exe file to a temporary file
  768. if (!::CopyFile(szModulePath, szMachineExe, false))
  769. {
  770. HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
  771. g.FormatString(strOut, "%sCopyFile(%s, %s)", pszMachineNA, szModulePath,
  772. szMachineExe);
  773. return hr;
  774. }
  775. // Create the INI file for writing
  776. TCHandle shFile = ::CreateFile(szMachineIni, GENERIC_WRITE, 0, NULL,
  777. CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
  778. if (INVALID_HANDLE_VALUE == shFile.GetHandle())
  779. {
  780. HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
  781. ::DeleteFile(szMachineExe);
  782. g.FormatString(strOut, "%sCreateFile(%s)", pszMachineNA, szMachineIni);
  783. return hr;
  784. }
  785. // Write the contents of the INI file
  786. DWORD dwWritten;
  787. if (!::WriteFile(shFile, (const void*)(LPCTSTR)strMINI,
  788. strMINI.GetLength() * sizeof(TCHAR), &dwWritten, NULL))
  789. {
  790. HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
  791. shFile = NULL;
  792. ::DeleteFile(szMachineExe);
  793. ::DeleteFile(szMachineIni);
  794. g.FormatString(strOut, "%sWriteFile(%s)", pszMachineNA, szMachineIni);
  795. return hr;
  796. }
  797. // Close the file
  798. shFile = NULL;
  799. // Delete the current machine.txt, if any
  800. ::DeleteFile(szMachineTxt);
  801. // Compose the machine.exe command line
  802. TCHAR szCommand[_MAX_PATH * 2];
  803. lstrcpy(szCommand, szMachineExe);
  804. lstrcat(szCommand, " -l");
  805. // Execute the machine.exe command line (hidden, if possible)
  806. PROCESS_INFORMATION pi;
  807. STARTUPINFO si = {sizeof(si)};
  808. si.dwFlags = STARTF_USESHOWWINDOW;
  809. si.wShowWindow = SW_HIDE;
  810. if (!::CreateProcess(NULL, szCommand, NULL, NULL, false, 0, NULL, NULL, &si, &pi))
  811. {
  812. HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
  813. ::DeleteFile(szMachineExe);
  814. ::DeleteFile(szMachineIni);
  815. g.FormatString(strOut, "%sCreateProcess(%s)", pszMachineNA, szCommand);
  816. return hr;
  817. }
  818. // Wait for the machine.exe process to exit
  819. const DWORD c_nTimeOutSeconds = 30;
  820. const DWORD c_dwTimeOut = c_nTimeOutSeconds * 1000;
  821. if (WAIT_TIMEOUT == ::WaitForSingleObject(pi.hProcess, c_dwTimeOut))
  822. {
  823. ::TerminateProcess(pi.hProcess, 1);
  824. ::DeleteFile(szMachineExe);
  825. ::DeleteFile(szMachineIni);
  826. g.FormatString(strOut,
  827. "%sMachine.exe failed to complete within %d seconds",
  828. pszMachineNA, c_nTimeOutSeconds);
  829. return S_FALSE;
  830. }
  831. // Delete the temporary files
  832. ::DeleteFile(szMachineExe);
  833. ::DeleteFile(szMachineIni);
  834. // Open the machine.txt file
  835. {
  836. ZFile file(szMachineTxt);
  837. ZString strText((LPCTSTR)file.GetPointer(), file.GetLength());
  838. strOut = strText;
  839. }
  840. // Delete the machine.txt file
  841. ::DeleteFile(szMachineTxt);
  842. // Indicate success
  843. return S_OK;
  844. }
  845. /////////////////////////////////////////////////////////////////////////////
  846. //
  847. template <class T>
  848. ZString CAppModeDebug<T>::ReadProcessString(void* pvAddr, bool fUnicode, int cch)
  849. {
  850. if (fUnicode)
  851. {
  852. const int cbChar = sizeof(WCHAR);
  853. if (-1 == cch)
  854. {
  855. cch = 0;
  856. WCHAR cChar;
  857. WCHAR* p = reinterpret_cast<WCHAR*>(pvAddr);
  858. while (true)
  859. {
  860. ::ReadProcessMemory(m_cpdi.hProcess, p, &cChar, cbChar, NULL);
  861. if (L'\0' == cChar)
  862. break;
  863. ++cch;
  864. ++p;
  865. }
  866. }
  867. WCHAR* psz = new WCHAR[cch + 1];
  868. ::ReadProcessMemory(m_cpdi.hProcess, pvAddr, psz, cch * cbChar, NULL);
  869. psz[cch] = L'\0';
  870. USES_CONVERSION;
  871. ZString str(W2CT(psz), cch);
  872. delete [] psz;
  873. return str;
  874. }
  875. else
  876. {
  877. const int cbChar = sizeof(CHAR);
  878. if (-1 == cch)
  879. {
  880. cch = 0;
  881. CHAR cChar;
  882. CHAR* p = reinterpret_cast<CHAR*>(pvAddr);
  883. while (true)
  884. {
  885. ::ReadProcessMemory(m_cpdi.hProcess, p, &cChar, cbChar, NULL);
  886. if ('\0' == cChar)
  887. break;
  888. ++cch;
  889. ++p;
  890. }
  891. }
  892. CHAR* psz = new CHAR[cch + 1];
  893. ::ReadProcessMemory(m_cpdi.hProcess, pvAddr, psz, cch * cbChar, NULL);
  894. psz[cch] = '\0';
  895. USES_CONVERSION;
  896. ZString str(A2CT(psz), cch);
  897. delete [] psz;
  898. return str;
  899. }
  900. }
  901. /////////////////////////////////////////////////////////////////////////////
  902. //
  903. template <class T>
  904. LPCTSTR CAppModeDebug<T>::GetExceptionName(DWORD dwExceptionCode)
  905. {
  906. #define CAppModeDebug_ExceptionName(x) case EXCEPTION_##x: return TEXT(#x);
  907. switch (dwExceptionCode)
  908. {
  909. CAppModeDebug_ExceptionName(ACCESS_VIOLATION)
  910. CAppModeDebug_ExceptionName(ARRAY_BOUNDS_EXCEEDED)
  911. CAppModeDebug_ExceptionName(BREAKPOINT)
  912. CAppModeDebug_ExceptionName(DATATYPE_MISALIGNMENT)
  913. CAppModeDebug_ExceptionName(FLT_DENORMAL_OPERAND)
  914. CAppModeDebug_ExceptionName(FLT_DIVIDE_BY_ZERO)
  915. CAppModeDebug_ExceptionName(FLT_INEXACT_RESULT)
  916. CAppModeDebug_ExceptionName(FLT_INVALID_OPERATION)
  917. CAppModeDebug_ExceptionName(FLT_OVERFLOW)
  918. CAppModeDebug_ExceptionName(FLT_STACK_CHECK)
  919. CAppModeDebug_ExceptionName(FLT_UNDERFLOW)
  920. CAppModeDebug_ExceptionName(ILLEGAL_INSTRUCTION)
  921. CAppModeDebug_ExceptionName(IN_PAGE_ERROR)
  922. CAppModeDebug_ExceptionName(INT_DIVIDE_BY_ZERO)
  923. CAppModeDebug_ExceptionName(INT_OVERFLOW)
  924. CAppModeDebug_ExceptionName(INVALID_DISPOSITION)
  925. CAppModeDebug_ExceptionName(NONCONTINUABLE_EXCEPTION)
  926. CAppModeDebug_ExceptionName(PRIV_INSTRUCTION)
  927. CAppModeDebug_ExceptionName(SINGLE_STEP)
  928. CAppModeDebug_ExceptionName(STACK_OVERFLOW)
  929. }
  930. return TEXT("unrecognized exception");
  931. }
  932. /////////////////////////////////////////////////////////////////////////////
  933. //
  934. template <class T>
  935. ZString CAppModeDebug<T>::GetAddressModule(void* pvAddr)
  936. {
  937. // Get the base load address of the module containing the specified address
  938. DWORD dwAddr = reinterpret_cast<DWORD>(pvAddr);
  939. DWORD dwBase = ::SymGetModuleBase(m_hProcessSym, dwAddr);
  940. pvAddr = reinterpret_cast<void*>(dwBase);
  941. // Find the module of the specified address
  942. CLoadedModuleIt itMod = m_Images.find(pvAddr);
  943. ZString strModule;
  944. if (itMod != m_Images.end())
  945. {
  946. TCHAR szModule[_MAX_PATH], szFName[_MAX_FNAME], szExt[_MAX_EXT];
  947. _tsplitpath(itMod->second.m_strModuleName, NULL, NULL, szFName, szExt);
  948. _tmakepath(szModule, NULL, NULL, szFName, szExt);
  949. strModule = szModule;
  950. }
  951. else
  952. {
  953. g.LoadString(strModule, IDS_MODULE_UNKNOWN);
  954. }
  955. return strModule;
  956. }
  957. /////////////////////////////////////////////////////////////////////////////
  958. //
  959. template <class T>
  960. HRESULT CAppModeDebug<T>::FormatThreadState(IStream* pStm, DWORD dwThreadId,
  961. bool bFull)
  962. {
  963. ZString str;
  964. // Get the thread info of the specified thread
  965. XThreadIt it = m_Threads.find(dwThreadId);
  966. assert(it != m_Threads.end());
  967. // Attempt to find the module name for the thread start address
  968. ZString strModule(GetAddressModule(it->second.lpStartAddress));
  969. // Lookup the symbol name of the thread start address
  970. ZString strSymbol;
  971. DWORD dwDisplacement;
  972. BYTE symBuffer[sizeof(IMAGEHLP_SYMBOL) + 1024];
  973. PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer;
  974. if (::SymGetSymFromAddr(m_hProcessSym,
  975. reinterpret_cast<DWORD>(it->second.lpStartAddress), &dwDisplacement, sym))
  976. strSymbol = sym->Name;
  977. else
  978. g.LoadString(strSymbol, IDS_NO_SYMBOLS);
  979. // Format the <Thread> XML tag for the specified thread
  980. RETURN_FAILED(g.FormatString(str, IDS_FMT_THREAD_BEGIN, dwThreadId,
  981. it->second.lpStartAddress, (LPCTSTR)strModule, (LPCTSTR)strSymbol));
  982. WRITE_STRING_TO_STREAM(pStm, str);
  983. // The rest is considered "Full" thread state
  984. if (bFull)
  985. {
  986. HANDLE hThread = it->second.hThread;
  987. // Get the thread context for the specified thread
  988. CONTEXT ctx = {CONTEXT_FULL};
  989. if (!::GetThreadContext(hThread, &ctx))
  990. return HRESULT_FROM_WIN32(::GetLastError());
  991. // Format the <Registers> XML tag for the specified thread
  992. RETURN_FAILED(g.FormatString(str, IDS_FMT_REGISTERS,
  993. ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx, ctx.Esi, ctx.Edi,
  994. ctx.Ebp, ctx.Esp, ctx.Eip, ctx.EFlags, ctx.SegCs, ctx.SegDs,
  995. ctx.SegSs, ctx.SegEs, ctx.SegFs, ctx.SegGs));
  996. WRITE_STRING_TO_STREAM(pStm, str);
  997. // Format some bytes from the stack
  998. const int c_cStackBytes = 320;
  999. TCHAR szBytes[c_cStackBytes * 2 + 1];
  1000. // Read some bytes from the thread's stack
  1001. BYTE bBytes[c_cStackBytes];
  1002. DWORD dwRead = 0;
  1003. if (::ReadProcessMemory(m_cpdi.hProcess,
  1004. reinterpret_cast<void*>(ctx.Esp), bBytes, sizeof(bBytes), &dwRead))
  1005. {
  1006. // Format the bytes from the stack
  1007. for (int i = 0, j = 0; i < c_cStackBytes; ++i, j += 2)
  1008. _stprintf(szBytes + j, "%02X", bBytes[i]);
  1009. szBytes[j] = TEXT('\0');
  1010. }
  1011. else
  1012. {
  1013. szBytes[0] = TEXT('\0');
  1014. }
  1015. // Format the <Stack> XML tag
  1016. RETURN_FAILED(g.FormatString(str, IDS_FMT_STACK_BEGIN, szBytes));
  1017. WRITE_STRING_TO_STREAM(pStm, str);
  1018. // Initialize the STACKFRAME structure
  1019. STACKFRAME stk;
  1020. ZeroMemory(&stk, sizeof(stk));
  1021. stk.AddrPC.Offset = ctx.Eip;
  1022. stk.AddrPC.Mode = AddrModeFlat;
  1023. stk.AddrStack.Offset = ctx.Esp;
  1024. stk.AddrStack.Mode = AddrModeFlat;
  1025. stk.AddrFrame.Offset = ctx.Ebp;
  1026. stk.AddrFrame.Mode = AddrModeFlat;
  1027. // Walk the stack
  1028. DWORD dwPrevFrame = stk.AddrFrame.Offset;
  1029. const int c_nMaxFrames = 100;
  1030. for (int f = 0; f < c_nMaxFrames; ++f)
  1031. {
  1032. const DWORD c_dwMachineType = IMAGE_FILE_MACHINE_I386;
  1033. bool bFrame = !!::StackWalk(c_dwMachineType,
  1034. reinterpret_cast<HANDLE>(this), hThread, &stk, &ctx,
  1035. ReadProcessMemoryProc, FunctionTableAccessProc, GetModuleBaseProc,
  1036. TranslateAddressProc);
  1037. if (!bFrame)
  1038. break;
  1039. // Format the stack frame
  1040. RETURN_FAILED(FormatStackFrame(pStm, stk));
  1041. // Avoid endlessly repeating frames
  1042. if (f && dwPrevFrame == stk.AddrFrame.Offset)
  1043. break;
  1044. dwPrevFrame = stk.AddrFrame.Offset;
  1045. }
  1046. // Format the </Stack> XML tag
  1047. RETURN_FAILED(g.LoadString(str, IDS_STACK_END));
  1048. WRITE_STRING_TO_STREAM(pStm, str);
  1049. }
  1050. // Format the </Thread> XML tag
  1051. RETURN_FAILED(g.LoadString(str, IDS_THREAD_END))
  1052. WRITE_STRING_TO_STREAM(pStm, str);
  1053. // Indicate success
  1054. return S_OK;
  1055. }
  1056. /////////////////////////////////////////////////////////////////////////////
  1057. //
  1058. template <class T>
  1059. HRESULT CAppModeDebug<T>::FormatStackFrame(IStream* pStm, STACKFRAME& stk)
  1060. {
  1061. BYTE symBuffer[sizeof(IMAGEHLP_SYMBOL) + 1024];
  1062. PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer;
  1063. // Attempt to lookup the symbol name for the frame
  1064. ZString strSymbol;
  1065. DWORD dwDisplacement;
  1066. if (::SymGetSymFromAddr(m_hProcessSym, stk.AddrPC.Offset, &dwDisplacement, sym))
  1067. strSymbol = sym->Name;
  1068. else
  1069. g.LoadString(strSymbol, IDS_NO_SYMBOLS);
  1070. // Attempt to find the module name for the frame
  1071. ZString strModule(GetAddressModule(reinterpret_cast<void*>(stk.AddrPC.Offset)));
  1072. // Format the <Frm> XML tag
  1073. ZString str;
  1074. RETURN_FAILED(g.FormatString(str, IDS_FMT_FRAME_BEGIN, stk.AddrPC.Offset,
  1075. stk.AddrFrame.Offset, stk.AddrReturn.Offset,
  1076. stk.Params[0], stk.Params[1], stk.Params[2], stk.Params[3],
  1077. (LPCTSTR)strModule, (LPCTSTR)strSymbol));
  1078. WRITE_STRING_TO_STREAM(pStm, str);
  1079. // Format the <FPO> XML tag, if a function pointer table entry exists
  1080. if (stk.FuncTableEntry)
  1081. {
  1082. // Determine the frame type string
  1083. FPO_DATA& fpo = *reinterpret_cast<FPO_DATA*>(stk.FuncTableEntry);
  1084. LPCTSTR pszFrame;
  1085. switch (fpo.cbFrame)
  1086. {
  1087. case FRAME_FPO : pszFrame = "FPO" ; break;
  1088. case FRAME_TRAP : pszFrame = "TRAP" ; break;
  1089. case FRAME_TSS : pszFrame = "TSS" ; break;
  1090. case FRAME_NONFPO: pszFrame = "NONFPO" ; break;
  1091. default : pszFrame = "(unknown)"; break;
  1092. }
  1093. // Format the <FPO> XML tag
  1094. RETURN_FAILED(g.FormatString(str, IDS_FMT_FPO, fpo.ulOffStart,
  1095. fpo.cbProcSize, fpo.cdwLocals, fpo.cdwParams, fpo.cbProlog, fpo.cbRegs,
  1096. fpo.fHasSEH ? "1" : "0", fpo.fUseBP ? "1" : "0", pszFrame));
  1097. WRITE_STRING_TO_STREAM(pStm, str);
  1098. }
  1099. // Format the </Frm> XML tag
  1100. RETURN_FAILED(g.LoadString(str, IDS_FRAME_END));
  1101. WRITE_STRING_TO_STREAM(pStm, str);
  1102. // Indicate success
  1103. return S_OK;
  1104. }
  1105. /////////////////////////////////////////////////////////////////////////////
  1106. //
  1107. template <class T>
  1108. HRESULT CAppModeDebug<T>::FormatModuleInfo(IStream* pStm, CLoadedModuleIt it)
  1109. {
  1110. ZString strFileSize, strFileDate, strFileTime, strFileVer, strProdVer;
  1111. ZString strIsDebug, strCompany, strDesc, strProdName, strCopy;
  1112. // Ensure that we have a fully-qualified module name
  1113. ZString strModule(GetModuleWithPath(it->second.m_strModuleName));
  1114. // FileVer and ProdVer
  1115. ZVersionInfo vi;
  1116. if (vi.Load(strModule))
  1117. {
  1118. RETURN_FAILED(g.FormatString(strFileVer, IDS_FMT_FILE_VER,
  1119. vi.GetFileVersionMSHigh(), vi.GetFileVersionMSLow(),
  1120. vi.GetFileVersionLSHigh(), vi.GetFileVersionLSLow()));
  1121. RETURN_FAILED(g.FormatString(strProdVer, IDS_FMT_PROD_VER,
  1122. vi.GetProductVersionMSHigh(), vi.GetProductVersionMSLow(),
  1123. vi.GetProductVersionLSHigh(), vi.GetProductVersionLSLow()));
  1124. // IsDebug, Company, Desc, ProdName, Copy
  1125. strIsDebug = vi.IsDebug() ? "1" : "0";
  1126. strCompany = vi.GetCompanyName();
  1127. strDesc = vi.GetFileDescription();
  1128. strProdName = vi.GetProductName();
  1129. strCopy = vi.GetLegalCopyright();
  1130. }
  1131. // Find the specified module file
  1132. WIN32_FIND_DATA fd;
  1133. TCFileFindHandle shFF = ::FindFirstFile(strModule, &fd);
  1134. if (!shFF.IsNull() && INVALID_HANDLE_VALUE != shFF.GetHandle())
  1135. {
  1136. // FileSize
  1137. RETURN_FAILED(g.FormatString(strFileSize, IDS_FMT_FILE_SIZE,
  1138. fd.nFileSizeLow));
  1139. // FileDate and FileTime
  1140. FILETIME ft;
  1141. if (!::FileTimeToLocalFileTime(&fd.ftLastWriteTime, &ft))
  1142. return HRESULT_FROM_WIN32(::GetLastError());
  1143. SYSTEMTIME st;
  1144. if (!::FileTimeToSystemTime(&ft, &st))
  1145. return HRESULT_FROM_WIN32(::GetLastError());
  1146. RETURN_FAILED(g.FormatString(strFileDate, IDS_FMT_FILE_DATE,
  1147. st.wYear, st.wMonth, st.wDay));
  1148. RETURN_FAILED(g.FormatString(strFileTime, IDS_FMT_FILE_TIME,
  1149. st.wHour, st.wMinute, st.wSecond));
  1150. }
  1151. // Format the <Module> XML tag
  1152. ZString str;
  1153. RETURN_FAILED(g.FormatString(str, IDS_FMT_MODULE,
  1154. (LPCTSTR)strModule, it->second.m_ModuleBase,
  1155. it->second.m_ModuleSize, (LPCTSTR)strFileSize, (LPCTSTR)strFileDate,
  1156. (LPCTSTR)strFileTime, (LPCTSTR)strFileVer, (LPCTSTR)strProdVer,
  1157. (LPCTSTR)strIsDebug, (LPCTSTR)strCompany, (LPCTSTR)strDesc,
  1158. (LPCTSTR)strProdName, (LPCTSTR)strCopy));
  1159. WRITE_STRING_TO_STREAM(pStm, str);
  1160. // Indicate success
  1161. return S_OK;
  1162. }
  1163. /////////////////////////////////////////////////////////////////////////////
  1164. //
  1165. template <class T>
  1166. HRESULT CAppModeDebug<T>::FormatProcessesInfo(IStream* pStm)
  1167. {
  1168. // TODO: Hook the NT4 case and use EnumProcesses
  1169. // This will fail under Windows NT 4.0 or earlier
  1170. if (!m_libToolHelp.IsLibraryLoadedAndResolved())
  1171. return S_FALSE;
  1172. // Get a snapshot of the processes currently running on the system
  1173. TCHandle shSnapshot = m_libToolHelp.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  1174. if (shSnapshot.IsNull() || INVALID_HANDLE_VALUE == shSnapshot)
  1175. return S_FALSE;
  1176. // Iterate over each process (except for the debuggee process)
  1177. PROCESSENTRY32 pe = {sizeof(pe)};
  1178. bool bProcess = !!m_libToolHelp.Process32First(shSnapshot, &pe);
  1179. while (bProcess)
  1180. {
  1181. // Format the process information
  1182. if (pe.th32ProcessID != m_dwProcessId && pe.th32ProcessID != ::GetCurrentProcessId())
  1183. RETURN_FAILED(FormatProcessInfo(pStm, &pe));
  1184. // Get the next process
  1185. ZeroMemory(pe.szExeFile, sizeof(pe.szExeFile));
  1186. pe.dwSize = sizeof(pe);
  1187. bProcess = !!m_libToolHelp.Process32Next(shSnapshot, &pe);
  1188. }
  1189. // Indicate success
  1190. return S_OK;
  1191. }
  1192. /////////////////////////////////////////////////////////////////////////////
  1193. //
  1194. template <class T>
  1195. HRESULT CAppModeDebug<T>::FormatProcessInfo(IStream* pStm, PROCESSENTRY32* ppe)
  1196. {
  1197. // Paths are usually not fully-qualified under Windows 2000
  1198. TCHAR szPath[_MAX_PATH * 2] = TEXT("");
  1199. TCHAR szEXE[_MAX_PATH * 2];
  1200. LPCTSTR pszEXE = ppe->szExeFile;
  1201. static bool s_bIsWinNT = ::IsWinNT();
  1202. if (s_bIsWinNT)
  1203. {
  1204. // Determine if the specified path is fully-qualified
  1205. TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR];
  1206. _tsplitpath(ppe->szExeFile, szDrive, szDir, NULL, NULL);
  1207. if (TEXT('\0') == szDrive[0] && TEXT('\0') == szDir[0])
  1208. {
  1209. // Handle a special case
  1210. if (0 == _tcsicmp(TEXT("csrss.exe"), pszEXE))
  1211. {
  1212. ::GetSystemDirectory(szPath, sizeofArray(szPath));
  1213. if (TEXT('\\') != szPath[_tcslen(szPath) - 1])
  1214. _tcscat(szPath, TEXT("\\"));
  1215. _tcscat(szPath, pszEXE);
  1216. _tcscpy(szEXE, szPath);
  1217. pszEXE = szEXE;
  1218. }
  1219. else
  1220. {
  1221. // Attempt to open the process
  1222. const DWORD dwAccess = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
  1223. TCHandle shProcess = ::OpenProcess(dwAccess, false, ppe->th32ProcessID);
  1224. if (!shProcess.IsNull())
  1225. {
  1226. // Get the name of the first module in the process
  1227. DWORD cb = 0;
  1228. HMODULE hModule = NULL;
  1229. if (::EnumProcessModules(shProcess, &hModule, sizeof(hModule), &cb))
  1230. {
  1231. if (::GetModuleFileNameEx(shProcess, hModule, szEXE, sizeofArray(szEXE)))
  1232. {
  1233. // Handle some special cases
  1234. if (0 == _tcsnicmp(TEXT("\\SystemRoot\\"), szEXE, 12))
  1235. {
  1236. ::GetWindowsDirectory(szPath, sizeofArray(szPath));
  1237. if (TEXT('\\') != szPath[_tcslen(szPath) - 1])
  1238. _tcscat(szPath, TEXT("\\"));
  1239. _tcscat(szPath, szEXE + 12);
  1240. _tcscpy(szEXE, szPath);
  1241. pszEXE = szEXE;
  1242. }
  1243. else if (0 == _tcsncmp(TEXT("\\??\\"), szEXE, 4))
  1244. {
  1245. pszEXE = szEXE + 4;
  1246. }
  1247. else
  1248. {
  1249. pszEXE = szEXE;
  1250. }
  1251. }
  1252. }
  1253. }
  1254. }
  1255. }
  1256. }
  1257. // Delegate to the string-based formatting method
  1258. return FormatProcessInfo(pStm, pszEXE);
  1259. }
  1260. /////////////////////////////////////////////////////////////////////////////
  1261. //
  1262. template <class T>
  1263. HRESULT CAppModeDebug<T>::FormatProcessInfo(IStream* pStm, LPCTSTR pszEXE)
  1264. {
  1265. ZString strFileSize, strFileDate, strFileTime, strFileVer, strProdVer;
  1266. ZString strIsDebug, strCompany, strDesc, strProdName, strCopy;
  1267. // Resolve as a long filename, if possible
  1268. TCHAR szEXE[_MAX_PATH * 2];
  1269. if (m_libToolHelp.Exists_GetLongPathName())
  1270. if (m_libToolHelp.GetLongPathName(pszEXE, szEXE, sizeofArray(szEXE)))
  1271. pszEXE = szEXE;
  1272. // FileVer and ProdVer
  1273. ZVersionInfo vi;
  1274. if (vi.Load(pszEXE))
  1275. {
  1276. RETURN_FAILED(g.FormatString(strFileVer, IDS_FMT_FILE_VER,
  1277. vi.GetFileVersionMSHigh(), vi.GetFileVersionMSLow(),
  1278. vi.GetFileVersionLSHigh(), vi.GetFileVersionLSLow()));
  1279. RETURN_FAILED(g.FormatString(strProdVer, IDS_FMT_PROD_VER,
  1280. vi.GetProductVersionMSHigh(), vi.GetProductVersionMSLow(),
  1281. vi.GetProductVersionLSHigh(), vi.GetProductVersionLSLow()));
  1282. // IsDebug, Company, Desc, ProdName, Copy
  1283. strIsDebug = vi.IsDebug() ? "1" : "0";
  1284. strCompany = vi.GetCompanyName();
  1285. strDesc = vi.GetFileDescription();
  1286. strProdName = vi.GetProductName();
  1287. strCopy = vi.GetLegalCopyright();
  1288. }
  1289. // Find the specified module file
  1290. WIN32_FIND_DATA fd;
  1291. TCFileFindHandle shFF = ::FindFirstFile(pszEXE, &fd);
  1292. if (!shFF.IsNull() && INVALID_HANDLE_VALUE != shFF.GetHandle())
  1293. {
  1294. // FileSize
  1295. RETURN_FAILED(g.FormatString(strFileSize, IDS_FMT_FILE_SIZE,
  1296. fd.nFileSizeLow));
  1297. // FileDate and FileTime
  1298. FILETIME ft;
  1299. if (!::FileTimeToLocalFileTime(&fd.ftLastWriteTime, &ft))
  1300. return HRESULT_FROM_WIN32(::GetLastError());
  1301. SYSTEMTIME st;
  1302. if (!::FileTimeToSystemTime(&ft, &st))
  1303. return HRESULT_FROM_WIN32(::GetLastError());
  1304. RETURN_FAILED(g.FormatString(strFileDate, IDS_FMT_FILE_DATE,
  1305. st.wYear, st.wMonth, st.wDay));
  1306. RETURN_FAILED(g.FormatString(strFileTime, IDS_FMT_FILE_TIME,
  1307. st.wHour, st.wMinute, st.wSecond));
  1308. }
  1309. // Format the <Process> XML tag
  1310. ZString str;
  1311. RETURN_FAILED(g.FormatString(str, IDS_FMT_PROCESS,
  1312. pszEXE, (LPCTSTR)strFileSize, (LPCTSTR)strFileDate,
  1313. (LPCTSTR)strFileTime, (LPCTSTR)strFileVer, (LPCTSTR)strProdVer,
  1314. (LPCTSTR)strIsDebug, (LPCTSTR)strCompany, (LPCTSTR)strDesc,
  1315. (LPCTSTR)strProdName, (LPCTSTR)strCopy));
  1316. WRITE_STRING_TO_STREAM(pStm, str);
  1317. // Indicate success
  1318. return S_OK;
  1319. }
  1320. /////////////////////////////////////////////////////////////////////////////
  1321. //
  1322. template <class T>
  1323. ZString CAppModeDebug<T>::GetSymbolPath()
  1324. {
  1325. TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR];
  1326. TCHAR szPath[_MAX_PATH * 2];
  1327. ZString str;
  1328. // Get the directory of the debuggee application
  1329. CLoadedModuleIt itApp = m_Images.find(m_cpdi.lpBaseOfImage);
  1330. if (itApp != m_Images.end())
  1331. {
  1332. _tsplitpath(itApp->second.m_strModuleName, szDrive, szDir, NULL, NULL);
  1333. _tmakepath(szPath, szDrive, szDir, NULL, NULL);
  1334. str += szPath;
  1335. str += ";";
  1336. }
  1337. // Get the current directory
  1338. ::GetCurrentDirectory(sizeofArray(szPath), szPath);
  1339. str += szPath;
  1340. str += ";";
  1341. // Get the directory of the guard application
  1342. ::GetModuleFileName(NULL, szPath, sizeofArray(szPath));
  1343. _tsplitpath(itApp->second.m_strModuleName, szDrive, szDir, NULL, NULL);
  1344. _tmakepath(szPath, szDrive, szDir, NULL, NULL);
  1345. str += szPath;
  1346. str += ";";
  1347. // Get the symbol search path
  1348. if (::GetEnvironmentVariable(TEXT("_NT_SYMBOL_PATH"), szPath,
  1349. sizeofArray(szPath)))
  1350. {
  1351. str += szPath;
  1352. str += ";";
  1353. }
  1354. // Get the alternate symbol search path
  1355. if (::GetEnvironmentVariable(TEXT("_NT_ALTERNATE_SYMBOL_PATH"), szPath,
  1356. sizeofArray(szPath)))
  1357. {
  1358. str += szPath;
  1359. str += ";";
  1360. }
  1361. // Get the system root path
  1362. if (::GetEnvironmentVariable(TEXT("SYSTEMROOT"), szPath,
  1363. sizeofArray(szPath)))
  1364. {
  1365. str += szPath;
  1366. str += ";";
  1367. }
  1368. // Return the composed string
  1369. return str;
  1370. }
  1371. /////////////////////////////////////////////////////////////////////////////
  1372. //
  1373. template <class T>
  1374. ZString CAppModeDebug<T>::GetSearchPath()
  1375. {
  1376. TCHAR szPath[_MAX_PATH * 2];
  1377. ZString str;
  1378. // Get the directory of the debuggee application
  1379. CLoadedModuleIt itApp = m_Images.find(m_cpdi.lpBaseOfImage);
  1380. if (itApp != m_Images.end())
  1381. {
  1382. TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR];
  1383. _tsplitpath(itApp->second.m_strModuleName, szDrive, szDir, NULL, NULL);
  1384. _tmakepath(szPath, szDrive, szDir, NULL, NULL);
  1385. str += szPath;
  1386. str += ";";
  1387. }
  1388. // Get the current directory
  1389. ::GetCurrentDirectory(sizeofArray(szPath), szPath);
  1390. str += szPath;
  1391. str += ";";
  1392. // Get the system directory
  1393. if (::GetSystemDirectory(szPath, sizeofArray(szPath)))
  1394. {
  1395. str += szPath;
  1396. str += ";";
  1397. }
  1398. // Get the Windows directory
  1399. if (::GetWindowsDirectory(szPath, sizeofArray(szPath)))
  1400. {
  1401. // Get the 16-bit system directory
  1402. static s_bIsWinNT = IsWinNT();
  1403. if (s_bIsWinNT)
  1404. {
  1405. str += szPath;
  1406. if (TEXT('\\') != szPath[_tcslen(szPath) - 1])
  1407. str += TEXT("\\");
  1408. str += TEXT("SYSTEM;");
  1409. }
  1410. // Append the Windows directory
  1411. str += szPath;
  1412. str += ";";
  1413. }
  1414. // Get the PATH environment variable
  1415. int cch = ::GetEnvironmentVariable(TEXT("PATH"), NULL, 0) + 1;
  1416. LPTSTR pszPath = (LPTSTR)_alloca(cch * sizeof(TCHAR));
  1417. if (::GetEnvironmentVariable(TEXT("PATH"), pszPath, cch))
  1418. {
  1419. str += pszPath;
  1420. str += ";";
  1421. }
  1422. // Return the composed string
  1423. return str;
  1424. }
  1425. /////////////////////////////////////////////////////////////////////////////
  1426. //
  1427. template <class T>
  1428. ZString CAppModeDebug<T>::GetModuleWithPath(LPCTSTR pszModule)
  1429. {
  1430. // Determine if the specified path is fully-qualified
  1431. TCHAR szModule[_MAX_PATH * 2], szDrive[_MAX_DRIVE], szDir[_MAX_DIR];
  1432. _tsplitpath(pszModule, szDrive, szDir, NULL, NULL);
  1433. if (TEXT('\0') == szDrive[0] && TEXT('\0') == szDir[0])
  1434. {
  1435. // Get the search path once
  1436. if (m_strSearchPath.IsEmpty())
  1437. m_strSearchPath = GetSearchPath();
  1438. // Use SearchPath to find the file
  1439. LPTSTR pszFilePart;
  1440. if (::SearchPath(m_strSearchPath, pszModule, NULL, sizeofArray(szModule),
  1441. szModule, &pszFilePart))
  1442. {
  1443. pszModule = szModule;
  1444. }
  1445. }
  1446. // Resolve as a long filename, if possible
  1447. if (m_libToolHelp.Exists_GetLongPathName())
  1448. if (m_libToolHelp.GetLongPathName(pszModule, szModule, sizeofArray(szModule)))
  1449. pszModule = szModule;
  1450. // Return as a ZString
  1451. return pszModule;
  1452. }
  1453. /////////////////////////////////////////////////////////////////////////////
  1454. //
  1455. template <class T>
  1456. BOOL CALLBACK CAppModeDebug<T>::ReadProcessMemoryProc(HANDLE hProcess,
  1457. DWORD lpBaseAddress, PVOID lpBuffer, DWORD nSize,
  1458. PDWORD lpNumberOfBytesRead)
  1459. {
  1460. // Reinterpret the specified process handle
  1461. CAppModeDebug<T>* pThis = reinterpret_cast<CAppModeDebug<T>*>(hProcess);
  1462. // Delegate to ReadProcessMemory, with the correct hProcess
  1463. return ::ReadProcessMemory(pThis->m_cpdi.hProcess,
  1464. reinterpret_cast<void*>(lpBaseAddress), lpBuffer, nSize,
  1465. lpNumberOfBytesRead);
  1466. }
  1467. /////////////////////////////////////////////////////////////////////////////
  1468. //
  1469. template <class T>
  1470. PVOID CALLBACK CAppModeDebug<T>::FunctionTableAccessProc(HANDLE hProcess,
  1471. DWORD AddrBase)
  1472. {
  1473. // Reinterpret the specified process handle
  1474. CAppModeDebug<T>* pThis = reinterpret_cast<CAppModeDebug<T>*>(hProcess);
  1475. // Delegate to SymFunctionTableAccess, with the correct hProcess
  1476. return SymFunctionTableAccess(pThis->m_hProcessSym, AddrBase);
  1477. }
  1478. /////////////////////////////////////////////////////////////////////////////
  1479. //
  1480. template <class T>
  1481. DWORD CALLBACK CAppModeDebug<T>::GetModuleBaseProc(HANDLE hProcess, DWORD Address)
  1482. {
  1483. // Reinterpret the specified process handle
  1484. CAppModeDebug<T>* pThis = reinterpret_cast<CAppModeDebug<T>*>(hProcess);
  1485. // Lookup the specified address
  1486. return ::SymGetModuleBase(pThis->m_hProcessSym, Address);
  1487. }
  1488. /////////////////////////////////////////////////////////////////////////////
  1489. //
  1490. template <class T>
  1491. DWORD CALLBACK CAppModeDebug<T>::TranslateAddressProc(HANDLE, HANDLE, LPADDRESS)
  1492. {
  1493. // We don't translate 16-bit addresses
  1494. return 0;
  1495. }
  1496. /////////////////////////////////////////////////////////////////////////////
  1497. // Overrides
  1498. /////////////////////////////////////////////////////////////////////////////
  1499. //
  1500. template <class T>
  1501. DWORD CAppModeDebug<T>::OnException(bool* pbExit)
  1502. {
  1503. // Ignore the very first breakpoint exception
  1504. if (EXCEPTION_BREAKPOINT == m_pde->u.Exception.ExceptionRecord.ExceptionCode)
  1505. {
  1506. if (!g.GetHitFirstBreakpoint())
  1507. {
  1508. // Flag that we've now hit the first breakpoint
  1509. g.SetHitFirstBreakpoint(true);
  1510. // Initialize USER32, to satisfy the app-starting cursor
  1511. MSG msg;
  1512. ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  1513. ::PostThreadMessage(::GetCurrentThreadId(), WM_APP, 0, 0);
  1514. ::GetMessage(&msg, NULL, 0, 0);
  1515. // Allow the debuggee to continue
  1516. return DBG_CONTINUE;
  1517. }
  1518. // Ignore the breakpoint if specified
  1519. if (g.GetConfigBool("IgnoreBreakpoints", true))
  1520. return DBG_CONTINUE;
  1521. }
  1522. // Ignore first-chance exceptions
  1523. if (m_pde->u.Exception.dwFirstChance)
  1524. return DBG_EXCEPTION_NOT_HANDLED;
  1525. // Report the exception as specified in the configuration
  1526. HRESULT hr = ReportException();
  1527. if (FAILED(hr))
  1528. {
  1529. g.HandleError(hr, IDS_E_FMT_UNEXPECTED,
  1530. "CAppModeDebug<T>::OnException calling ReportException()");
  1531. }
  1532. *pbExit = true;
  1533. return DBG_CONTINUE;
  1534. }
  1535. /////////////////////////////////////////////////////////////////////////////
  1536. //
  1537. template <class T>
  1538. inline DWORD CAppModeDebug<T>::OnCreateThread(bool* pbExit)
  1539. {
  1540. return DBG_CONTINUE;
  1541. }
  1542. /////////////////////////////////////////////////////////////////////////////
  1543. //
  1544. template <class T>
  1545. inline DWORD CAppModeDebug<T>::OnCreateProcess(bool* pbExit)
  1546. {
  1547. return DBG_CONTINUE;
  1548. }
  1549. /////////////////////////////////////////////////////////////////////////////
  1550. //
  1551. template <class T>
  1552. inline DWORD CAppModeDebug<T>::OnExitThread(bool* pbExit)
  1553. {
  1554. return DBG_CONTINUE;
  1555. }
  1556. /////////////////////////////////////////////////////////////////////////////
  1557. //
  1558. template <class T>
  1559. inline DWORD CAppModeDebug<T>::OnExitProcess(bool* pbExit)
  1560. {
  1561. *pbExit = true;
  1562. return DBG_CONTINUE;
  1563. }
  1564. /////////////////////////////////////////////////////////////////////////////
  1565. //
  1566. template <class T>
  1567. inline DWORD CAppModeDebug<T>::OnLoadDLL(bool* pbExit)
  1568. {
  1569. return DBG_CONTINUE;
  1570. }
  1571. /////////////////////////////////////////////////////////////////////////////
  1572. //
  1573. template <class T>
  1574. inline DWORD CAppModeDebug<T>::OnUnloadDLL(bool* pbExit)
  1575. {
  1576. return DBG_CONTINUE;
  1577. }
  1578. /////////////////////////////////////////////////////////////////////////////
  1579. //
  1580. template <class T>
  1581. inline DWORD CAppModeDebug<T>::OnOutputDebugString(bool* pbExit)
  1582. {
  1583. return DBG_CONTINUE;
  1584. }
  1585. /////////////////////////////////////////////////////////////////////////////
  1586. //
  1587. template <class T>
  1588. inline DWORD CAppModeDebug<T>::OnRIP(bool* pbExit)
  1589. {
  1590. *pbExit = true;
  1591. return DBG_CONTINUE;
  1592. }
  1593. /////////////////////////////////////////////////////////////////////////////
  1594. //
  1595. template <class T>
  1596. inline DWORD CAppModeDebug<T>::OnOther(bool* pbExit)
  1597. {
  1598. *pbExit = true;
  1599. return DBG_CONTINUE;
  1600. }