// @NOTE: Currently ignoring FName::Number, and assuming valid. public static string GetFNameAsString(string expr_str, DkmVisualizedExpression context_expr) { /* var em = ExpressionManipulator.FromExpression(expr_str); * em = em.DirectMember(Memb.CompIndex); * DkmSuccessEvaluationResult comp_idx_eval = DefaultEE.DefaultEval(em.Expression, context_expr, true) as DkmSuccessEvaluationResult; * * // @TODO: For now, to avoid requiring more lookups, we'll allow a failure and return null, * // and let the caller decide how to interpret it. * //Debug.Assert(comp_idx_eval != null); * if(comp_idx_eval == null) * { * return null; * } * * string comp_idx_str = comp_idx_eval.Value; * // "((FNameEntry&)GNameBlocksDebug[Value >> FNameDebugVisualizer::OffsetBits][FNameDebugVisualizer::EntryStride * (Value & FNameDebugVisualizer::OffsetMask)])" * // // @TODO: Condition = "Header.Len > FNameDebugVisualizer::MaxLength" -> Illegal * // "Header.bIsWide" -> WideName,[Header.Len]su * // AnsiName,[Header.Len]s * * string ansi_expr_str = String.Format("((FNameEntry*)(((FNameEntry***)GFNameTableForDebuggerVisualizers_MT)[{0} / 16384][{0} % 16384]))->AnsiName", comp_idx_str); * var ansi_eval = DefaultEE.DefaultEval(ansi_expr_str, context_expr, true); * return ansi_eval.GetUnderlyingString(); */ var default_eval = DefaultEE.DefaultEval(expr_str, context_expr, false /*true*/) as DkmSuccessEvaluationResult; if (default_eval == null) { return(null); } // Remove quotes return(default_eval.Value.Substring(1, default_eval.Value.Length - 2)); }
// Given an expression string resolving to a UClass* (which is assumed to point to a **Blueprint** class), this determines // the UE4 (non-prefixed) name of the native base class. public static string GetBlueprintClassNativeBaseName(string uclass_expr_str, DkmVisualizedExpression context_expr) { var uclass_em = ExpressionManipulator.FromExpression(uclass_expr_str); do { var super_em = uclass_em.PtrCast(Typ.UStruct).PtrMember(Memb.SuperStruct).PtrCast(Typ.UClass); var is_native_res = IsNativeUClassOrUInterface(super_em.Expression, context_expr); if (!is_native_res.IsValid) { return(null); } if (is_native_res.Value) { var name_em = super_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName); return(GetFNameAsString(name_em.Expression, context_expr)); } uclass_em = super_em; }while (DefaultEE.DefaultEval(uclass_em.PtrCast(Cpp.Void).Expression, context_expr, true).TagValue == DkmEvaluationResult.Tag.SuccessResult); // This shouldn't really happen return(null); }
public override void PrepareExpansion(out DkmEvaluationResultEnumContext enumContext) { // First, default expansion // @TODO: Calling this both in EvaluateExpressionResult, and from UseDefaultEvaluationBehavior // in the component. Be good if we can somehow test if this is actually redoing the evaluation, // or just returning the existing one. var eval = DefaultEE.DefaultEval(expression_, true); DkmEvaluationResult[] children; DkmEvaluationResultEnumContext default_enum_ctx; expression_.GetChildrenCallback(eval, 0, expression_.InspectionContext, out children, out default_enum_ctx); // Now any custom additions to the expansion int custom_children = 0; // Need to determine if we should display the property list child DeterminePropertyListVisibility(); if (IsPropertyListShown) { custom_children++; } // Create an aggregate enumeration context, with the length of the default enum, plus what we are adding on. // Store the default enumeration context inside it as a data item. enumContext = DkmEvaluationResultEnumContext.Create( default_enum_ctx.Count + custom_children, expression_.StackFrame, expression_.InspectionContext, new DefaultEnumContextDataItem(default_enum_ctx) ); }
protected static string GetBoolPropertyFieldMask(string uboolprop_expr_str, DkmVisualizedExpression context_expr) { var boolprop_em = ExpressionManipulator.FromExpression(uboolprop_expr_str).PtrMember(Memb.FieldMask); // uint8 property var eval = DefaultEE.DefaultEval(boolprop_em.Expression, context_expr, true) as DkmSuccessEvaluationResult; var val_str = eval.Value; return(Utility.GetNumberFromUcharValueString(val_str)); }
// @TODO: Unsafe, assumes valid and non-empty. public static string GetFStringAsString(string expr_str, DkmVisualizedExpression context_expr) { var em = ExpressionManipulator.FromExpression(expr_str); em = em.DirectMember("Data").DirectMember("AllocatorInstance").DirectMember("Data").PtrCast("wchar_t"); var data_eval = DefaultEE.DefaultEval(em.Expression, context_expr, true); Debug.Assert(data_eval != null); return(data_eval.GetUnderlyingString()); }
// Given an expression which resolves to a pointer of any kind, returns the pointer value as an integer. public static ulong EvaluatePointerAddress(string pointer_expr_str, DkmVisualizedExpression context_expr) { ExpressionManipulator em = ExpressionManipulator.FromExpression(pointer_expr_str); // Cast to void* to avoid unnecessary visualization processing. string address_expr_str = em.PtrCast(Cpp.Void).Expression; var address_eval = (DkmEvaluationResult)DefaultEE.DefaultEval(address_expr_str, context_expr, true); if (address_eval.TagValue == DkmEvaluationResult.Tag.SuccessResult) { var eval = address_eval as DkmSuccessEvaluationResult; return(eval.Address != null ? eval.Address.Value : 0); } return(0); }
void IDkmCustomVisualizer.UseDefaultEvaluationBehavior(DkmVisualizedExpression expression, out bool useDefaultEvaluationBehavior, out DkmEvaluationResult defaultEvaluationResult) { Debug.Print("UE4PV: UseDefaultEvaluationBehavior('{0}'/'{1}', [{2}, {3}])", Utility.GetExpressionFullName(expression), Utility.GetExpressionName(expression), expression.TagValue, expression.VisualizerId ); if (KUE4VS.ExtContext.Instance.ExtensionOptions.EnablePropVis) { var data_item = expression.GetDataItem <ExpressionDataItem>(); if (data_item != null) { Debug.Assert(data_item.Visualizer != null); if (data_item.Visualizer.WantsCustomExpansion) { useDefaultEvaluationBehavior = false; defaultEvaluationResult = null; return; } } } // Don't need any special expansion, just delegate back to the default EE useDefaultEvaluationBehavior = true; /* @NOTE: * Not sure where exactly the problem is, but UObject properties don't expand in VS 2013. * When we try, there is an initial call to this method with the property's child expr, * so we come here, and do a default eval, which, if the prop is a UObject, will invoke * EvaluateVisualizedExpression above with a new root expr. In 2015, that is followed by * another call to this method for the root expr, which has an attached visualizer and we * do the custom expansion. In 2013, it seems the second call into this method does not * occur for some reason. */ defaultEvaluationResult = DefaultEE.DefaultEval(expression, false); /* * Doing this will crash VS! * DkmSuccessEvaluationResult.Create(null, null, "", "", DkmEvaluationResultFlags.None, * "", "", "", DkmEvaluationResultCategory.Other, DkmEvaluationResultAccessType.None, * DkmEvaluationResultStorageType.None, DkmEvaluationResultTypeModifierFlags.None, * null, null, null, null); */ }
// @TODO: Differentiate between false result and evaluation failure public static bool EvaluateBooleanExpression(string bool_expr_str, DkmVisualizedExpression context_expr) { try { var eval = DefaultEE.DefaultEval(bool_expr_str, context_expr, true); if (eval.TagValue == DkmEvaluationResult.Tag.SuccessResult) { var result = eval as DkmSuccessEvaluationResult; return(bool.Parse(result.Value)); } else { return(false); } } catch (Exception e) { return(false); } }
// @NOTE: Currently ignoring FName::Number, and assuming valid. public static string GetFNameAsString(string expr_str, DkmVisualizedExpression context_expr) { var em = ExpressionManipulator.FromExpression(expr_str); em = em.DirectMember(Memb.CompIndex); DkmSuccessEvaluationResult comp_idx_eval = DefaultEE.DefaultEval(em.Expression, context_expr, true) as DkmSuccessEvaluationResult; // @TODO: For now, to avoid requiring more lookups, we'll allow a failure and return null, // and let the caller decide how to interpret it. //Debug.Assert(comp_idx_eval != null); if (comp_idx_eval == null) { return(null); } string comp_idx_str = comp_idx_eval.Value; string ansi_expr_str = String.Format("((FNameEntry*)(((FNameEntry***)GFNameTableForDebuggerVisualizers_MT)[{0} / 16384][{0} % 16384]))->AnsiName", comp_idx_str); var ansi_eval = DefaultEE.DefaultEval(ansi_expr_str, context_expr, true); return(ansi_eval.GetUnderlyingString()); }
// Determines the corresponding C++ name for the passed in UClass unprefixed name. public static string DetermineNativeUClassCppTypeName(string uclass_fname, DkmVisualizedExpression context_expr) { // Now we need to prefix the name, with either 'A' for actor-derived classes, or 'U' for all others. string cpp_prefix = "U"; // This is kind of awkward. Cleanest approach would be to walk up the super-class chain, but that could be // a LOT of expression evaluations. // The following approach is hacky but should be quicker. Relying on the assumption that UE4 wouldn't allow // both a UObject class and an AActor class with the same name (@TODO: is this true??), we just assume the 'U' // prefix, form an arbitrary expression using it, and see if it can be successfully evaluated by the debugger. // If not, we assume the prefix is 'A' instead. // @NOTE: This will fail if someone is stupid enough to create a reflected class 'AMyClass' as well // as a non-reflected class 'UMyClass'. // First do quick checks for common types, to avoid an unnecessary expression evaluation. if (uclass_fname == "Actor") { cpp_prefix = "A"; } else if (uclass_fname != "Object") { // Assume 'U'. // Also, after casting to our assumed type, cast again to void*. This shouldn't affect the success/failure result // of the evaluation, but will avoid unnecessary visualization callbacks when the evaluation succeeds. string assumed_cpp_type = cpp_prefix + uclass_fname; string cast_expr_str = String.Format("(void*)({0}*)nullptr", assumed_cpp_type); var cast_eval = DefaultEE.DefaultEval(cast_expr_str, context_expr, true); if (cast_eval.TagValue != DkmEvaluationResult.Tag.SuccessResult) { // Cast failed, assume therefore must be an actor type cpp_prefix = "A"; } } return(cpp_prefix + uclass_fname); }
void IDkmCustomVisualizer.UseDefaultEvaluationBehavior(DkmVisualizedExpression expression, out bool useDefaultEvaluationBehavior, out DkmEvaluationResult defaultEvaluationResult) { Debug.Print("UE4PV: UseDefaultEvaluationBehavior('{0}'/'{1}', [{2}, {3}])", Utility.GetExpressionFullName(expression), Utility.GetExpressionName(expression), expression.TagValue, expression.VisualizerId ); var data_item = expression.GetDataItem <ExpressionDataItem>(); if (data_item != null) { Debug.Assert(data_item.Visualizer != null); if (data_item.Visualizer.WantsCustomExpansion) { useDefaultEvaluationBehavior = false; defaultEvaluationResult = null; return; } } // Don't need any special expansion, just delegate back to the default EE useDefaultEvaluationBehavior = true; /* @NOTE: * Not sure where exactly the problem is, but UObject properties don't expand in VS 2013. * When we try, there is an initial call to this method with the property's child expr, * so we come here, and do a default eval, which, if the prop is a UObject, will invoke * EvaluateVisualizedExpression above with a new root expr. In 2014, that is followed by * another call to this method for the root expr, which has an attached visualizer and we * do the custom expansion. In 2013, it seems the second call into this method does not * occur for some reason. */ defaultEvaluationResult = DefaultEE.DefaultEval(expression, false); }
protected DkmEvaluationResult GeneratePropertyValueEval(string container_expr_str, string uprop_expr_str, uint index, DkmVisualizedExpression context_expr) { Debug.Print("UE4PV: Trying to generate property value for property #{0}", index + 1); var uprop_em = ExpressionManipulator.FromExpression(uprop_expr_str); // Get name of property string prop_name_expr_str = uprop_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression; string prop_name = UE4Utility.GetFNameAsString(prop_name_expr_str, context_expr); // And the property type string prop_type = GetPropertyType(uprop_em.Expression, context_expr); // Now, determine address of the actual property value // First requires container address // Cast to void* first, so that expression evaluation is simplified and faster container_expr_str = ExpressionManipulator.FromExpression(container_expr_str).PtrCast(Cpp.Void).Expression; var container_eval = DefaultEE.DefaultEval(container_expr_str, context_expr, true) as DkmSuccessEvaluationResult; Debug.Assert(container_eval != null && container_eval.Address != null); string address_str = container_eval.Address.Value.ToString(); // Next need offset in container (which is an int32 property of the UProperty class) var offset_expr_str = uprop_em.PtrMember(Memb.PropOffset).Expression; var offset_eval = DefaultEE.DefaultEval(offset_expr_str, context_expr, true) as DkmSuccessEvaluationResult; string offset_str = offset_eval.Value; // Then need to create an expression which adds on the offset address_str = String.Format("((uint8*){0} + {1})", address_str, offset_str); // Next, we need to cast this expression depending on the type of property we have. // Retrieve a list of possible cast expressions. var cpp_type_info_list = GetCppTypeForPropertyType(prop_type, uprop_em.Expression, context_expr); // Accept the first one that is successfully evaluated DkmSuccessEvaluationResult success_eval = null; string display_type = null; foreach (var cpp_type_info in cpp_type_info_list) { string prop_value_expr_str = AdjustPropertyExpressionStringForType(address_str, prop_type, uprop_em.Expression, context_expr, cpp_type_info); Debug.Print("UE4PV: Attempting exp eval as: '{0}'", prop_value_expr_str); // Attempt to evaluate the expression DkmEvaluationResult raw_eval = DefaultEE.DefaultEval(prop_value_expr_str, context_expr, false); if (raw_eval.TagValue == DkmEvaluationResult.Tag.SuccessResult) { // Success success_eval = raw_eval as DkmSuccessEvaluationResult; display_type = cpp_type_info.Display; break; } } if (success_eval == null) { // Was not able to find an evaluatable expression. return(null); } return(DkmSuccessEvaluationResult.Create( expression_.InspectionContext, expression_.StackFrame, prop_name, success_eval.FullName, //prop_value_expr_str, success_eval.Flags, success_eval.Value, // @TODO: Perhaps need to disable for some stuff, like bitfield bool? success_eval.EditableValue, display_type, //success_eval.Type, success_eval.Category, success_eval.Access, success_eval.StorageType, success_eval.TypeModifierFlags, success_eval.Address, success_eval.CustomUIVisualizers, success_eval.ExternalModules, null )); }
public void EvaluateProperties() { List <DkmEvaluationResult> evals = new List <DkmEvaluationResult>(); // Assume we've been constructed with the fabricated property list expression DkmChildVisualizedExpression proplist_expr = (DkmChildVisualizedExpression)expression_; Debug.Assert(proplist_expr != null); // start could be an expression with the type of any UObject-derived class DkmVisualizedExpression start_expr = proplist_expr.Parent; string base_expression_str = Utility.GetExpressionFullName(start_expr); base_expression_str = Utility.StripExpressionFormatting(base_expression_str); ExpressionManipulator obj_em = null; // @TODO: Deal with non-pointer start expression obj_em = ExpressionManipulator.FromExpression(base_expression_str); // Determine if our base expression is <UObject-type>* or <UObject-type> bool is_pointer = start_expr.GetDataItem <UObjectDataItem>().IsPointer; if (!is_pointer) { obj_em = obj_em.AddressOf(); } var uclass_em = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjClass); if (Config.PropertyDisplayPolicy == Config.PropDisplayPolicyType.BlueprintOnly) { // See if the actual class of the object instance whose properties we want to enumerate // is native or not. var is_native_res = UE4Utility.TestUClassFlags( uclass_em.Expression, ClassFlags.Native, start_expr ); // If the instance class is native, then it can't possibly have any non-native properties, // so bail out now. // @TODO: Even if the class is not native, we should still be able to avoid doing all the work // for enumerating every native property in order to find the non-native ones... // @TODO: How best to deal with failed is_native evaluation? if (is_native_res.IsValid && is_native_res.Value) { return; } } // Get the UStruct part of the UClass, in order to begin iterating properties var ustruct_em = uclass_em.PtrCast(Typ.UStruct); // Now access PropertyLink member, which is the start of the linked list of properties var prop_em = ustruct_em.PtrMember(Memb.FirstProperty); uint idx = 0; while (true) { Debug.Print("UE4PV: Invoking raw eval on UProperty* expression"); var prop_eval = DefaultEE.DefaultEval(prop_em.Expression, start_expr, true) as DkmSuccessEvaluationResult; Debug.Assert(prop_eval != null); if (prop_eval.Address.Value == 0) { // nullptr, end of property list break; } bool should_skip = false; if (!should_skip && Config.PropertyDisplayPolicy == Config.PropDisplayPolicyType.BlueprintOnly) { // Check to see if this property is native or blueprint // We can test this by getting the UProperty's Outer, and checking its object flags for RF_Native. var prop_outer_em = prop_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjOuter); // @NOTE: RF_Native has gone, and checking for RF_MarkAsNative never seems to return true... //var is_native_res = UE4Utility.TestUObjectFlags(prop_outer_em.Expression, ObjFlags.Native, start_expr); // So, access class flags instead. // Note that we make the assumption here that the property's outer is a UClass, which should be safe since // we're starting out with a uobject, so all properties, including inherited ones, should be outered to a uclass. var prop_outer_uclass_em = prop_outer_em.PtrCast(Typ.UClass); var is_native_res = UE4Utility.TestUClassFlags(prop_outer_uclass_em.Expression, ClassFlags.Native, start_expr); // According to UE4 UStruct API docs, property linked list is ordered from most-derived // to base. If so, we should be able to bail out here knowing that having hit a native property, // all following properties must be native too. if (is_native_res.IsValid && is_native_res.Value) { return; } } if (!should_skip) { // @TODO: Here we use the starting expression for the container. // May not work if the root expression was not of pointer type!! var prop_val_eval = GeneratePropertyValueEval( obj_em.Expression, prop_em.Expression, idx, start_expr ); if (prop_val_eval != null && !Config.IsPropertyHidden(prop_val_eval.Name)) { prop_evals_[prop_val_eval.Name] = prop_val_eval; ++idx; } } // Advance to next link prop_em = prop_em.PtrMember(Memb.NextProperty); } }
protected void EvaluateExpressionResult() { // We're going to customize the unexpanded display string, as well as the expanded // view (if requested later). // @TODO: Really don't understand why, but when we invoke the default eval below, and we get // reentrant calls for child member UObjects, they are coming back as root expressions // with an empty FullName. This subsequently fails to evaluate if we pass it through to // default eval again. Perhaps this is somehow related to breaking the potential infinite // recursion of visualizing children in order to visualize the parent, but don't follow how it // it supposed to be dealt with. if (expression_.TagValue == DkmVisualizedExpression.Tag.RootVisualizedExpression) { var as_root = expression_ as DkmRootVisualizedExpression; if (as_root.FullName.Length == 0) { string display_str = "{...}"; eval_ = DkmSuccessEvaluationResult.Create( expression_.InspectionContext, expression_.StackFrame, as_root.Name, as_root.Name, DkmEvaluationResultFlags.ReadOnly, display_str, "", #if !VS2013 as_root.Type, #else "Unknown", #endif DkmEvaluationResultCategory.Other, DkmEvaluationResultAccessType.None, DkmEvaluationResultStorageType.None, DkmEvaluationResultTypeModifierFlags.None, null, null, null, null ); state_ = EvaluationState.MinimalEvaluation; return; } } // string custom_display_str = Resources.UE4PropVis.IDS_DISP_CONDENSED; DkmSuccessEvaluationResult proto_eval = null; bool is_pointer; bool is_null; string address_str = ""; // @NOTE: Initially here we were executing the full default evaluation of our expression. // Problem is that this will call back into us for all UObject children of the expression, // because it needs to generate a visualization for them in order to construct its {member vals...} display string. // And then, we just ignore that anyway and display our own... // The following attempts to avoid that by casting our expression to void* and then evaluating that. // If it fails, we assume we are non-pointer. If it succeeds, we can determine from the result whether we are null or not. // @WARNING: This may not be so safe, since we can't determine whether evaluation failed because we tried to cast a non-pointer // to void*, or because the passed in expression was not valid in the first place. Believe we should be okay, since we should // only be receiving expressions that have already been determined to be suitable for our custom visualizer. // Ideally though, we'd be able to get a raw expression evaluation, without any visualization taking place. // Seems there must be a way to do this, but looks like it requires using a different API... const bool UseVoidCastOptimization = true; string default_expr_str = Utility.GetExpressionFullName(expression_); if (UseVoidCastOptimization) { default_expr_str = ExpressionManipulator.FromExpression(default_expr_str).PtrCast(Cpp.Void).Expression; } DkmEvaluationResult eval = DefaultEE.DefaultEval(default_expr_str, expression_, true); if (!UseVoidCastOptimization) { if (eval.TagValue != DkmEvaluationResult.Tag.SuccessResult) { // Genuine failure to evaluate the passed in expression eval_ = eval; state_ = EvaluationState.Failed; return; } else { proto_eval = (DkmSuccessEvaluationResult)eval; custom_display_str = proto_eval.Value; is_pointer = IsPointer(proto_eval); is_null = is_pointer && IsPointerNull(proto_eval); // @TODO: need to extract address string } } else { DkmDataAddress address = null; if (eval.TagValue != DkmEvaluationResult.Tag.SuccessResult) { // Assume the failure just implies the expression was non-pointer (thereby assuming that it was itself valid!) // @TODO: Could actually fall back onto standard evaluation here, in order to determine for sure // that the original expression is valid. Failure wouldn't be common since most of the time we are // dealing with pointer expressions, so any potential optimization should still be gained. is_pointer = false; is_null = false; } else { var success = (DkmSuccessEvaluationResult)eval; Debug.Assert(IsPointer(success)); is_pointer = true; is_null = is_pointer && IsPointerNull(success); address = success.Address; address_str = success.Value; } proto_eval = DkmSuccessEvaluationResult.Create( expression_.InspectionContext, expression_.StackFrame, "", "", DkmEvaluationResultFlags.ReadOnly | DkmEvaluationResultFlags.Expandable, "", "", #if !VS2013 Utility.GetExpressionType(expression_), #else is_pointer ? "UObject *" : "UObject", #endif DkmEvaluationResultCategory.Other, DkmEvaluationResultAccessType.None, DkmEvaluationResultStorageType.None, DkmEvaluationResultTypeModifierFlags.None, address, null, null, null ); } string obj_expression_str = Utility.GetExpressionFullName(expression_); var obj_em = ExpressionManipulator.FromExpression(obj_expression_str); // Store pointer flags on the expression expression_.SetDataItem( DkmDataCreationDisposition.CreateAlways, new UObjectDataItem(is_pointer, is_null) ); if (!is_pointer) { // All subsequent manipulations of the expression assume it starts as a pointer to a // UObject-derived class, so if our expression is to a dereferenced UObject, just // prefix an 'address of' to the expression string. obj_em = obj_em.AddressOf(); } // Generate the condensed display string. if (is_null) { if (Config.CustomNullObjectPreview) { custom_display_str = "<NULL> UObject"; } else { var null_eval = DefaultEE.DefaultEval("(void*)0", expression_, true) as DkmSuccessEvaluationResult; custom_display_str = null_eval.Value + " <NULL>"; } } else if (Config.DisplayUObjectPreview) { // Prefix the address, if this is a pointer expression string address_prefix_str = ""; if (is_pointer) { address_prefix_str = address_str + " "; } // Specialized display for UClass? bool uclass_specialized = false; if (Config.DisplaySpecializedUClassPreview) { var uclass_em = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjClass); var _uclass_fname_expr_str = uclass_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression; string _obj_uclass_name_str = UE4Utility.GetFNameAsString(_uclass_fname_expr_str, expression_); // @TODO: To simplify and for performance reasons, just hard coding known UClass variants if (_obj_uclass_name_str == "Class" || _obj_uclass_name_str == "BlueprintGeneratedClass" || _obj_uclass_name_str == "WidgetBlueprintGeneratedClass") { var fname_expr_str = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression; string obj_name_str = UE4Utility.GetFNameAsString(fname_expr_str, expression_); var parent_uclass_fname_expr_str = obj_em.PtrCast(Typ.UStruct).PtrMember(Memb.SuperStruct).PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression; // This will return null if lookup failed for any reason. // We'll assume this meant no super class exists (ie. we're dealing with UClass itself) string parent_uclass_name_str = UE4Utility.GetFNameAsString(parent_uclass_fname_expr_str, expression_); if (parent_uclass_name_str == null) { parent_uclass_name_str = "None"; } custom_display_str = String.Format( "{0}{{ClassName='{1}', Parent='{2}'}}", address_prefix_str, obj_name_str, parent_uclass_name_str ); uclass_specialized = true; } } if (!uclass_specialized) { // For standard UObject condensed display string, show the object FName and its UClass. // @TODO: The evaluations required for this may be a performance issue, since they'll be done for all UObject children of any default visualized // aggregate type, even when it is not expanded (the default behaviour is to create a {...} display list of child member visualizations). var fname_expr_str = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression; string obj_name_str = UE4Utility.GetFNameAsString(fname_expr_str, expression_); var uclass_fname_expr_str = obj_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjClass).PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression; string obj_uclass_name_str = UE4Utility.GetFNameAsString(uclass_fname_expr_str, expression_); custom_display_str = String.Format( "{0}{{Name='{1}', Class='{2}'}}", address_prefix_str, obj_name_str, obj_uclass_name_str ); } } eval_ = DkmSuccessEvaluationResult.Create( proto_eval.InspectionContext, proto_eval.StackFrame, // Override the eval name with the original expression name, since it will // have inherited the ",!" suffix. Utility.GetExpressionName(expression_), Utility.GetExpressionFullName(expression_), proto_eval.Flags, custom_display_str, //success_eval.Value, proto_eval.EditableValue, proto_eval.Type, proto_eval.Category, proto_eval.Access, proto_eval.StorageType, proto_eval.TypeModifierFlags, proto_eval.Address, proto_eval.CustomUIVisualizers, proto_eval.ExternalModules, null ); state_ = EvaluationState.Evaluated; }