PageRenderTime 25ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/source2/MDbg4/src/debugger/corapi/Debugger.cs

https://bitbucket.org/mvptracker/cosmos
C# | 1132 lines | 783 code | 136 blank | 213 comment | 38 complexity | 58f6c3f3b4c7bf91ce192eab5b9347e1 MD5 | raw file
Possible License(s): BSD-2-Clause
  1. //---------------------------------------------------------------------
  2. // This file is part of the CLR Managed Debugger (mdbg) Sample.
  3. //
  4. // Copyright (C) Microsoft Corporation. All rights reserved.
  5. //---------------------------------------------------------------------
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Diagnostics;
  10. using System.Runtime.InteropServices;
  11. using System.Runtime.InteropServices.ComTypes;
  12. using System.Runtime.CompilerServices;
  13. using System.Threading;
  14. using System.Text;
  15. using System.Security.Permissions;
  16. using System.Globalization;
  17. using Microsoft.Samples.Debugging.CorDebug.NativeApi;
  18. namespace Microsoft.Samples.Debugging.CorDebug
  19. {
  20. /**
  21. * Wraps the native CLR Debugger.
  22. * Note that we don't derive the class from WrapperBase, becuase this
  23. * class will never be returned in any callback.
  24. */
  25. public sealed class CorDebugger : MarshalByRefObject
  26. {
  27. private const int MaxVersionStringLength = 256; // == MAX_PATH
  28. public static string GetDebuggerVersionFromFile(string pathToExe)
  29. {
  30. Debug.Assert(!string.IsNullOrEmpty(pathToExe));
  31. if (string.IsNullOrEmpty(pathToExe))
  32. throw new ArgumentException("Value cannot be null or empty.", "pathToExe");
  33. int neededSize;
  34. StringBuilder sb = new StringBuilder(MaxVersionStringLength);
  35. NativeMethods.GetRequestedRuntimeVersion(pathToExe, sb, sb.Capacity, out neededSize);
  36. return sb.ToString();
  37. }
  38. public static string GetDebuggerVersionFromPid(int pid)
  39. {
  40. using (ProcessSafeHandle ph = NativeMethods.OpenProcess((int)(NativeMethods.ProcessAccessOptions.ProcessVMRead |
  41. NativeMethods.ProcessAccessOptions.ProcessQueryInformation |
  42. NativeMethods.ProcessAccessOptions.ProcessDupHandle |
  43. NativeMethods.ProcessAccessOptions.Synchronize),
  44. false, // inherit handle
  45. pid))
  46. {
  47. if (ph.IsInvalid)
  48. throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
  49. int neededSize;
  50. StringBuilder sb = new StringBuilder(MaxVersionStringLength);
  51. NativeMethods.GetVersionFromProcess(ph, sb, sb.Capacity, out neededSize);
  52. return sb.ToString();
  53. }
  54. }
  55. public static string GetDefaultDebuggerVersion()
  56. {
  57. return RuntimeEnvironment.GetSystemVersion();
  58. }
  59. /// <summary>Creates a debugger wrapper from Guid.</summary>
  60. public CorDebugger(Guid debuggerGuid)
  61. {
  62. ICorDebug rawDebuggingAPI;
  63. Guid ifaceId = typeof(ICorDebug).GUID;
  64. NativeMethods.CoCreateInstance(ref debuggerGuid,
  65. IntPtr.Zero, // pUnkOuter
  66. 1, // CLSCTX_INPROC_SERVER
  67. ref ifaceId,
  68. out rawDebuggingAPI);
  69. InitFromICorDebug(rawDebuggingAPI);
  70. }
  71. [CLSCompliant(false)]
  72. public CorDebugger(ICorDebug rawDebuggingAPI)
  73. {
  74. InitFromICorDebug(rawDebuggingAPI);
  75. }
  76. /// <summary>Creates a debugger interface that is able debug requested version of CLR</summary>
  77. /// <param name="debuggerVerison">Version number of the debugging interface.</param>
  78. /// <remarks>The version number is usually retrieved either by calling one of following mscoree functions:
  79. /// GetCorVerison, GetRequestedRuntimeVersion or GetVersionFromProcess.</remarks>
  80. public CorDebugger(string debuggerVersion)
  81. {
  82. InitFromVersion(debuggerVersion);
  83. }
  84. [CLSCompliant(false)]
  85. public ICorDebug Raw
  86. {
  87. get
  88. {
  89. return m_debugger;
  90. }
  91. }
  92. /**
  93. * Closes the debugger. After this method is called, it is an error
  94. * to call any other methods on this object.
  95. */
  96. public void Terminate()
  97. {
  98. Debug.Assert(m_debugger != null);
  99. ICorDebug d = m_debugger;
  100. m_debugger = null;
  101. d.Terminate();
  102. }
  103. /**
  104. * Specify the callback object to use for managed events.
  105. */
  106. internal void SetManagedHandler(ICorDebugManagedCallback managedCallback)
  107. {
  108. m_debugger.SetManagedHandler(managedCallback);
  109. }
  110. /**
  111. * Specify the callback object to use for unmanaged events.
  112. */
  113. internal void SetUnmanagedHandler(ICorDebugUnmanagedCallback nativeCallback)
  114. {
  115. m_debugger.SetUnmanagedHandler(nativeCallback);
  116. }
  117. /**
  118. * Launch a process under the control of the debugger.
  119. *
  120. * Parameters are the same as the Win32 CreateProcess call.
  121. */
  122. public CorProcess CreateProcess(
  123. String applicationName,
  124. String commandLine
  125. )
  126. {
  127. return CreateProcess(applicationName, commandLine, ".");
  128. }
  129. /**
  130. * Launch a process under the control of the debugger.
  131. *
  132. * Parameters are the same as the Win32 CreateProcess call.
  133. */
  134. public CorProcess CreateProcess(
  135. String applicationName,
  136. String commandLine,
  137. String currentDirectory
  138. )
  139. {
  140. return CreateProcess(applicationName, commandLine, currentDirectory, 0);
  141. }
  142. /**
  143. * Launch a process under the control of the debugger.
  144. *
  145. * Parameters are the same as the Win32 CreateProcess call.
  146. */
  147. public CorProcess CreateProcess(
  148. String applicationName,
  149. String commandLine,
  150. String currentDirectory,
  151. int flags
  152. )
  153. {
  154. return CreateProcess(applicationName, commandLine, currentDirectory, flags, null);
  155. }
  156. public CorProcess CreateProcess(String applicationName,
  157. String commandLine,
  158. String currentDirectory,
  159. int flags,
  160. CorRemoteTarget target)
  161. {
  162. PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
  163. STARTUPINFO si = new STARTUPINFO();
  164. si.cb = Marshal.SizeOf(si);
  165. // initialize safe handles
  166. si.hStdInput = new Microsoft.Win32.SafeHandles.SafeFileHandle(new IntPtr(0), false);
  167. si.hStdOutput = new Microsoft.Win32.SafeHandles.SafeFileHandle(new IntPtr(0), false);
  168. si.hStdError = new Microsoft.Win32.SafeHandles.SafeFileHandle(new IntPtr(0), false);
  169. CorProcess ret;
  170. //constrained execution region (Cer)
  171. System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
  172. try
  173. {
  174. }
  175. finally
  176. {
  177. ret = CreateProcess(target,
  178. applicationName,
  179. commandLine,
  180. null,
  181. null,
  182. true, // inherit handles
  183. flags, // creation flags
  184. new IntPtr(0), // environment
  185. currentDirectory,
  186. si, // startup info
  187. ref pi, // process information
  188. CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS);
  189. NativeMethods.CloseHandle(pi.hProcess);
  190. NativeMethods.CloseHandle(pi.hThread);
  191. }
  192. return ret;
  193. }
  194. /**
  195. * Launch a process under the control of the debugger.
  196. *
  197. * Parameters are the same as the Win32 CreateProcess call.
  198. *
  199. * The caller should remember to execute:
  200. *
  201. * Microsoft.Win32.Interop.Windows.CloseHandle (
  202. * processInformation.hProcess);
  203. *
  204. * after CreateProcess returns.
  205. */
  206. [CLSCompliant(false)]
  207. public CorProcess CreateProcess(
  208. String applicationName,
  209. String commandLine,
  210. SECURITY_ATTRIBUTES processAttributes,
  211. SECURITY_ATTRIBUTES threadAttributes,
  212. bool inheritHandles,
  213. int creationFlags,
  214. IntPtr environment,
  215. String currentDirectory,
  216. STARTUPINFO startupInfo,
  217. ref PROCESS_INFORMATION processInformation,
  218. CorDebugCreateProcessFlags debuggingFlags)
  219. {
  220. return CreateProcess(null,
  221. applicationName,
  222. commandLine,
  223. processAttributes,
  224. threadAttributes,
  225. inheritHandles,
  226. creationFlags,
  227. environment,
  228. currentDirectory,
  229. startupInfo,
  230. ref processInformation,
  231. debuggingFlags);
  232. }
  233. [CLSCompliant(false)]
  234. public CorProcess CreateProcess(CorRemoteTarget target,
  235. String applicationName,
  236. String commandLine,
  237. SECURITY_ATTRIBUTES processAttributes,
  238. SECURITY_ATTRIBUTES threadAttributes,
  239. bool inheritHandles,
  240. int creationFlags,
  241. IntPtr environment,
  242. String currentDirectory,
  243. STARTUPINFO startupInfo,
  244. ref PROCESS_INFORMATION processInformation,
  245. CorDebugCreateProcessFlags debuggingFlags)
  246. {
  247. /*
  248. * If commandLine is: <c:\a b\a arg1 arg2> and c:\a.exe does not exist,
  249. * then without this logic, "c:\a b\a.exe" would be tried next.
  250. * To prevent this ambiguity, this forces the user to quote if the path
  251. * has spaces in it: <"c:\a b\a" arg1 arg2>
  252. */
  253. if (null == applicationName && !commandLine.StartsWith("\""))
  254. {
  255. int firstSpace = commandLine.IndexOf(" ");
  256. if (firstSpace != -1)
  257. commandLine = String.Format(CultureInfo.InvariantCulture, "\"{0}\" {1}", commandLine.Substring(0, firstSpace), commandLine.Substring(firstSpace, commandLine.Length - firstSpace));
  258. }
  259. ICorDebugProcess proc = null;
  260. if (target == null)
  261. {
  262. m_debugger.CreateProcess(
  263. applicationName,
  264. commandLine,
  265. processAttributes,
  266. threadAttributes,
  267. inheritHandles ? 1 : 0,
  268. (uint)creationFlags,
  269. environment,
  270. currentDirectory,
  271. startupInfo,
  272. processInformation,
  273. debuggingFlags,
  274. out proc);
  275. }
  276. else
  277. {
  278. m_remoteDebugger.CreateProcessEx(target,
  279. applicationName,
  280. commandLine,
  281. processAttributes,
  282. threadAttributes,
  283. inheritHandles ? 1 : 0,
  284. (uint)creationFlags,
  285. environment,
  286. currentDirectory,
  287. startupInfo,
  288. processInformation,
  289. debuggingFlags,
  290. out proc);
  291. }
  292. return CorProcess.GetCorProcess(proc);
  293. }
  294. /**
  295. * Attach to an active process
  296. */
  297. public CorProcess DebugActiveProcess(int processId, bool win32Attach)
  298. {
  299. return DebugActiveProcess(processId, win32Attach, null);
  300. }
  301. public CorProcess DebugActiveProcess(int processId, bool win32Attach, CorRemoteTarget target)
  302. {
  303. ICorDebugProcess proc = null;
  304. if (target == null)
  305. {
  306. m_debugger.DebugActiveProcess((uint)processId, win32Attach ? 1 : 0, out proc);
  307. }
  308. else
  309. {
  310. m_remoteDebugger.DebugActiveProcessEx(target, (uint)processId, win32Attach ? 1 : 0, out proc);
  311. }
  312. return CorProcess.GetCorProcess(proc);
  313. }
  314. /**
  315. * Enumerate all processes currently being debugged.
  316. */
  317. public IEnumerable Processes
  318. {
  319. get
  320. {
  321. ICorDebugProcessEnum eproc = null;
  322. m_debugger.EnumerateProcesses(out eproc);
  323. return new CorProcessEnumerator(eproc);
  324. }
  325. }
  326. /**
  327. * Get the Process object for the given PID.
  328. */
  329. public CorProcess GetProcess(int processId)
  330. {
  331. ICorDebugProcess proc = null;
  332. m_debugger.GetProcess((uint)processId, out proc);
  333. return CorProcess.GetCorProcess(proc);
  334. }
  335. /**
  336. * Warn us of potentional problems in using debugging (eg. whether a kernel debugger is
  337. * attached). This API should probably be renamed or the warnings turned into errors
  338. * in CreateProcess/DebugActiveProcess
  339. */
  340. public void CanLaunchOrAttach(int processId, bool win32DebuggingEnabled)
  341. {
  342. m_debugger.CanLaunchOrAttach((uint)processId,
  343. win32DebuggingEnabled ? 1 : 0);
  344. }
  345. ////////////////////////////////////////////////////////////////////////////////
  346. //
  347. // CorDebugger private implement part
  348. //
  349. ////////////////////////////////////////////////////////////////////////////////
  350. // called by constructors during initialization
  351. private void InitFromVersion(string debuggerVersion)
  352. {
  353. if (debuggerVersion.StartsWith("v1"))
  354. {
  355. // ICorDebug before V2 did not cooperate well with COM-intop. MDbg's managed
  356. // wrappers over ICorDebug only work on V2 and beyond.
  357. throw new ArgumentException("Can't debug a version 1 CLR process (\"" + debuggerVersion +
  358. "\"). Run application in a version 2 CLR, or use a version 1 debugger instead.");
  359. }
  360. bool fUseV2 = false;
  361. ICorDebug rawDebuggingAPI = null;
  362. try
  363. {
  364. CLRMetaHost mh = new CLRMetaHost();
  365. CLRRuntimeInfo rti = mh.GetRuntime(debuggerVersion);
  366. rawDebuggingAPI = rti.GetLegacyICorDebugInterface();
  367. }
  368. catch (NotImplementedException)
  369. {
  370. fUseV2 = true;
  371. }
  372. catch (EntryPointNotFoundException)
  373. {
  374. fUseV2 = true;
  375. }
  376. if (fUseV2)
  377. {
  378. // fallback to v2 method
  379. try
  380. {
  381. rawDebuggingAPI = NativeMethods.CreateDebuggingInterfaceFromVersion((int)CorDebuggerVersion.Whidbey, debuggerVersion);
  382. }
  383. catch (ArgumentException)
  384. {
  385. // This can commonly happen if:
  386. // 1) the debuggee is missing a config file
  387. // 2) the debuggee has a config file for a not-installed CLR.
  388. //
  389. // Give a more descriptive error.
  390. // We explicitly don't pass the inner exception because:
  391. // - it's uninteresting. It's really just from a pinvoke and so there are no
  392. // extra managed frames.
  393. // - MDbg's error reporting will call Exception.GetBaseException() and so just
  394. // grab the inner exception.
  395. throw new ArgumentException("Failed to create debugging services for version '" + debuggerVersion + "'");
  396. }
  397. }
  398. Debug.Assert(rawDebuggingAPI != null);
  399. InitFromICorDebug(rawDebuggingAPI);
  400. }
  401. private void InitFromICorDebug(ICorDebug rawDebuggingAPI)
  402. {
  403. Debug.Assert(rawDebuggingAPI != null);
  404. if (rawDebuggingAPI == null)
  405. throw new ArgumentException("Cannot be null.", "rawDebugggingAPI");
  406. m_debugger = rawDebuggingAPI;
  407. m_debugger.Initialize();
  408. m_debugger.SetManagedHandler(new ManagedCallback(this));
  409. // This may return null.
  410. m_remoteDebugger = rawDebuggingAPI as ICorDebugRemote;
  411. }
  412. /**
  413. * Helper for invoking events. Checks to make sure that handlers
  414. * are hooked up to a handler before the handler is invoked.
  415. *
  416. * We want to allow maximum flexibility by our callers. As such,
  417. * we don't require that they call <code>e.Controller.Continue</code>,
  418. * nor do we require that this class call it. <b>Someone</b> needs
  419. * to call it, however.
  420. *
  421. * Consequently, if an exception is thrown and the process is stopped,
  422. * the process is continued automatically.
  423. */
  424. void InternalFireEvent(ManagedCallbackType callbackType, CorEventArgs e)
  425. {
  426. CorProcess owner = e.Process;
  427. Debug.Assert(owner != null);
  428. try
  429. {
  430. owner.DispatchEvent(callbackType, e);
  431. }
  432. finally
  433. {
  434. // If the callback marked the event to be continued, then call Continue now.
  435. if (e.Continue)
  436. {
  437. e.Controller.Continue(false);
  438. }
  439. }
  440. }
  441. ////////////////////////////////////////////////////////////////////////////////
  442. //
  443. // ManagedCallback
  444. //
  445. ////////////////////////////////////////////////////////////////////////////////
  446. /**
  447. * This is the object that gets passed to the debugger. It's
  448. * the intermediate "source" of the events, which repackages
  449. * the event arguments into a more approprate form and forwards
  450. * the call to the appropriate function.
  451. */
  452. private class ManagedCallback : ManagedCallbackBase
  453. {
  454. public ManagedCallback(CorDebugger outer)
  455. {
  456. m_outer = outer;
  457. }
  458. protected override void HandleEvent(ManagedCallbackType eventId, CorEventArgs args)
  459. {
  460. m_outer.InternalFireEvent(eventId, args);
  461. }
  462. private CorDebugger m_outer;
  463. }
  464. private ICorDebug m_debugger = null;
  465. private ICorDebugRemote m_remoteDebugger = null;
  466. } /* class Debugger */
  467. public class ProcessSafeHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
  468. {
  469. private ProcessSafeHandle()
  470. : base(true)
  471. {
  472. }
  473. private ProcessSafeHandle(IntPtr handle, bool ownsHandle)
  474. : base(ownsHandle)
  475. {
  476. SetHandle(handle);
  477. }
  478. [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
  479. override protected bool ReleaseHandle()
  480. {
  481. return NativeMethods.CloseHandle(handle);
  482. }
  483. }
  484. public static class NativeMethods
  485. {
  486. private const string Kernel32LibraryName = "kernel32.dll";
  487. private const string Ole32LibraryName = "ole32.dll";
  488. private const string ShlwapiLibraryName = "shlwapi.dll";
  489. private const string ShimLibraryName = "mscoree.dll";
  490. public const int MAX_PATH = 260;
  491. [
  492. System.Runtime.ConstrainedExecution.ReliabilityContract(System.Runtime.ConstrainedExecution.Consistency.WillNotCorruptState, System.Runtime.ConstrainedExecution.Cer.Success),
  493. DllImport(Kernel32LibraryName)
  494. ]
  495. public static extern bool CloseHandle(IntPtr handle);
  496. [
  497. DllImport(ShimLibraryName, CharSet = CharSet.Unicode, PreserveSig = false)
  498. ]
  499. public static extern ICorDebug CreateDebuggingInterfaceFromVersion(int iDebuggerVersion
  500. , string szDebuggeeVersion);
  501. [
  502. DllImport(ShimLibraryName, CharSet = CharSet.Unicode, PreserveSig = false)
  503. ]
  504. public static extern void GetVersionFromProcess(ProcessSafeHandle hProcess, StringBuilder versionString,
  505. Int32 bufferSize, out Int32 dwLength);
  506. [
  507. DllImport(ShimLibraryName, CharSet = CharSet.Unicode, PreserveSig = false)
  508. ]
  509. public static extern void GetRequestedRuntimeVersion(string pExe, StringBuilder pVersion,
  510. Int32 cchBuffer, out Int32 dwLength);
  511. [
  512. DllImport(ShimLibraryName, CharSet = CharSet.Unicode, PreserveSig = false)
  513. ]
  514. public static extern void CLRCreateInstance(ref Guid clsid, ref Guid riid,
  515. [MarshalAs(UnmanagedType.Interface)]out object metahostInterface);
  516. public enum ProcessAccessOptions : int
  517. {
  518. ProcessTerminate = 0x0001,
  519. ProcessCreateThread = 0x0002,
  520. ProcessSetSessionID = 0x0004,
  521. ProcessVMOperation = 0x0008,
  522. ProcessVMRead = 0x0010,
  523. ProcessVMWrite = 0x0020,
  524. ProcessDupHandle = 0x0040,
  525. ProcessCreateProcess = 0x0080,
  526. ProcessSetQuota = 0x0100,
  527. ProcessSetInformation = 0x0200,
  528. ProcessQueryInformation = 0x0400,
  529. ProcessSuspendResume = 0x0800,
  530. Synchronize = 0x100000,
  531. }
  532. [
  533. DllImport(Kernel32LibraryName, PreserveSig = true)
  534. ]
  535. public static extern ProcessSafeHandle OpenProcess(Int32 dwDesiredAccess, bool bInheritHandle, Int32 dwProcessId);
  536. [
  537. DllImport(Kernel32LibraryName, CharSet = CharSet.Unicode, PreserveSig = true)
  538. ]
  539. [return: MarshalAs(UnmanagedType.Bool)]
  540. public static extern bool QueryFullProcessImageName(ProcessSafeHandle hProcess,
  541. int dwFlags,
  542. StringBuilder lpExeName,
  543. ref int lpdwSize);
  544. [
  545. DllImport(Ole32LibraryName, PreserveSig = false)
  546. ]
  547. public static extern void CoCreateInstance(ref Guid rclsid, IntPtr pUnkOuter,
  548. Int32 dwClsContext,
  549. ref Guid riid, // must use "typeof(ICorDebug).GUID"
  550. [MarshalAs(UnmanagedType.Interface)]out ICorDebug debuggingInterface
  551. );
  552. public enum Stgm
  553. {
  554. StgmRead = 0x00000000,
  555. StgmWrite = 0x00000001,
  556. StgmReadWrite = 0x00000002,
  557. StgmShareDenyNone = 0x00000040,
  558. StgmShareDenyRead = 0x00000030,
  559. StgmShareDenyWrite = 0x00000020,
  560. StgmShareExclusive = 0x00000010,
  561. StgmPriority = 0x00040000,
  562. StgmCreate = 0x00001000,
  563. StgmConvert = 0x00020000,
  564. StgmFailIfThere = 0x00000000,
  565. StgmDirect = 0x00000000,
  566. StgmTransacted = 0x00010000,
  567. StgmNoScratch = 0x00100000,
  568. StgmNoSnapshot = 0x00200000,
  569. StgmSimple = 0x08000000,
  570. StgmDirectSwmr = 0x00400000,
  571. StgmDeleteOnRelease = 0x04000000
  572. }
  573. // SHCreateStreamOnFile* is used to create IStreams to pass to ICLRMetaHostPolicy::GetRequestedRuntime().
  574. // Since we can't count on the EX version being available, we have SHCreateStreamOnFile as a fallback.
  575. [
  576. DllImport(ShlwapiLibraryName, PreserveSig = false)
  577. ]
  578. // Only in version 6 and later
  579. public static extern void SHCreateStreamOnFileEx([MarshalAs(UnmanagedType.LPWStr)]string file,
  580. Stgm dwMode,
  581. Int32 dwAttributes, // Used if a file is created. Identical to dwFlagsAndAttributes param of CreateFile.
  582. bool create,
  583. IntPtr pTemplate, // Reserved, always pass null.
  584. [MarshalAs(UnmanagedType.Interface)]out IStream openedStream);
  585. [
  586. DllImport(ShlwapiLibraryName, PreserveSig = false)
  587. ]
  588. public static extern void SHCreateStreamOnFile(string file,
  589. Stgm dwMode,
  590. [MarshalAs(UnmanagedType.Interface)]out IStream openedStream);
  591. }
  592. // Wrapper for ICLRMetaHost. Used to find information about runtimes.
  593. public sealed class CLRMetaHost
  594. {
  595. private ICLRMetaHost m_metaHost;
  596. public const int MaxVersionStringLength = 26; // 24 + NULL and an extra
  597. private static readonly Guid clsidCLRMetaHost = new Guid("9280188D-0E8E-4867-B30C-7FA83884E8DE");
  598. public CLRMetaHost()
  599. {
  600. object o;
  601. Guid ifaceId = typeof(ICLRMetaHost).GUID;
  602. Guid clsid = clsidCLRMetaHost;
  603. NativeMethods.CLRCreateInstance(ref clsid, ref ifaceId, out o);
  604. m_metaHost = (ICLRMetaHost)o;
  605. }
  606. public CLRRuntimeInfo GetInstalledRuntimeByVersion(string version)
  607. {
  608. IEnumerable<CLRRuntimeInfo> runtimes = EnumerateInstalledRuntimes();
  609. foreach (CLRRuntimeInfo rti in runtimes)
  610. {
  611. if (rti.GetVersionString().ToString().ToLower() == version.ToLower())
  612. {
  613. return rti;
  614. }
  615. }
  616. return null;
  617. }
  618. public CLRRuntimeInfo GetLoadedRuntimeByVersion(Int32 processId, string version)
  619. {
  620. IEnumerable<CLRRuntimeInfo> runtimes = EnumerateLoadedRuntimes(processId);
  621. foreach (CLRRuntimeInfo rti in runtimes)
  622. {
  623. if (rti.GetVersionString().Equals(version, StringComparison.OrdinalIgnoreCase))
  624. {
  625. return rti;
  626. }
  627. }
  628. return null;
  629. }
  630. // Retrieve information about runtimes installed on the machine (i.e. in %WINDIR%\Microsoft.NET\)
  631. public IEnumerable<CLRRuntimeInfo> EnumerateInstalledRuntimes()
  632. {
  633. List<CLRRuntimeInfo> runtimes = new List<CLRRuntimeInfo>();
  634. IEnumUnknown enumRuntimes = m_metaHost.EnumerateInstalledRuntimes();
  635. // Since we're only getting one at a time, we can pass NULL for count.
  636. // S_OK also means we got the single element we asked for.
  637. for (object oIUnknown; enumRuntimes.Next(1, out oIUnknown, IntPtr.Zero) == 0; /* empty */)
  638. {
  639. runtimes.Add(new CLRRuntimeInfo(oIUnknown));
  640. }
  641. return runtimes;
  642. }
  643. // Retrieve information about runtimes that are currently loaded into the target process.
  644. public IEnumerable<CLRRuntimeInfo> EnumerateLoadedRuntimes(Int32 processId)
  645. {
  646. List<CLRRuntimeInfo> runtimes = new List<CLRRuntimeInfo>();
  647. IEnumUnknown enumRuntimes;
  648. using (ProcessSafeHandle hProcess = NativeMethods.OpenProcess((int)(NativeMethods.ProcessAccessOptions.ProcessVMRead |
  649. NativeMethods.ProcessAccessOptions.ProcessQueryInformation |
  650. NativeMethods.ProcessAccessOptions.ProcessDupHandle |
  651. NativeMethods.ProcessAccessOptions.Synchronize),
  652. false, // inherit handle
  653. processId))
  654. {
  655. if (hProcess.IsInvalid)
  656. {
  657. throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
  658. }
  659. enumRuntimes = m_metaHost.EnumerateLoadedRuntimes(hProcess);
  660. }
  661. // Since we're only getting one at a time, we can pass NULL for count.
  662. // S_OK also means we got the single element we asked for.
  663. for (object oIUnknown; enumRuntimes.Next(1, out oIUnknown, IntPtr.Zero) == 0; /* empty */)
  664. {
  665. runtimes.Add(new CLRRuntimeInfo(oIUnknown));
  666. }
  667. return runtimes;
  668. }
  669. public CLRRuntimeInfo GetRuntime(string version)
  670. {
  671. Guid ifaceId = typeof(ICLRRuntimeInfo).GUID;
  672. return new CLRRuntimeInfo(m_metaHost.GetRuntime(version, ref ifaceId));
  673. }
  674. }
  675. // You're expected to get this interface from mscoree!GetCLRMetaHost.
  676. // Details for APIs are in metahost.idl.
  677. [ComImport, InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown), Guid("D332DB9E-B9B3-4125-8207-A14884F53216")]
  678. internal interface ICLRMetaHost
  679. {
  680. [return: MarshalAs(UnmanagedType.Interface)]
  681. System.Object GetRuntime(
  682. [In, MarshalAs(UnmanagedType.LPWStr)] string pwzVersion,
  683. [In] ref Guid riid /*must use typeof(ICLRRuntimeInfo).GUID*/);
  684. void GetVersionFromFile(
  685. [In, MarshalAs(UnmanagedType.LPWStr)] string pwzFilePath,
  686. [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzBuffer,
  687. [In, Out] ref uint pcchBuffer);
  688. [return: MarshalAs(UnmanagedType.Interface)]
  689. IEnumUnknown EnumerateInstalledRuntimes();
  690. [return: MarshalAs(UnmanagedType.Interface)]
  691. IEnumUnknown EnumerateLoadedRuntimes(
  692. [In] ProcessSafeHandle hndProcess);
  693. }
  694. // Wrapper for ICLRMetaHostPolicy.
  695. public sealed class CLRMetaHostPolicy
  696. {
  697. public enum MetaHostPolicyFlags
  698. {
  699. metaHostPolicyHighCompat = 0,
  700. metaHostPolicyLowFootprint = 1
  701. }
  702. private ICLRMetaHostPolicy m_MHPolicy;
  703. private int MaxVersionStringLength = 26; //24 for version, + 2 terminating NULLs
  704. private static readonly Guid clsidCLRMetaHostPolicy = new Guid("2EBCD49A-1B47-4a61-B13A-4A03701E594B");
  705. public CLRMetaHostPolicy()
  706. {
  707. object o;
  708. Guid ifaceId = typeof(ICLRMetaHostPolicy).GUID;
  709. Guid clsid = clsidCLRMetaHostPolicy;
  710. NativeMethods.CLRCreateInstance(ref clsid, ref ifaceId, out o);
  711. m_MHPolicy = (ICLRMetaHostPolicy)o;
  712. }
  713. // Returns a CLRRuntimeInfo for the runtime that the specified binary
  714. // will run against.
  715. public CLRRuntimeInfo GetRequestedRuntime(MetaHostPolicyFlags flags,
  716. String binaryPath,
  717. String configPath,
  718. ref StringBuilder version,
  719. ref StringBuilder imageVersion)
  720. {
  721. IStream configStream = null;
  722. if (configPath != null)
  723. {
  724. try
  725. {
  726. NativeMethods.SHCreateStreamOnFileEx(configPath,
  727. NativeMethods.Stgm.StgmRead,
  728. 0, // We're not creating a file, so no flags needed.
  729. false, // Do NOT create a new file.
  730. IntPtr.Zero,
  731. out configStream);
  732. }
  733. catch (EntryPointNotFoundException)
  734. {
  735. // Fall back on the older method.
  736. NativeMethods.SHCreateStreamOnFile(configPath,
  737. NativeMethods.Stgm.StgmRead,
  738. out configStream);
  739. }
  740. }
  741. // In case they're empty.
  742. version.EnsureCapacity(MaxVersionStringLength);
  743. uint versionCapacity = System.Convert.ToUInt32(version.Capacity);
  744. imageVersion.EnsureCapacity(MaxVersionStringLength);
  745. uint imageVersionCapacity = System.Convert.ToUInt32(imageVersion.Capacity);
  746. Guid ifaceId = typeof(ICLRRuntimeInfo).GUID;
  747. uint configFlags;
  748. object o = m_MHPolicy.GetRequestedRuntime(flags,
  749. binaryPath,
  750. configStream,
  751. version,
  752. ref versionCapacity,
  753. imageVersion,
  754. ref imageVersionCapacity,
  755. out configFlags,
  756. ref ifaceId);
  757. return new CLRRuntimeInfo(o);
  758. }
  759. }
  760. // You're expected to get this interface from mscoree!CLRCreateInstance.
  761. // Details for APIs are in metahost.idl.
  762. [ComImport, InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown), Guid("E2190695-77B2-492E-8E14-C4B3A7FDD593")]
  763. internal interface ICLRMetaHostPolicy
  764. {
  765. [return: MarshalAs(UnmanagedType.Interface)]
  766. System.Object GetRequestedRuntime([In, ComAliasName("metahost.assembly.MetaHostPolicyFlags")] CLRMetaHostPolicy.MetaHostPolicyFlags dwPolicyFlags,
  767. [In, MarshalAs(UnmanagedType.LPWStr)] string pwzBinary,
  768. [In, MarshalAs(UnmanagedType.Interface)] IStream pCfgStream,
  769. [In, Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzVersion,
  770. [In, Out] ref uint pcchVersion,
  771. [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzImageVersion,
  772. [In, Out] ref uint pcchImageVersion,
  773. [Out] out uint pdwConfigFlags,
  774. [In] ref Guid riid /* must use typeof(ICLRRuntimeInfo).GUID */);
  775. }
  776. // Wrapper for ICLRRuntimeInfo. Represents information about a CLR install instance.
  777. public sealed class CLRRuntimeInfo
  778. {
  779. private static Guid m_ClsIdClrDebuggingLegacy = new Guid("DF8395B5-A4BA-450b-A77C-A9A47762C520");
  780. private ICLRRuntimeInfo m_runtimeInfo;
  781. public CLRRuntimeInfo(System.Object clrRuntimeInfo)
  782. {
  783. m_runtimeInfo = (ICLRRuntimeInfo)clrRuntimeInfo;
  784. }
  785. public string GetVersionString()
  786. {
  787. StringBuilder sb = new StringBuilder(CLRMetaHost.MaxVersionStringLength);
  788. int verStrLength = sb.Capacity;
  789. m_runtimeInfo.GetVersionString(sb, ref verStrLength);
  790. return sb.ToString();
  791. }
  792. public string GetRuntimeDirectory()
  793. {
  794. StringBuilder sb = new StringBuilder();
  795. int strLength = 0;
  796. m_runtimeInfo.GetRuntimeDirectory(sb, ref strLength);
  797. sb.Capacity = strLength;
  798. int ret = m_runtimeInfo.GetRuntimeDirectory(sb, ref strLength);
  799. if (ret < 0)
  800. Marshal.ThrowExceptionForHR(ret);
  801. return sb.ToString();
  802. }
  803. public ICorDebug GetLegacyICorDebugInterface()
  804. {
  805. Guid ifaceId = typeof(ICorDebug).GUID;
  806. Guid clsId = m_ClsIdClrDebuggingLegacy;
  807. return (ICorDebug)m_runtimeInfo.GetInterface(ref clsId, ref ifaceId);
  808. }
  809. }
  810. // Details about this interface are in metahost.idl.
  811. [ComImport, InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown), Guid("BD39D1D2-BA2F-486A-89B0-B4B0CB466891")]
  812. internal interface ICLRRuntimeInfo
  813. {
  814. // Marshalling pcchBuffer as int even though it's unsigned. Max version string is 24 characters, so we should not need to go over 2 billion soon.
  815. void GetVersionString([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzBuffer,
  816. [In, Out, MarshalAs(UnmanagedType.U4)] ref int pcchBuffer);
  817. // Marshalling pcchBuffer as int even though it's unsigned. MAX_PATH is 260, unicode paths are 65535, so we should not need to go over 2 billion soon.
  818. [PreserveSig]
  819. int GetRuntimeDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzBuffer,
  820. [In, Out, MarshalAs(UnmanagedType.U4)] ref int pcchBuffer);
  821. int IsLoaded([In] IntPtr hndProcess);
  822. // Marshal pcchBuffer as int even though it's unsigned. Error strings approaching 2 billion characters are currently unheard-of.
  823. [LCIDConversion(3)]
  824. void LoadErrorString([In, MarshalAs(UnmanagedType.U4)] int iResourceID,
  825. [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzBuffer,
  826. [In, Out, MarshalAs(UnmanagedType.U4)] ref int pcchBuffer,
  827. [In] int iLocaleID);
  828. IntPtr LoadLibrary([In, MarshalAs(UnmanagedType.LPWStr)] string pwzDllName);
  829. IntPtr GetProcAddress([In, MarshalAs(UnmanagedType.LPStr)] string pszProcName);
  830. [return: MarshalAs(UnmanagedType.IUnknown)]
  831. System.Object GetInterface([In] ref Guid rclsid, [In] ref Guid riid);
  832. }
  833. /// <summary>
  834. /// Wrapper for the ICLRDebugging shim interface. This interface exposes the native pipeline
  835. /// architecture startup APIs
  836. /// </summary>
  837. public sealed class CLRDebugging
  838. {
  839. private static readonly Guid clsidCLRDebugging = new Guid("BACC578D-FBDD-48a4-969F-02D932B74634");
  840. private ICLRDebugging m_CLRDebugging;
  841. /// <summary>
  842. /// Constructor
  843. /// </summary>
  844. /// <remarks>Creates the underlying interface from mscoree!CLRCreateInstance</remarks>
  845. public CLRDebugging()
  846. {
  847. object o;
  848. Guid ifaceId = typeof(ICLRDebugging).GUID;
  849. Guid clsid = clsidCLRDebugging;
  850. NativeMethods.CLRCreateInstance(ref clsid, ref ifaceId, out o);
  851. m_CLRDebugging = (ICLRDebugging)o;
  852. }
  853. /// <summary>
  854. /// Detects if a native module represents a CLR and if so provides the debugging interface
  855. /// and versioning information
  856. /// </summary>
  857. /// <param name="moduleBaseAddress">The native base address of a module which might be a CLR</param>
  858. /// <param name="dataTarget">The process abstraction which can be used for inspection</param>
  859. /// <param name="libraryProvider">A callback interface for locating version specific debug libraries
  860. /// such as mscordbi.dll and mscordacwks.dll</param>
  861. /// <param name="maxDebuggerSupportedVersion">The highest version of the CLR/debugging libraries which
  862. /// the caller can support</param>
  863. /// <param name="version">The version of the CLR detected or null if no CLR was detected</param>
  864. /// <param name="flags">Flags which have additional information about the CLR.
  865. /// See ClrDebuggingProcessFlags for more details</param>
  866. /// <returns>The CLR's debugging interface</returns>
  867. public CorProcess OpenVirtualProcess(ulong moduleBaseAddress,
  868. ICorDebugDataTarget dataTarget,
  869. ICLRDebuggingLibraryProvider libraryProvider,
  870. Version maxDebuggerSupportedVersion,
  871. out Version version,
  872. out ClrDebuggingProcessFlags flags)
  873. {
  874. CorProcess process;
  875. int hr = TryOpenVirtualProcess(moduleBaseAddress, dataTarget, libraryProvider, maxDebuggerSupportedVersion, out version, out flags, out process);
  876. if (hr < 0)
  877. throw new COMException("Failed to OpenVirtualProcess for module at " + moduleBaseAddress + ".", hr);
  878. return process;
  879. }
  880. /// <summary>
  881. /// Version of the above that doesn't throw exceptions on failure
  882. /// </summary>
  883. public int TryOpenVirtualProcess(ulong moduleBaseAddress,
  884. ICorDebugDataTarget dataTarget,
  885. ICLRDebuggingLibraryProvider libraryProvider,
  886. Version maxDebuggerSupportedVersion,
  887. out Version version,
  888. out ClrDebuggingProcessFlags flags,
  889. out CorProcess process)
  890. {
  891. ClrDebuggingVersion maxSupport = new ClrDebuggingVersion();
  892. ClrDebuggingVersion clrVersion = new ClrDebuggingVersion();
  893. maxSupport.StructVersion = 0;
  894. maxSupport.Major = (short)maxDebuggerSupportedVersion.Major;
  895. maxSupport.Minor = (short)maxDebuggerSupportedVersion.Minor;
  896. maxSupport.Build = (short)maxDebuggerSupportedVersion.Build;
  897. maxSupport.Revision = (short)maxDebuggerSupportedVersion.Revision;
  898. object processIface = null;
  899. clrVersion.StructVersion = 0;
  900. Guid iid = typeof(ICorDebugProcess).GUID;
  901. int result = m_CLRDebugging.OpenVirtualProcess(moduleBaseAddress, dataTarget, libraryProvider,
  902. ref maxSupport, ref iid, out processIface, ref clrVersion, out flags);
  903. // This may be set regardless of success/failure
  904. version = new Version(clrVersion.Major, clrVersion.Minor, clrVersion.Build, clrVersion.Revision);
  905. if (result < 0)
  906. {
  907. // OpenVirtualProcess failed
  908. process = null;
  909. return result;
  910. }
  911. // Success
  912. process = CorProcess.GetCorProcess((ICorDebugProcess)processIface);
  913. return 0;
  914. }
  915. /// <summary>
  916. /// Determines if the module is no longer in use
  917. /// </summary>
  918. /// <param name="moduleHandle">A module handle that was provided via the ILibraryProvider</param>
  919. /// <returns>True if the module can be unloaded, False otherwise</returns>
  920. public bool CanUnloadNow(IntPtr moduleHandle)
  921. {
  922. int ret = m_CLRDebugging.CanUnloadNow(moduleHandle);
  923. if (ret == (int)HResult.S_OK)
  924. return true;
  925. else if (ret == (int)HResult.S_FALSE)
  926. return false;
  927. else
  928. Marshal.ThrowExceptionForHR(ret);
  929. //unreachable
  930. throw new Exception();
  931. }
  932. }
  933. /// <summary>
  934. /// Represents a version of the CLR runtime
  935. /// </summary>
  936. public struct ClrDebuggingVersion
  937. {
  938. public short StructVersion;
  939. public short Major;
  940. public short Minor;
  941. public short Build;
  942. public short Revision;
  943. }
  944. /// <summary>
  945. /// Information flags about the state of a CLR when it is being attached
  946. /// to in the native pipeline debugging model
  947. /// </summary>
  948. public enum ClrDebuggingProcessFlags
  949. {
  950. // This CLR has a non-catchup managed debug event to send after jit attach is complete
  951. ManagedDebugEventPending = 1
  952. }
  953. /// <summary>
  954. /// This interface exposes the native pipeline architecture startup APIs
  955. /// </summary>
  956. [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("D28F3C5A-9634-4206-A509-477552EEFB10")]
  957. public interface ICLRDebugging
  958. {
  959. /// <summary>
  960. /// Detects if a native module represents a CLR and if so provides the debugging interface
  961. /// and versioning information
  962. /// </summary>
  963. /// <param name="moduleBaseAddress">The native base address of a module which might be a CLR</param>
  964. /// <param name="dataTarget">The process abstraction which can be used for inspection</param>
  965. /// <param name="libraryProvider">A callback interface for locating version specific debug libraries
  966. /// such as mscordbi.dll and mscordacwks.dll</param>
  967. /// <param name="maxDebuggerSupportedVersion">The highest version of the CLR/debugging libraries which
  968. /// the caller can support</param>
  969. /// <param name="process">The CLR's debugging interface or null if no debugger was detected</param>
  970. /// <param name="version">The version of the CLR detected or null if no CLR was detected</param>
  971. /// <param name="flags">Flags which have additional information about the CLR.
  972. /// See ClrDebuggingProcessFlags for more details</param>
  973. /// <returns>HResults.S_OK if an appropriate version CLR was detected, otherwise an appropriate
  974. /// error hresult</returns>
  975. [PreserveSig]
  976. int OpenVirtualProcess([In] ulong moduleBaseAddress,
  977. [In, MarshalAs(UnmanagedType.IUnknown)] object dataTarget,
  978. [In, MarshalAs(UnmanagedType.Interface)] ICLRDebuggingLibraryProvider libraryProvider,
  979. [In] ref ClrDebuggingVersion maxDebuggerSupportedVersion,
  980. [In] ref Guid riidProcess,
  981. [Out, MarshalAs(UnmanagedType.IUnknown)] out object process,
  982. [In, Out] ref ClrDebuggingVersion version,
  983. [Out] out ClrDebuggingProcessFlags flags);
  984. /// <summary>
  985. /// Determines if the module is no longer in use
  986. /// </summary>
  987. /// <param name="moduleHandle">A module handle that was provided via the ILibraryProvider</param>
  988. /// <returns>HResults.S_OK if the module can be unloaded, HResults.S_FALSE if it is in use
  989. /// or an appropriate error hresult otherwise</returns>
  990. [PreserveSig]
  991. int CanUnloadNow(IntPtr moduleHandle);
  992. }
  993. /// <summary>
  994. /// Provides version specific debugging libraries such as mscordbi.dll and mscordacwks.dll during
  995. /// startup in the native pipeline debugging architecture
  996. /// </su