How to avoid "Access is denied" error while accessing Process properties? - c#

I have a helper method in C# that tries to get object properties (like turning them into Dictionary object with key-value pair). It is similar to:
public static Dictionary<string, object?> GetPropertyValues(object obj, int currentLevel = 0, int maxLevel = 3)
{
Dictionary<string, object?> propertyValues = new Dictionary<string, object?>();
if (currentLevel >= maxLevel)
{
propertyValues.Add("currentLevel", currentLevel);
propertyValues.Add("maxLevel", maxLevel);
return propertyValues;
}
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
{
string name = descriptor.Name;
object? value = descriptor.GetValue(obj);
if (value != null)
{
if (!value.GetType().IsPrimitive)
{
value = GetPropertyValues(value, ++currentLevel, maxLevel);
}
}
propertyValues.Add(name, value);
}
return propertyValues;
}
but if I send and instance of a Process class then following line throws exception:
object? value = descriptor.GetValue(obj);
Property accessor 'SafeHandle' on object 'System.Diagnostics.Process'
threw the following exception:'Access is denied.'
I can also see Visual Studio debugger shows the same error;
Is there any way to avoid this error or skip properties if they will throw exception?
While I can cover the line with try/catch I am trying to find a native way to run execution without exception driven way.

Related

Code works in Watch Window, but unable to cast object during code execution

I have these lines of code in my .NET 4.7.2 server application:
object saveObject = proxyDef.GetEntityAsNativeObject(entity, DynamicProxyAssembly); // this works
((AxdSalesOrder)saveObject).SalesTable[0].TableDlvAddr = null; // this throws error
I can execute the null-set (2nd line above) in the VS2019 Watch Window and it works perfectly and achieves the desired effect. But when I allow it to run in normal execution (whether in debug mode or not), I get an unhandled exception on that 2nd line:
Unable to cast object of type 'AxdSalesOrder' to type
'Elogix.MSAx.Core.Ax2012ElogixServices.AxdSalesOrder'
There is dynamic stuff going in in relation to that type:
public override object GetEntityAsNativeObject(MSAxEntity entity, Assembly dynamicProxyAssembly) {
var salesOrderObject = Activator.CreateInstance(dynamicProxyAssembly.GetType("AxdSalesOrder"));
var salesOrderTable = DynamicEntityUtil.CreateObjectFromDynamicEntity(entity, dynamicProxyAssembly, "AxdEntity_SalesTable");
Array tableLines =
Array.CreateInstance(
salesOrderObject.GetType().GetProperty("SalesTable").PropertyType.GetElementType(), 1);
tableLines.SetValue(salesOrderTable, 0);
salesOrderObject.SetPropertyValue("SalesTable", tableLines);
return salesOrderObject;
}
public static object CreateObjectFromDynamicEntity(DynamicEntity entity, Assembly dynamicProxyAssembly, string objectTypeName) {
return CreateObjectFromDynamicEntity(entity, dynamicProxyAssembly.GetType(objectTypeName));
}
public static object CreateObjectFromDynamicEntity(DynamicEntity entity, Type type) {
if (type == null) {
throw new ArgumentException("Cannot create object from dynamic entity because \"Type\" is null.");
}
if (type.IsArray) {
return CreateArrayFromDynamicEntity(entity, type);
}
return CreateClassFromDynamicEntity(entity, type);
}
private static object CreateClassFromDynamicEntity(DynamicEntity entity, Type type) {
var nativeObject = Activator.CreateInstance(type);
// this will recursively convert the dynamic values to the native type values on the object.
updateValuesFromDynamicValues(entity);
var modifiedProperties = from property in entity.Properties
//where property.State != DynamicPropertyState.Unchanged
select property;
foreach(var property in modifiedProperties) {
Type valueUnderlyingType = Nullable.GetUnderlyingType(property.Type);
if (valueUnderlyingType != null && valueUnderlyingType.IsEnum) {
PropertyInfo info = nativeObject.GetType().GetProperty(property.Name);
Type targetUnderlyingType = Nullable.GetUnderlyingType(info.PropertyType);
if (property.Value == null) {
info.SetValue(nativeObject, null, null);
} else {
object correctedValue = property.Value.CorrectedEnumValue(targetUnderlyingType);
info.SetValue(nativeObject, correctedValue, null);
}
} else if (property.Type.IsEnum) {
if (property.Value == null) {
continue;
}
object correctedValue = property.Value.CorrectedEnumValue(property.Type);
nativeObject.SetPropertyValue(property.Name, correctedValue);
} else {
try {
nativeObject.SetPropertyValue(property.Name, property.Value);
} catch (Exception ex) {
Log.Write(ex.Message);
}
}
}
return nativeObject;
}
Here is how it looks in the VS2019 Watch Window:
Did this in Immediate Window:
var t = saveObject.GetType();
t.FullName
"AxdSalesOrder"
As you can see, the type's FullName is not very full, not qualified by anything, due to the dynamic nature of the type.
I can try it this way:
(saveObject as AxdSalesOrder).SalesTable[0].TableDlvAddr = null;
Again, that works in Watch, but throws this exception when run in normal execution:
Object reference not set to an instance of an object.
Clearly, VS/Watch knows the type, which is why it can execute without errors inside Watch. But the .net runtime apparently doesn't know the type at run time. How can I get this object cast to work in normal code execution like it does when run in Watch?
The answer here is to take advantage of dynamic typing since the static type is not available here:
dynamic saveObject = proxyDef.GetEntityAsNativeObject(entity, DynamicProxyAssembly);
saveObject.SalesTable[0].TableDlvAddr = null;
Another possible approach would be to use reflection, however since the expression applied to the object involves two properties and an index lookup (.SalesTable[0].TableDlvAddr) this would look much less readable.
The GetEntityAsNativeObject could also benefit from this style, you could consider rewriting it to using dynamic binding rather than reflection.

