mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 20:39:01 +00:00
493 lines
21 KiB
C#
493 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Microsoft.VisualStudio;
|
|
using Microsoft.VisualStudio.Debugger.Interop;
|
|
using System.Diagnostics;
|
|
using Cosmos.Debug.Common;
|
|
using System.Windows.Forms;
|
|
using Dapper;
|
|
using DapperExtensions;
|
|
using SQLinq;
|
|
using SQLinq.Dapper;
|
|
|
|
namespace Cosmos.Debug.VSDebugEngine {
|
|
// Represents a logical stack frame on the thread stack.
|
|
// Also implements the IDebugExpressionContext interface, which allows expression evaluation and watch windows.
|
|
public class AD7StackFrame : IDebugStackFrame2, IDebugExpressionContext2 {
|
|
readonly AD7Engine mEngine;
|
|
readonly AD7Thread mThread;
|
|
//readonly X86ThreadContext m_threadContext;
|
|
|
|
private string mDocName;
|
|
private string mFunctionName;
|
|
private uint mLineNum;
|
|
private bool mHasSource;
|
|
|
|
// Must have empty holders, some code looks at length and can run
|
|
// before we set them.
|
|
internal LOCAL_ARGUMENT_INFO[] mLocalInfos = new LOCAL_ARGUMENT_INFO[] {};
|
|
internal LOCAL_ARGUMENT_INFO[] mArgumentInfos = new LOCAL_ARGUMENT_INFO[] {};
|
|
|
|
// An array of this frame's parameters
|
|
private DebugLocalInfo[] mParams;
|
|
|
|
// An array of this frame's locals
|
|
private DebugLocalInfo[] mLocals;
|
|
private AD7Process mProcess;
|
|
|
|
public AD7StackFrame(AD7Engine aEngine, AD7Thread aThread, AD7Process aProcess) {
|
|
mEngine = aEngine;
|
|
mThread = aThread;
|
|
mProcess = aProcess;
|
|
var xProcess = mEngine.mProcess;
|
|
if (mHasSource = xProcess.mCurrentAddress.HasValue) {
|
|
UInt32 xAddress = xProcess.mCurrentAddress.Value;
|
|
var xSourceInfos = xProcess.mDebugInfoDb.GetSourceInfos(xAddress);
|
|
if (mHasSource = xSourceInfos.ContainsKey(xAddress)) {
|
|
var xSourceInfo = xSourceInfos[xAddress];
|
|
mDocName = xSourceInfo.SourceFile;
|
|
mFunctionName = xSourceInfo.MethodName;
|
|
mLineNum = (uint)xSourceInfo.Line;
|
|
|
|
// Multiple labels that point to a single address can happen because of exception handling exits etc.
|
|
// Because of this given an address, we might find more than one label that matches the address.
|
|
// Currently, the label we are looking for will always be the first one so we choose that one.
|
|
// In the future this might "break", so be careful about this. In the future we may need to classify
|
|
// labels in the output and mark them somehow.
|
|
var xLabelsForAddr = xProcess.mDebugInfoDb.GetLabels(xProcess.mCurrentAddress.Value);
|
|
if (xLabelsForAddr.Length > 0) {
|
|
MethodIlOp xSymbolInfo;
|
|
string xLabel = xLabelsForAddr[0]; // Necessary for LINQ
|
|
xSymbolInfo = aProcess.mDebugInfoDb.Connection.Query<MethodIlOp>(new SQLinq<MethodIlOp>().Where(q => q.LabelName == xLabel)).FirstOrDefault();
|
|
var xMethod = mProcess.mDebugInfoDb.Connection.Get<Method>(xSymbolInfo.MethodID);
|
|
if (xSymbolInfo != null)
|
|
{
|
|
var xAllInfos = aProcess.mDebugInfoDb.Connection.Query<LOCAL_ARGUMENT_INFO>(new SQLinq<LOCAL_ARGUMENT_INFO>().Where(q => q.METHODLABELNAME == xMethod.LabelCall));
|
|
mLocalInfos = xAllInfos.Where(q => !q.IsArgument).ToArray();
|
|
mArgumentInfos = xAllInfos.Where(q => q.IsArgument).ToArray();
|
|
if (mArgumentInfos.Length > 0)
|
|
{
|
|
mParams = new DebugLocalInfo[mArgumentInfos.Length];
|
|
for (int i = 0; i < mArgumentInfos.Length; i++)
|
|
{
|
|
mParams[i] = new DebugLocalInfo
|
|
{
|
|
Name = mArgumentInfos[i].NAME,
|
|
Index = i,
|
|
IsLocal = false
|
|
};
|
|
}
|
|
}
|
|
|
|
if (mLocalInfos.Length > 0)
|
|
{
|
|
mLocals = new DebugLocalInfo[mLocalInfos.Length];
|
|
for (int i = 0; i < mLocalInfos.Length; i++)
|
|
{
|
|
mLocals[i] = new DebugLocalInfo
|
|
{
|
|
Name = mLocalInfos[i].NAME,
|
|
Index = i,
|
|
IsLocal = true
|
|
};
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
MessageBox.Show("No Symbol found for address 0x" + xProcess.mCurrentAddress.Value.ToString("X8").ToUpper());
|
|
}
|
|
xProcess.DebugMsg(String.Format("StackFrame: Returning: {0}#{1}[{2}]", mDocName, mFunctionName, mLineNum));
|
|
}
|
|
}
|
|
if (!mHasSource) {
|
|
xProcess.DebugMsg("StackFrame: No Source available");
|
|
}
|
|
|
|
// If source information is available, create the collections of locals and parameters and populate them with
|
|
// values from the debuggee.
|
|
//if (m_hasSource) {
|
|
//if (mArgumentInfos.Length > 0) {
|
|
//m_parameters = new VariableInformation[m_numParameters];
|
|
//m_engine.DebuggedProcess.GetFunctionArgumentsByIP(m_threadContext.eip, m_threadContext.ebp, m_parameters);
|
|
//}
|
|
|
|
//if (mLocalInfos.Length > 0) {
|
|
//m_locals = new VariableInformation[m_numLocals];
|
|
//m_engine.DebuggedProcess.GetFunctionLocalsByIP(m_threadContext.eip, m_threadContext.ebp, m_locals);
|
|
//}
|
|
//}
|
|
}
|
|
|
|
#region Non-interface methods
|
|
|
|
// Construct a FRAMEINFO for this stack frame with the requested information.
|
|
public void SetFrameInfo(enum_FRAMEINFO_FLAGS dwFieldSpec, out FRAMEINFO frameInfo) {
|
|
System.Diagnostics.Debug.WriteLine("In AD7StackFrame.SetFrameInfo");
|
|
System.Diagnostics.Debug.WriteLine("\tdwFieldSpec = " + dwFieldSpec.ToString());
|
|
frameInfo = new FRAMEINFO();
|
|
|
|
//uint ip = m_threadContext.eip;
|
|
//DebuggedModule module = null;// m_engine.DebuggedProcess.ResolveAddress(ip);
|
|
|
|
// The debugger is asking for the formatted name of the function which is displayed in the callstack window.
|
|
// There are several optional parts to this name including the module, argument types and values, and line numbers.
|
|
// The optional information is requested by setting flags in the dwFieldSpec parameter.
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME)) {
|
|
// If there is source information, construct a string that contains the module name, function name, and optionally argument names and values.
|
|
if (mHasSource) {
|
|
frameInfo.m_bstrFuncName = "";
|
|
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_MODULE)) {
|
|
// m_
|
|
//frameInfo.m_bstrFuncName = System.IO.Path.GetFileName(module.Name) + "!";
|
|
frameInfo.m_bstrFuncName = "module!";
|
|
}
|
|
|
|
frameInfo.m_bstrFuncName += mFunctionName;
|
|
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS) && mArgumentInfos.Length > 0) {
|
|
frameInfo.m_bstrFuncName += "(";
|
|
for (int i = 0; i < mParams.Length; i++) {
|
|
if ((dwFieldSpec & enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS_TYPES) != 0) {
|
|
//frameInfo.m_bstrFuncName += m_parameters[i]. + " ";
|
|
frameInfo.m_bstrFuncName += "ParamType ";
|
|
}
|
|
|
|
if ((dwFieldSpec & enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS_NAMES) != 0) {
|
|
frameInfo.m_bstrFuncName += mParams[i].Name;
|
|
}
|
|
|
|
// if ((dwFieldSpec & (uint)enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS_VALUES) != 0) {
|
|
// frameInfo.m_bstrFuncName += "=" + m_parameters[i].m_value;
|
|
// }
|
|
|
|
if (i < mParams.Length - 1) {
|
|
frameInfo.m_bstrFuncName += ", ";
|
|
}
|
|
}
|
|
frameInfo.m_bstrFuncName += ")";
|
|
}
|
|
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_LINES)) {
|
|
frameInfo.m_bstrFuncName += " Line:" + mLineNum.ToString();
|
|
}
|
|
} else {
|
|
// No source information, so only return the module name and the instruction pointer.
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_MODULE)) {
|
|
//frameInfo.m_bstrFuncName = EngineUtils.GetAddressDescription(module, ip);
|
|
} else {
|
|
//frameInfo.m_bstrFuncName = EngineUtils.GetAddressDescription(null, ip);
|
|
}
|
|
}
|
|
frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_FUNCNAME;
|
|
}
|
|
|
|
// The debugger is requesting the name of the module for this stack frame.
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_MODULE)) {
|
|
frameInfo.m_bstrModule = "module";
|
|
frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_MODULE;
|
|
}
|
|
|
|
// The debugger is requesting the range of memory addresses for this frame.
|
|
// For the sample engine, this is the contents of the frame pointer.
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_STACKRANGE)) {
|
|
//frameInfo.m_addrMin = m_threadContext.ebp;
|
|
//frameInfo.m_addrMax = m_threadContext.ebp;
|
|
frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_STACKRANGE;
|
|
}
|
|
|
|
// The debugger is requesting the IDebugStackFrame2 value for this frame info.
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_FRAME)) {
|
|
frameInfo.m_pFrame = this;
|
|
frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_FRAME;
|
|
}
|
|
|
|
// Does this stack frame of symbols loaded?
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_DEBUGINFO)) {
|
|
frameInfo.m_fHasDebugInfo = mHasSource ? 1 : 0;
|
|
frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_DEBUGINFO;
|
|
}
|
|
|
|
// Is this frame stale?
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_STALECODE)) {
|
|
frameInfo.m_fStaleCode = 0;
|
|
frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_STALECODE;
|
|
}
|
|
|
|
// The debugger would like a pointer to the IDebugModule2 that contains this stack frame.
|
|
if (dwFieldSpec.HasFlag(enum_FRAMEINFO_FLAGS.FIF_DEBUG_MODULEP)) {
|
|
if (mEngine.mModule != null) {
|
|
frameInfo.m_pModule = mEngine.mModule;
|
|
frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_DEBUG_MODULEP;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Construct an instance of IEnumDebugPropertyInfo2 for the combined locals and parameters.
|
|
private void CreateLocalsPlusArgsProperties(out uint elementsReturned, out IEnumDebugPropertyInfo2 enumObject) {
|
|
elementsReturned = 0;
|
|
|
|
int localsLength = 0;
|
|
|
|
if (mLocals != null) {
|
|
localsLength = mLocals.Length;
|
|
elementsReturned += (uint)localsLength;
|
|
}
|
|
|
|
if (mParams != null) {
|
|
elementsReturned += (uint)mParams.Length;
|
|
}
|
|
var propInfo = new DEBUG_PROPERTY_INFO[elementsReturned];
|
|
|
|
if (mLocals != null) {
|
|
for (int i = 0; i < mLocals.Length; i++) {
|
|
AD7Property property = new AD7Property(mLocals[i], this.mProcess, this);
|
|
propInfo[i] = property.ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_STANDARD);
|
|
}
|
|
}
|
|
|
|
if (mParams != null) {
|
|
for (int i = 0; i < mParams.Length; i++) {
|
|
AD7Property property = new AD7Property(mParams[i], this.mProcess, this);
|
|
propInfo[localsLength + i] = property.ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_STANDARD);
|
|
}
|
|
}
|
|
|
|
enumObject = new AD7PropertyInfoEnum(propInfo);
|
|
}
|
|
|
|
// Construct an instance of IEnumDebugPropertyInfo2 for the locals collection only.
|
|
private void CreateLocalProperties(out uint elementsReturned, out IEnumDebugPropertyInfo2 enumObject) {
|
|
elementsReturned = (uint)mLocals.Length;
|
|
var propInfo = new DEBUG_PROPERTY_INFO[mLocals.Length];
|
|
|
|
for (int i = 0; i < propInfo.Length; i++) {
|
|
AD7Property property = new AD7Property(mLocals[i], mProcess, this);
|
|
propInfo[i] = property.ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_STANDARD);
|
|
}
|
|
|
|
enumObject = new AD7PropertyInfoEnum(propInfo);
|
|
}
|
|
|
|
// Construct an instance of IEnumDebugPropertyInfo2 for the parameters collection only.
|
|
private void CreateParameterProperties(out uint elementsReturned, out IEnumDebugPropertyInfo2 enumObject) {
|
|
elementsReturned = (uint)mParams.Length;
|
|
var propInfo = new DEBUG_PROPERTY_INFO[mParams.Length];
|
|
|
|
for (int i = 0; i < propInfo.Length; i++) {
|
|
AD7Property property = new AD7Property(mParams[i], mProcess, this);
|
|
propInfo[i] = property.ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_STANDARD);
|
|
}
|
|
|
|
enumObject = new AD7PropertyInfoEnum(propInfo);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDebugStackFrame2 Members
|
|
|
|
// 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) {
|
|
CreateLocalsPlusArgsProperties(out elementsReturned, out enumObject);
|
|
hr = VSConstants.S_OK;
|
|
} else if (guidFilter == AD7Guids.guidFilterLocals) {
|
|
CreateLocalProperties(out elementsReturned, out enumObject);
|
|
hr = VSConstants.S_OK;
|
|
} else if (guidFilter == AD7Guids.guidFilterArgs) {
|
|
CreateParameterProperties(out elementsReturned, out enumObject);
|
|
hr = VSConstants.S_OK;
|
|
} else {
|
|
hr = VSConstants.E_NOTIMPL;
|
|
}
|
|
}
|
|
//catch (ComponentException e)
|
|
//{
|
|
// return e.HResult;
|
|
//}
|
|
catch (Exception e) {
|
|
return EngineUtils.UnexpectedException(e);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// 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 VSConstants.S_OK;
|
|
}
|
|
//catch (ComponentException e)
|
|
//{
|
|
// return e.HResult;
|
|
//}
|
|
catch (Exception e) {
|
|
return EngineUtils.UnexpectedException(e);
|
|
}
|
|
}
|
|
|
|
// Gets a description of the properties of a stack frame.
|
|
// Calling the IDebugProperty2::EnumChildren method with appropriate filters can retrieve the local variables, method parameters, registers, and "this"
|
|
// pointer associated with the stack frame. The debugger calls EnumProperties to obtain these values in the sample.
|
|
int IDebugStackFrame2.GetDebugProperty(out IDebugProperty2 property) {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
// 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 (mHasSource) {
|
|
// Assume all lines begin and end at the beginning of the line.
|
|
TEXT_POSITION begTp = new TEXT_POSITION();
|
|
begTp.dwColumn = 0;
|
|
begTp.dwLine = mLineNum - 1;
|
|
TEXT_POSITION endTp = new TEXT_POSITION();
|
|
endTp.dwColumn = 0;
|
|
endTp.dwLine = mLineNum - 1;
|
|
|
|
docContext = new AD7DocumentContext(mDocName, begTp, endTp, null);
|
|
return VSConstants.S_OK;
|
|
}
|
|
}
|
|
//catch (ComponentException e)
|
|
//{
|
|
// return e.HResult;
|
|
//}
|
|
catch (Exception e) {
|
|
return EngineUtils.UnexpectedException(e);
|
|
}
|
|
|
|
return VSConstants.S_FALSE;
|
|
}
|
|
|
|
// Gets an evaluation context for expression evaluation within the current context of a stack frame and thread.
|
|
// Generally, an expression evaluation context can be thought of as a scope for performing expression evaluation.
|
|
// Call the IDebugExpressionContext2::ParseText method to parse an expression and then call the resulting IDebugExpression2::EvaluateSync
|
|
// or IDebugExpression2::EvaluateAsync methods to evaluate the parsed expression.
|
|
int IDebugStackFrame2.GetExpressionContext(out IDebugExpressionContext2 ppExprCxt) {
|
|
ppExprCxt = (IDebugExpressionContext2)this;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
// Gets a description of the stack frame.
|
|
int IDebugStackFrame2.GetInfo(enum_FRAMEINFO_FLAGS dwFieldSpec, uint nRadix, FRAMEINFO[] pFrameInfo) {
|
|
try {
|
|
SetFrameInfo((enum_FRAMEINFO_FLAGS)dwFieldSpec, out pFrameInfo[0]);
|
|
|
|
return VSConstants.S_OK;
|
|
}
|
|
//catch (ComponentException e)
|
|
//{
|
|
// return e.HResult;
|
|
//}
|
|
catch (Exception e) {
|
|
return EngineUtils.UnexpectedException(e);
|
|
}
|
|
}
|
|
|
|
// Gets the language associated with this stack frame.
|
|
// In this sample, all the supported stack frames are C++
|
|
int IDebugStackFrame2.GetLanguageInfo(ref string pbstrLanguage, ref Guid pguidLanguage) {
|
|
pbstrLanguage = "CSharp";
|
|
pguidLanguage = AD7Guids.guidLanguageCSharp;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
// 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 = mFunctionName;
|
|
return VSConstants.S_OK;
|
|
}
|
|
//catch (ComponentException e)
|
|
//{
|
|
// return e.HResult;
|
|
//}
|
|
catch (Exception e) {
|
|
return EngineUtils.UnexpectedException(e);
|
|
}
|
|
}
|
|
|
|
// Gets a machine-dependent representation of the range of physical addresses associated with a stack frame.
|
|
int IDebugStackFrame2.GetPhysicalStackRange(out ulong addrMin, out ulong addrMax) {
|
|
addrMin = 0;// m_threadContext.ebp;
|
|
addrMax = 0;// m_threadContext.ebp;
|
|
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
// Gets the thread associated with a stack frame.
|
|
int IDebugStackFrame2.GetThread(out IDebugThread2 thread) {
|
|
thread = mThread;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Retrieves the name of the evaluation context.
|
|
// The name is the description of this evaluation context. It is typically something that can be parsed by an expression evaluator
|
|
// that refers to this exact evaluation context. For example, in C++ the name is as follows:
|
|
// "{ function-name, source-file-name, module-file-name }"
|
|
int IDebugExpressionContext2.GetName(out string pbstrName) {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
// 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) {
|
|
//System.Windows.Forms.MessageBox.Show("pszCode: " + pszCode);
|
|
pbstrError = "";
|
|
pichError = 0;
|
|
ppExpr = null;
|
|
|
|
try {
|
|
if (mParams != null) {
|
|
foreach (DebugLocalInfo currVariable in mParams) {
|
|
if (String.CompareOrdinal(currVariable.Name, pszCode) == 0) {
|
|
ppExpr = new AD7Expression(currVariable, mProcess, this);
|
|
return VSConstants.S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mLocals != null) {
|
|
foreach (DebugLocalInfo currVariable in mLocals) {
|
|
if (String.CompareOrdinal(currVariable.Name, pszCode) == 0) {
|
|
ppExpr = new AD7Expression(currVariable, mProcess, this);
|
|
return VSConstants.S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
pbstrError = "Invalid Expression";
|
|
pichError = (uint)pbstrError.Length;
|
|
return VSConstants.S_FALSE;
|
|
} catch (Exception e) {
|
|
return EngineUtils.UnexpectedException(e);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|