// This function is used to terminate a process that the SampleEngine launched // The debugger will call IDebugEngineLaunch2::CanTerminateProcess before calling this method. int IDebugEngineLaunch2.TerminateProcess(IDebugProcess2 process) { Debug.Assert(Worker.MainThreadId == Worker.CurrentThreadId); Debug.Assert(m_pollThread != null); Debug.Assert(m_engineCallback != null); Debug.Assert(m_debuggedProcess != null); try { int processId = EngineUtils.GetProcessId(process); if (processId != m_debuggedProcess.Id) { return(Constants.S_FALSE); } m_debuggedProcess.Terminate(); return(Constants.S_OK); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Called by the SDM to indicate that a synchronous debug event, previously sent by the DE to the SDM, // was received and processed. The only event the sample engine sends in this fashion is Program Destroy. // It responds to that event by shutting down the engine. int IDebugEngine2.ContinueFromSynchronousEvent(IDebugEvent2 eventObject) { Debug.Assert(Worker.MainThreadId == Worker.CurrentThreadId); try { if (eventObject is AD7ProgramDestroyEvent) { WorkerThread pollThread = m_pollThread; DebuggedProcess debuggedProcess = m_debuggedProcess; m_engineCallback = null; m_debuggedProcess = null; m_pollThread = null; m_ad7ProgramId = Guid.Empty; debuggedProcess.Close(); pollThread.Close(); } else { Debug.Fail("Unknown syncronious event"); } } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } return(Constants.S_OK); }
// Gets the document context for this stack frame. The debugger will call this when the current stack frame is changed // and will use it to open the correct source document for this stack frame. int IDebugStackFrame2.GetDocumentContext(out IDebugDocumentContext2 docContext) { docContext = null; try { if (m_hasSource) { // Assume all lines begin and end at the beginning of the line. TEXT_POSITION begTp = new TEXT_POSITION(); begTp.dwColumn = 0; begTp.dwLine = m_lineNum - 1; TEXT_POSITION endTp = new TEXT_POSITION(); endTp.dwColumn = 0; endTp.dwLine = m_lineNum - 1; docContext = new AD7DocumentContext(m_documentName, begTp, endTp, null); return Constants.S_OK; } } catch (ComponentException e) { return e.HResult; } catch (Exception e) { return EngineUtils.UnexpectedException(e); } return Constants.S_FALSE; }
// Binds this pending breakpoint to one or more code locations. int IDebugPendingBreakpoint2.Bind() { try { if (CanBind()) { IDebugDocumentPosition2 docPosition = (IDebugDocumentPosition2)(Marshal.GetObjectForIUnknown(m_bpRequestInfo.bpLocation.unionmember2)); // Get the name of the document that the breakpoint was put in string documentName; EngineUtils.CheckOk(docPosition.GetFileName(out documentName)); // Get the location in the document that the breakpoint is in. TEXT_POSITION[] startPosition = new TEXT_POSITION[1]; TEXT_POSITION[] endPosition = new TEXT_POSITION[1]; EngineUtils.CheckOk(docPosition.GetRange(startPosition, endPosition)); // Ask the symbol engine to find all addresses in all modules with symbols that match this source and line number. uint[] addresses = m_engine.DebuggedProcess.GetAddressesForSourceLocation(null, documentName, startPosition[0].dwLine + 1, startPosition[0].dwColumn); lock (m_boundBreakpoints) { foreach (uint addr in addresses) { AD7BreakpointResolution breakpointResolution = new AD7BreakpointResolution(m_engine, addr, GetDocumentContext(addr)); AD7BoundBreakpoint boundBreakpoint = new AD7BoundBreakpoint(m_engine, addr, this, breakpointResolution); m_boundBreakpoints.Add(boundBreakpoint); m_engine.DebuggedProcess.SetBreakpoint(addr, boundBreakpoint); } } return(Constants.S_OK); } else { // The breakpoint could not be bound. This may occur for many reasons such as an invalid location, an invalid expression, etc... // The sample engine does not support this, but a real world engine will want to send an instance of IDebugBreakpointErrorEvent2 to the // UI and return a valid instance of IDebugErrorBreakpoint2 from IDebugPendingBreakpoint2::EnumErrorBreakpoints. The debugger will then // display information about why the breakpoint did not bind to the user. return(Constants.S_FALSE); } } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Resume a process launched by IDebugEngineLaunch2.LaunchSuspended int IDebugEngineLaunch2.ResumeProcess(IDebugProcess2 process) { Debug.Assert(Worker.MainThreadId == Worker.CurrentThreadId); Debug.Assert(m_pollThread != null); Debug.Assert(m_engineCallback != null); Debug.Assert(m_debuggedProcess != null); Debug.Assert(m_ad7ProgramId == Guid.Empty); try { int processId = EngineUtils.GetProcessId(process); if (processId != m_debuggedProcess.Id) { return(Constants.S_FALSE); } // Send a program node to the SDM. This will cause the SDM to turn around and call IDebugEngine2.Attach // which will complete the hookup with AD7 IDebugPort2 port; EngineUtils.RequireOk(process.GetPort(out port)); IDebugDefaultPort2 defaultPort = (IDebugDefaultPort2)port; IDebugPortNotify2 portNotify; EngineUtils.RequireOk(defaultPort.GetPortNotify(out portNotify)); EngineUtils.RequireOk(portNotify.AddProgramNode(new AD7ProgramNode(m_debuggedProcess.Id))); if (m_ad7ProgramId == Guid.Empty) { Debug.Fail("Unexpected problem -- IDebugEngine2.Attach wasn't called"); return(Constants.E_FAIL); } // Resume the threads in the debuggee process m_pollThread.RunOperation(new Operation(delegate { m_debuggedProcess.ResumeFromLaunch(); })); return(Constants.S_OK); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Parses a text-based expression for evaluation. // The engine sample only supports locals and parameters so the only task here is to check the names in those collections. int IDebugExpressionContext2.ParseText(string pszCode, enum_PARSEFLAGS dwFlags, uint nRadix, out IDebugExpression2 ppExpr, out string pbstrError, out uint pichError) { pbstrError = ""; pichError = 0; ppExpr = null; try { if (m_parameters != null) { foreach (VariableInformation currVariable in m_parameters) { if (String.CompareOrdinal(currVariable.m_name, pszCode) == 0) { ppExpr = new AD7Expression(currVariable); return Constants.S_OK; } } } if (m_locals != null) { foreach (VariableInformation currVariable in m_locals) { if (String.CompareOrdinal(currVariable.m_name, pszCode) == 0) { ppExpr = new AD7Expression(currVariable); return Constants.S_OK; } } } pbstrError = "Invalid Expression"; pichError = (uint)pbstrError.Length; return Constants.S_FALSE; } catch (ComponentException e) { return e.HResult; } catch (Exception e) { return EngineUtils.UnexpectedException(e); } }
// Gets properties that describe a thread. int IDebugThread2.GetThreadProperties(enum_THREADPROPERTY_FIELDS dwFields, THREADPROPERTIES[] propertiesArray) { try { THREADPROPERTIES props = new THREADPROPERTIES(); if ((dwFields & enum_THREADPROPERTY_FIELDS.TPF_ID) != 0) { props.dwThreadId = (uint)m_debuggedThread.Id; props.dwFields |= enum_THREADPROPERTY_FIELDS.TPF_ID; } if ((dwFields & enum_THREADPROPERTY_FIELDS.TPF_SUSPENDCOUNT) != 0) { // sample debug engine doesn't support suspending threads props.dwFields |= enum_THREADPROPERTY_FIELDS.TPF_SUSPENDCOUNT; } if ((dwFields & enum_THREADPROPERTY_FIELDS.TPF_STATE) != 0) { props.dwThreadState = (uint)enum_THREADSTATE.THREADSTATE_RUNNING; props.dwFields |= enum_THREADPROPERTY_FIELDS.TPF_STATE; } if ((dwFields & enum_THREADPROPERTY_FIELDS.TPF_PRIORITY) != 0) { props.bstrPriority = "Normal"; props.dwFields |= enum_THREADPROPERTY_FIELDS.TPF_PRIORITY; } if ((dwFields & enum_THREADPROPERTY_FIELDS.TPF_NAME) != 0) { props.bstrName = ThreadNameString; props.dwFields |= enum_THREADPROPERTY_FIELDS.TPF_NAME; } if ((dwFields & enum_THREADPROPERTY_FIELDS.TPF_LOCATION) != 0) { props.bstrLocation = GetCurrentLocation(true); props.dwFields |= enum_THREADPROPERTY_FIELDS.TPF_LOCATION; } return(Constants.S_OK); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Creates a pending breakpoint in the engine. A pending breakpoint is contains all the information needed to bind a breakpoint to // a location in the debuggee. int IDebugEngine2.CreatePendingBreakpoint(IDebugBreakpointRequest2 pBPRequest, out IDebugPendingBreakpoint2 ppPendingBP) { Debug.Assert(m_breakpointManager != null); ppPendingBP = null; try { m_breakpointManager.CreatePendingBreakpoint(pBPRequest, out ppPendingBP); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } return(Constants.S_OK); }
// Gets a description of the stack frame. int IDebugStackFrame2.GetInfo(enum_FRAMEINFO_FLAGS dwFieldSpec, uint nRadix, FRAMEINFO[] pFrameInfo) { try { SetFrameInfo(dwFieldSpec, out pFrameInfo[0]); return Constants.S_OK; } catch (ComponentException e) { return e.HResult; } catch (Exception e) { return EngineUtils.UnexpectedException(e); } }
// Gets the code context for this stack frame. The code context represents the current instruction pointer in this stack frame. int IDebugStackFrame2.GetCodeContext(out IDebugCodeContext2 memoryAddress) { memoryAddress = null; try { memoryAddress = new AD7MemoryAddress(m_engine, m_threadContext.eip); return Constants.S_OK; } catch (ComponentException e) { return e.HResult; } catch (Exception e) { return EngineUtils.UnexpectedException(e); } }
// Launches a process by means of the debug engine. // Normally, Visual Studio launches a program using the IDebugPortEx2::LaunchSuspended method and then attaches the debugger // to the suspended program. However, there are circumstances in which the debug engine may need to launch a program // (for example, if the debug engine is part of an interpreter and the program being debugged is an interpreted language), // in which case Visual Studio uses the IDebugEngineLaunch2::LaunchSuspended method // The IDebugEngineLaunch2::ResumeProcess method is called to start the process after the process has been successfully launched in a suspended state. int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 port, string exe, string args, string dir, string env, string options, enum_LAUNCH_FLAGS launchFlags, uint hStdInput, uint hStdOutput, uint hStdError, IDebugEventCallback2 ad7Callback, out IDebugProcess2 process) { Debug.Assert(Worker.MainThreadId == Worker.CurrentThreadId); Debug.Assert(m_pollThread == null); Debug.Assert(m_engineCallback == null); Debug.Assert(m_debuggedProcess == null); Debug.Assert(m_ad7ProgramId == Guid.Empty); process = null; try { string commandLine = EngineUtils.BuildCommandLine(exe, args); ProcessLaunchInfo processLaunchInfo = new ProcessLaunchInfo(exe, commandLine, dir, env, options, (uint)launchFlags, hStdInput, hStdOutput, hStdError); // We are being asked to debug a process when we currently aren't debugging anything m_pollThread = new WorkerThread(); m_engineCallback = new EngineCallback(this, ad7Callback); // Complete the win32 attach on the poll thread m_pollThread.RunOperation(new Operation(delegate { m_debuggedProcess = Worker.LaunchProcess(m_engineCallback, processLaunchInfo); })); AD_PROCESS_ID adProcessId = new AD_PROCESS_ID(); adProcessId.ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_SYSTEM; adProcessId.dwProcessId = (uint)m_debuggedProcess.Id; EngineUtils.RequireOk(port.GetProcess(adProcessId, out process)); return(Constants.S_OK); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Retrieves a list of all code contexts associated with this document context. // The engine sample only supports one code context per document context and // the code contexts are always memory addresses. int IDebugDocumentContext2.EnumCodeContexts(out IEnumDebugCodeContexts2 ppEnumCodeCxts) { ppEnumCodeCxts = null; try { AD7MemoryAddress[] codeContexts = new AD7MemoryAddress[1]; codeContexts[0] = m_codeContext; ppEnumCodeCxts = new AD7CodeContextEnum(codeContexts); return(Constants.S_OK); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Gets the name of the stack frame. // The name of a stack frame is typically the name of the method being executed. int IDebugStackFrame2.GetName(out string name) { name = null; try { name = EngineUtils.GetAddressDescription(null, m_threadContext.eip); return Constants.S_OK; } catch (ComponentException e) { return e.HResult; } catch (Exception e) { return EngineUtils.UnexpectedException(e); } }
// Retrieves a list of the stack frames for this thread. // For the sample engine, enumerating the stack frames requires walking the callstack in the debuggee for this thread // and coverting that to an implementation of IEnumDebugFrameInfo2. // Real engines will most likely want to cache this information to avoid recomputing it each time it is asked for, // and or construct it on demand instead of walking the entire stack. int IDebugThread2.EnumFrameInfo(enum_FRAMEINFO_FLAGS dwFieldSpec, uint nRadix, out IEnumDebugFrameInfo2 enumObject) { // Ask the lower-level to perform a stack walk on this thread m_engine.DebuggedProcess.DoStackWalk(this.m_debuggedThread); enumObject = null; try { System.Collections.Generic.List <X86ThreadContext> stackFrames = this.m_debuggedThread.StackFrames; int numStackFrames = stackFrames.Count; FRAMEINFO[] frameInfoArray; if (numStackFrames == 0) { // failed to walk any frames. Only return the top frame. frameInfoArray = new FRAMEINFO[1]; AD7StackFrame frame = new AD7StackFrame(m_engine, this, GetThreadContext()); frame.SetFrameInfo(dwFieldSpec, out frameInfoArray[0]); } else { frameInfoArray = new FRAMEINFO[numStackFrames]; for (int i = 0; i < numStackFrames; i++) { AD7StackFrame frame = new AD7StackFrame(m_engine, this, stackFrames[i]); frame.SetFrameInfo(dwFieldSpec, out frameInfoArray[i]); } } enumObject = new AD7FrameInfoEnum(frameInfoArray); return(Constants.S_OK); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Creates an enumerator for properties associated with the stack frame, such as local variables. // The sample engine only supports returning locals and parameters. Other possible values include // class fields (this pointer), registers, exceptions... int IDebugStackFrame2.EnumProperties(enum_DEBUGPROP_INFO_FLAGS dwFields, uint nRadix, ref Guid guidFilter, uint dwTimeout, out uint elementsReturned, out IEnumDebugPropertyInfo2 enumObject) { int hr; elementsReturned = 0; enumObject = null; try { if (guidFilter == AD7Guids.guidFilterLocalsPlusArgs || guidFilter == AD7Guids.guidFilterAllLocalsPlusArgs || guidFilter == AD7Guids.guidFilterAllLocals) { CreateLocalsPlusArgsProperties(out elementsReturned, out enumObject); hr = Constants.S_OK; } else if (guidFilter == AD7Guids.guidFilterLocals) { CreateLocalProperties(out elementsReturned, out enumObject); hr = Constants.S_OK; } else if (guidFilter == AD7Guids.guidFilterArgs) { CreateParameterProperties(out elementsReturned, out enumObject); hr = Constants.S_OK; } else { hr = Constants.E_NOTIMPL; } } catch (ComponentException e) { return e.HResult; } catch (Exception e) { return EngineUtils.UnexpectedException(e); } return hr; }
// Gets the file statement range of the document context. // A statement range is the range of the lines that contributed the code to which this document context refers. int IDebugDocumentContext2.GetStatementRange(TEXT_POSITION[] pBegPosition, TEXT_POSITION[] pEndPosition) { try { pBegPosition[0].dwColumn = m_begPos.dwColumn; pBegPosition[0].dwLine = m_begPos.dwLine; pEndPosition[0].dwColumn = m_endPos.dwColumn; pEndPosition[0].dwLine = m_endPos.dwLine; } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } return(Constants.S_OK); }
// Gets information that describes this context. public int GetInfo(enum_CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO[] pinfo) { try { pinfo[0].dwFields = 0; if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS) != 0) { pinfo[0].bstrAddress = m_address.ToString(); pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS; } // Fields not supported by the sample if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSOFFSET) != 0) { } if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESSABSOLUTE) != 0) { } if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_MODULEURL) != 0) { } if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_FUNCTION) != 0) { } if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_FUNCTIONOFFSET) != 0) { } return(Constants.S_OK); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Attach the debug engine to a program. int IDebugEngine2.Attach(IDebugProgram2[] rgpPrograms, IDebugProgramNode2[] rgpProgramNodes, uint celtPrograms, IDebugEventCallback2 ad7Callback, enum_ATTACH_REASON dwReason) { Debug.Assert(Worker.MainThreadId == Worker.CurrentThreadId); Debug.Assert(m_ad7ProgramId == Guid.Empty); if (celtPrograms != 1) { Debug.Fail("SampleEngine only expects to see one program in a process"); throw new ArgumentException(); } try { int processId = EngineUtils.GetProcessId(rgpPrograms[0]); if (processId == 0) { return(Constants.E_NOTIMPL); // sample engine only supports system processes } EngineUtils.RequireOk(rgpPrograms[0].GetProgramId(out m_ad7ProgramId)); // Attach can either be called to attach to a new process, or to complete an attach // to a launched process if (m_pollThread == null) { // We are being asked to debug a process when we currently aren't debugging anything m_pollThread = new WorkerThread(); m_engineCallback = new EngineCallback(this, ad7Callback); // Complete the win32 attach on the poll thread m_pollThread.RunOperation(new Operation(delegate { m_debuggedProcess = Worker.AttachToProcess(m_engineCallback, processId); })); m_pollThread.SetDebugProcess(m_debuggedProcess); } else { if (processId != m_debuggedProcess.Id) { Debug.Fail("Asked to attach to a process while we are debugging"); return(Constants.E_FAIL); } m_pollThread.SetDebugProcess(m_debuggedProcess); } AD7EngineCreateEvent.Send(this); AD7ProgramCreateEvent.Send(this); // start polling for debug events on the poll thread m_pollThread.RunOperationAsync(new Operation(delegate { m_debuggedProcess.ResumeEventPump(); })); return(Constants.S_OK); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Compares the memory context to each context in the given array in the manner indicated by compare flags, // returning an index of the first context that matches. public int Compare(enum_CONTEXT_COMPARE uContextCompare, IDebugMemoryContext2[] compareToItems, uint compareToLength, out uint foundIndex) { foundIndex = uint.MaxValue; try { enum_CONTEXT_COMPARE contextCompare = (enum_CONTEXT_COMPARE)uContextCompare; for (uint c = 0; c < compareToLength; c++) { AD7MemoryAddress compareTo = compareToItems[c] as AD7MemoryAddress; if (compareTo == null) { continue; } if (!AD7Engine.ReferenceEquals(this.m_engine, compareTo.m_engine)) { continue; } bool result; switch (contextCompare) { case enum_CONTEXT_COMPARE.CONTEXT_EQUAL: result = (this.m_address == compareTo.m_address); break; case enum_CONTEXT_COMPARE.CONTEXT_LESS_THAN: result = (this.m_address < compareTo.m_address); break; case enum_CONTEXT_COMPARE.CONTEXT_GREATER_THAN: result = (this.m_address > compareTo.m_address); break; case enum_CONTEXT_COMPARE.CONTEXT_LESS_THAN_OR_EQUAL: result = (this.m_address <= compareTo.m_address); break; case enum_CONTEXT_COMPARE.CONTEXT_GREATER_THAN_OR_EQUAL: result = (this.m_address >= compareTo.m_address); break; // The sample debug engine doesn't understand scopes or functions case enum_CONTEXT_COMPARE.CONTEXT_SAME_SCOPE: case enum_CONTEXT_COMPARE.CONTEXT_SAME_FUNCTION: result = (this.m_address == compareTo.m_address); break; case enum_CONTEXT_COMPARE.CONTEXT_SAME_MODULE: result = (this.m_address == compareTo.m_address); if (result == false) { DebuggedModule module = m_engine.DebuggedProcess.ResolveAddress(m_address); if (module != null) { result = (compareTo.m_address >= module.BaseAddress) && (compareTo.m_address < module.BaseAddress + module.Size); } } break; case enum_CONTEXT_COMPARE.CONTEXT_SAME_PROCESS: result = true; break; default: // A new comparison was invented that we don't support return(Constants.E_NOTIMPL); } if (result) { foundIndex = c; return(Constants.S_OK); } } return(Constants.S_FALSE); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }
// Gets the MODULE_INFO that describes this module. // This is how the debugger obtains most of the information about the module. int IDebugModule2.GetInfo(enum_MODULE_INFO_FIELDS dwFields, MODULE_INFO[] infoArray) { try { MODULE_INFO info = new MODULE_INFO(); if ((dwFields & enum_MODULE_INFO_FIELDS.MIF_NAME) != 0) { info.m_bstrName = System.IO.Path.GetFileName(this.DebuggedModule.Name); info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_NAME; } if ((dwFields & enum_MODULE_INFO_FIELDS.MIF_URL) != 0) { info.m_bstrUrl = this.DebuggedModule.Name; info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_URL; } if ((dwFields & enum_MODULE_INFO_FIELDS.MIF_LOADADDRESS) != 0) { info.m_addrLoadAddress = (ulong)this.DebuggedModule.BaseAddress; info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_LOADADDRESS; } if ((dwFields & enum_MODULE_INFO_FIELDS.MIF_PREFFEREDADDRESS) != 0) { // A debugger that actually supports showing the preferred base should crack the PE header and get // that field. This debugger does not do that, so assume the module loaded where it was suppose to. info.m_addrPreferredLoadAddress = (ulong)this.DebuggedModule.BaseAddress; info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_PREFFEREDADDRESS;; } if ((dwFields & enum_MODULE_INFO_FIELDS.MIF_SIZE) != 0) { info.m_dwSize = this.DebuggedModule.Size; info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_SIZE; } if ((dwFields & enum_MODULE_INFO_FIELDS.MIF_LOADORDER) != 0) { info.m_dwLoadOrder = this.DebuggedModule.GetLoadOrder(); info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_LOADORDER; } if ((dwFields & enum_MODULE_INFO_FIELDS.MIF_URLSYMBOLLOCATION) != 0) { if (this.DebuggedModule.SymbolsLoaded) { info.m_bstrUrlSymbolLocation = this.DebuggedModule.SymbolPath; info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_URLSYMBOLLOCATION; } } if ((dwFields & enum_MODULE_INFO_FIELDS.MIF_FLAGS) != 0) { info.m_dwModuleFlags = 0; if (this.DebuggedModule.SymbolsLoaded) { info.m_dwModuleFlags |= (enum_MODULE_FLAGS.MODULE_FLAG_SYMBOLS); } info.dwValidFields |= enum_MODULE_INFO_FIELDS.MIF_FLAGS; } infoArray[0] = info; return(Constants.S_OK); } catch (ComponentException e) { return(e.HResult); } catch (Exception e) { return(EngineUtils.UnexpectedException(e)); } }