Accessing element in Array, Dictionary or List using Reflection

Ive created a tool for my team which allows them to view an object visually by using the Dump() method of LinqPad. This is purely for use by the development teams to make things easier rather than having to attach a debugger which isnt always possible.
They enter the name of the object they want to Dump and the program does it in its current context.
eg. User enters Sales.CurrentUser and the application visualises the CurrentUser object using LinqPad's Dump() method.
public void VisualiseObject()
{
object CurrentObject = this;
string Result = GetObjectFromUser("Enter Propery or Field path eg. Sales.CurrentUser", "");
if (Result == null)
return;
else if (Result != string.Empty)
{
string[] Objects = Result.Split(".".ToCharArray());
try
{
foreach (string objName in Objects)
{
CurrentObject = GetPropValue(CurrentObject, objName);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
if (CurrentObject == null)
{
MessageBox.Show("Object is null");
return;
}
}
CurrentObject.Dump(Result);
}
public static object GetPropValue(object src, string propName)
{
object result = null;
try
{
result = src.GetType().GetProperty(propName).GetValue(src, null);
}
catch
{
try
{
result = src.GetType().GetField(propName).GetValue(src);
}
catch (Exception)
{
result = null;
}
}
return result;
}
Anyway im trying to think of a way to support the user supplying an index or key to a array, list or dictionary object and handle this in reflection.
The user could supply for example. An array or list Sales.AllUsers[0] or for dictionary Sales.Items["Water"]
Any ideas?
You can try the Dynamic Linq Expressions, it allows you to do parse expressions (just be carefull with code injection).
Or you can try accessing the Get method of the array:
(given that prop is a variable of type int[])
var getter = array.GetType().GetMethod("Get");
var value = getter.Invoke(array, new object[] { index });
Haven't tested with Dictionary or Collection but should also work in the same way.

Avoid changing the state of a variable when making use of 'Session' in C#

I want to save the result of a calculation (assigned to variable 'ans') for a second request. To do so, I use Session.
Even though it worked in the first request, when I want to call the variable in a second request (else-Statement) the following error occurs :"Cannot apply indexing with [] to an expression of type 'object'"
Does the state of the variable change through saving it to /accessing it from Session?
The format of 'ans' and 'ansTemp' for the second request is both times tuple of 5 items.
private Dictionary<string,int> DoCalculation(CalculatorModel model)
{
var ansTemp = Session["ANS"];
Dictionary<string, int> calculatedValues = new Dictionary<string, int>();
if (ansTemp == null)
{
if (model != null)
{
Class1 clsOne = new Class1(pathToLib, pathToPyFile);
try
{
var ans = clsOne.CallFunction("first_chart", rootPythonDir, model.age);
//Session.Add("ANS", ans);
Session["ANS"] = ans;
var assetAllocationCategory = ans[1];
}
catch (Exception ex) { }
}
}
else
{
var ans = ansTemp;
var assetAllocationCategory = ans[1];
}
}
Defining ansTemp as a dynamic instead of var when accessing the Session["ANS"] solved the problem.

Index outside of bound exception

I have the following code, and somehow yesterday evening it had thrown a lot of exceptions:
Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
I just don't see how this is possible, I check for null and if the key is available. This is the only method where lastTimeoutCheck is used.
private static Dictionary<string, DateTime> lastTimeoutCheck;
private static readonly object CacheLock = new object();
private static void CheckTimeout(string groupName)
{
if (lastTimeoutCheck == null)
{
lastTimeoutCheck = new Dictionary<string, DateTime>();
return;
}
if (!lastTimeoutCheck.ContainsKey(groupName))
{
lastTimeoutCheck.Add(groupName, DateTime.UtcNow);
return;
}
if (lastTimeoutCheck[groupName] <
DateTime.UtcNow.AddMinutes(-GroupConfigSection.TimeOutCheckMinutes))
{
lock (CheckLock)
{
if (lastTimeoutCheck[groupName] <
DateTime.UtcNow.AddMinutes(-GroupConfigSection.TimeOutCheckMinutes))
{
GroupHolder groupHolder =
(GroupHolder) System.Web.HttpContext.Current.Cache.Get(groupName);
if (groupHolder != null)
{
groupHolder.UpdateTime();
}
lastTimeoutCheck[groupName] = DateTime.UtcNow;
}
}
}
}
Since your variable is static and the error indicates it runs on a web server, you are most likely facing the problem that two threads access the same value at the same time, resulting in two adds at the same time.
The solution depends on your situation:
Don't make the dictionary static, if you don't intend it to be shared across sessions. This doesn't really fix the problem. It makes it more unlikely to occur;
Use a thread safe dictionary type: ConcurrentDictionary.

.NET: How to convert Exception to string?

When an exception is thrown (while debugging in the IDE), i have the opportunity to view details of the exception:
But in code if i call exception.ToString() i do not get to see those useful details:
System.Data.SqlClient.SqlException (0x80131904): Could not find stored procedure 'FetchActiveUsers'.
[...snip stack trace...]
But Visual Studio has some magic where it can copy the exception to the clipboard:
Which gives the useful details:
System.Data.SqlClient.SqlException was unhandled by user code
Message=Could not find stored procedure 'FetchActiveUsers'.
Source=.Net SqlClient Data Provider
ErrorCode=-2146232060
Class=16
LineNumber=1
Number=2812
Procedure=""
Server=vader
State=62
StackTrace:
[...snip stack trace...]
InnerException:
Well i want that!
What would be the contents of:
String ExceptionToString(Exception ex)
{
//todo: Write useful routine
return ex.ToString();
}
that can accomplish the same magic. Is there a .NET function built in somewhere? Does Exception have a secret method somewhere to convert it to a string?
ErrorCode is specific to ExternalException, not Exception and LineNumber and Number are specific to SqlException, not Exception. Therefore, the only way to get these properties from a general extension method on Exception is to use reflection to iterate over all of the public properties.
So you'll have to say something like:
public static string GetExceptionDetails(this Exception exception) {
var properties = exception.GetType()
.GetProperties();
var fields = properties
.Select(property => new {
Name = property.Name,
Value = property.GetValue(exception, null)
})
.Select(x => String.Format(
"{0} = {1}",
x.Name,
x.Value != null ? x.Value.ToString() : String.Empty
));
return String.Join("\n", fields);
}
(Not tested for compliation issues.)
.NET 2.0 compatible answer:
public static string GetExceptionDetails(this Exception exception)
{
PropertyInfo[] properties = exception.GetType()
.GetProperties();
List<string> fields = new List<string>();
foreach(PropertyInfo property in properties) {
object value = property.GetValue(exception, null);
fields.Add(String.Format(
"{0} = {1}",
property.Name,
value != null ? value.ToString() : String.Empty
));
}
return String.Join("\n", fields.ToArray());
}
I first tried Jason's answer (at the top), which worked pretty well, but I also wanted:
To loop iteratively through inner exceptions and indent them.
Ignore null properties and increases readability of the output.
It includes the metadata in the Data property. (if any) but excludes the Data property itself. (its useless).
I now use this:
public static void WriteExceptionDetails(Exception exception, StringBuilder builderToFill, int level)
{
var indent = new string(' ', level);
if (level > 0)
{
builderToFill.AppendLine(indent + "=== INNER EXCEPTION ===");
}
Action<string> append = (prop) =>
{
var propInfo = exception.GetType().GetProperty(prop);
var val = propInfo.GetValue(exception);
if (val != null)
{
builderToFill.AppendFormat("{0}{1}: {2}{3}", indent, prop, val.ToString(), Environment.NewLine);
}
};
append("Message");
append("HResult");
append("HelpLink");
append("Source");
append("StackTrace");
append("TargetSite");
foreach (DictionaryEntry de in exception.Data)
{
builderToFill.AppendFormat("{0} {1} = {2}{3}", indent, de.Key, de.Value, Environment.NewLine);
}
if (exception.InnerException != null)
{
WriteExceptionDetails(exception.InnerException, builderToFill, ++level);
}
}
Call like this:
var builder = new StringBuilder();
WriteExceptionDetails(exception, builder, 0);
return builder.ToString();
This comprehensive answer handles writing out:
The Data collection property found on all exceptions (The accepted answer does not do this).
Any other custom properties added to the exception.
Recursively writes out the InnerException (The accepted answer does not do this).
Writes out the collection of exceptions contained within the AggregateException.
It also writes out the properties of the exceptions in a nicer order. It's using C# 6.0 but should be very easy for you to convert to older versions if necessary.
public static class ExceptionExtensions
{
public static string ToDetailedString(this Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
return ToDetailedString(exception, ExceptionOptions.Default);
}
public static string ToDetailedString(this Exception exception, ExceptionOptions options)
{
var stringBuilder = new StringBuilder();
AppendValue(stringBuilder, "Type", exception.GetType().FullName, options);
foreach (PropertyInfo property in exception
.GetType()
.GetProperties()
.OrderByDescending(x => string.Equals(x.Name, nameof(exception.Message), StringComparison.Ordinal))
.ThenByDescending(x => string.Equals(x.Name, nameof(exception.Source), StringComparison.Ordinal))
.ThenBy(x => string.Equals(x.Name, nameof(exception.InnerException), StringComparison.Ordinal))
.ThenBy(x => string.Equals(x.Name, nameof(AggregateException.InnerExceptions), StringComparison.Ordinal)))
{
var value = property.GetValue(exception, null);
if (value == null && options.OmitNullProperties)
{
if (options.OmitNullProperties)
{
continue;
}
else
{
value = string.Empty;
}
}
AppendValue(stringBuilder, property.Name, value, options);
}
return stringBuilder.ToString().TrimEnd('\r', '\n');
}
private static void AppendCollection(
StringBuilder stringBuilder,
string propertyName,
IEnumerable collection,
ExceptionOptions options)
{
stringBuilder.AppendLine($"{options.Indent}{propertyName} =");
var innerOptions = new ExceptionOptions(options, options.CurrentIndentLevel + 1);
var i = 0;
foreach (var item in collection)
{
var innerPropertyName = $"[{i}]";
if (item is Exception)
{
var innerException = (Exception)item;
AppendException(
stringBuilder,
innerPropertyName,
innerException,
innerOptions);
}
else
{
AppendValue(
stringBuilder,
innerPropertyName,
item,
innerOptions);
}
++i;
}
}
private static void AppendException(
StringBuilder stringBuilder,
string propertyName,
Exception exception,
ExceptionOptions options)
{
var innerExceptionString = ToDetailedString(
exception,
new ExceptionOptions(options, options.CurrentIndentLevel + 1));
stringBuilder.AppendLine($"{options.Indent}{propertyName} =");
stringBuilder.AppendLine(innerExceptionString);
}
private static string IndentString(string value, ExceptionOptions options)
{
return value.Replace(Environment.NewLine, Environment.NewLine + options.Indent);
}
private static void AppendValue(
StringBuilder stringBuilder,
string propertyName,
object value,
ExceptionOptions options)
{
if (value is DictionaryEntry)
{
DictionaryEntry dictionaryEntry = (DictionaryEntry)value;
stringBuilder.AppendLine($"{options.Indent}{propertyName} = {dictionaryEntry.Key} : {dictionaryEntry.Value}");
}
else if (value is Exception)
{
var innerException = (Exception)value;
AppendException(
stringBuilder,
propertyName,
innerException,
options);
}
else if (value is IEnumerable && !(value is string))
{
var collection = (IEnumerable)value;
if (collection.GetEnumerator().MoveNext())
{
AppendCollection(
stringBuilder,
propertyName,
collection,
options);
}
}
else
{
stringBuilder.AppendLine($"{options.Indent}{propertyName} = {value}");
}
}
}
public struct ExceptionOptions
{
public static readonly ExceptionOptions Default = new ExceptionOptions()
{
CurrentIndentLevel = 0,
IndentSpaces = 4,
OmitNullProperties = true
};
internal ExceptionOptions(ExceptionOptions options, int currentIndent)
{
this.CurrentIndentLevel = currentIndent;
this.IndentSpaces = options.IndentSpaces;
this.OmitNullProperties = options.OmitNullProperties;
}
internal string Indent { get { return new string(' ', this.IndentSpaces * this.CurrentIndentLevel); } }
internal int CurrentIndentLevel { get; set; }
public int IndentSpaces { get; set; }
public bool OmitNullProperties { get; set; }
}
Top Tip - Logging Exceptions
Most people will be using this code for logging. Consider using Serilog with my Serilog.Exceptions NuGet package which also logs all properties of an exception but does it faster and without reflection in the majority of cases. Serilog is a very advanced logging framework which is all the rage at the time of writing.
Top Tip - Human Readable Stack Traces
You can use the Ben.Demystifier NuGet package to get human readable stack traces for your exceptions or the serilog-enrichers-demystify NuGet package if you are using Serilog. If you are using .NET Core 2.1, then this feature comes built in.
For people who don't want to mess with overriding, this simple non-intrusive method might be enough:
public static string GetExceptionDetails(Exception exception)
{
return "Exception: " + exception.GetType()
+ "\r\nInnerException: " + exception.InnerException
+ "\r\nMessage: " + exception.Message
+ "\r\nStackTrace: " + exception.StackTrace;
}
It does not show the SQLException-specific details you want, though...
There is no secret method. You could probably just override the ToString() method and build the string you want.
Things like ErrorCode and Message are just properties of the exception that you can add to the desired string output.
Update: After re-reading your question and thinking more about this, Jason's answer is more likely what you are wanting. Overriding the ToString() method would only be helpful for exceptions that you created, not already implemented ones. It doesn't make sense to sub class existing exceptions just to add this functionality.
For displaying some details to user you should use ex.Message. For displaying to developers you will probably need ex.Message and ex.StackTrace.
There is no 'secret' method, you could consider Message property to be best fit for user friendly message.
Also be careful that in some case you may have inner exception in exception you catch which would be also useful to log.
You will probably have to manually construct that string by concatenating the various fields you are interested in.
Each left-side name is property in the Exception. If you want to display Message field, you can do
return ex.Message;
Pretty simple. Likewise, the StackTrace can be displayed as below link.
A complete example of StackTrace: http://msdn.microsoft.com/en-us/library/system.exception.stacktrace.aspx
and Exception class: http://msdn.microsoft.com/en-us/library/system.exception.aspx
I think the exception serialization to JSON is nice option. Sample result:
{
"Errors": [{
"Source": ".Net SqlClient Data Provider",
"Number": -1,
"Class": 20,
"Server": "111.168.222.70",
"Message": "A transport-level error has occurred when receiving results from the server. (provider: Session Provider, error: 19 - Physical connection is not usable)"
}
],
"ClientConnectionId": "b1854037-51e4-4943-94b4-72b7bb4c6ab7",
"ClassName": "System.Data.SqlClient.SqlException",
"Message": "A transport-level error has occurred when receiving results from the server. (provider: Session Provider, error: 19 - Physical connection is not usable)",
"Data": {
"HelpLink.ProdName": "Microsoft SQL Server",
"HelpLink.EvtSrc": "MSSQLServer",
"HelpLink.EvtID": "-1",
"HelpLink.BaseHelpUrl": "http://go.microsoft.com/fwlink",
"HelpLink.LinkId": "20476"
},
"InnerException": null,
"HelpURL": null,
"StackTraceString": " at System.Data.SqlClient.SqlConnection.OnError ... DbExecutionStrategy.Execute[TResult](Func`1 operation)",
"RemoteStackTraceString": null,
"RemoteStackIndex": 0,
"ExceptionMethod": "8\nOnError\nSystem.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\nSystem.Data.SqlClient.SqlConnection\nVoid OnError(System.Data.SqlClient.SqlException, Boolean, System.Action`1[System.Action])",
"HResult": -2146232060,
"Source": ".Net SqlClient Data Provider",
"WatsonBuckets": null
}
If you call ToString on Exception object, you get the class name appended by the message, followed by inner exception and then the stack trace.
className + message + InnerException + stackTrace
Given that, InnerException and StackTrace are only added if they are not null. Also, the fields you have mentioned in the screenshot are not part of standard Exception class. Yes, exception does offer a public property called "Data", that contain additional user-defined information about the exception.
In visual studio that sort of information can be outputted by a debugger visualizer.
I assume that because it is possible to write your own debugger visualizer:
http://msdn.microsoft.com/en-us/library/e2zc529c.aspx
That in theory, if your can reverse engineer the built-in debugger visualizer for exceptions (if your can work out where they are stored) then you could use the same functionality.
EDIT:
Here is a post about where the debugger visualizers are kept: Where do I find Microsoft.VisualStudio.DebuggerVisualizers?
You might be able to use it for your own purposes.

Categories