Did you sometimes face the following generic error messages and wondered what they mean?
- Object ‘CLRObject’ could not be created
- ClrObject static method invocation error
Those error messages mean that you are working with .NET Framework objects and that in those an Exception (a CLR Exception) was raised that was not handled in the layer of the .NET Framework itself and was passed by to Dynamics AX.
In the first case the CLR Exception occurred before the CLR Object could be created (e. g. when executing the Constructor of the CLR Object) and in the second case the CLR Exception occurred while executing a static method.
Unfortunately the general error message is not so helpful, but fortunately CLR Exceptions usually contain a lot of interesting information like an exact error message and even can include nested Exceptions.
But the question left is: How you can access this information?
The first thing you need to do is finding out at which line of code the Exception is triggered. While usually double clicking in the InfoLog shows you the exact line of X++ code the error was triggered this might fail in this situation. In this case you can follow the steps outlined the existing post Tips & Tricks around debugging X++ code in Dynamics AX (Section: How can I see the call stack that resulted in a X++ exception?) to find the line of X++ code resulting in the CLR Exception.
When you know what code block causes the issue the next thing you need to do is adding a X++ try-catch block around it and catch the type Exception::CLRError.
Inside the catch block you can access the captured CLR exception by calling CLRInterop::getLastException(). If you just want to see what was going wrong without any nice formatting you just can call the ToString() method and show the result in another InfoLog message.
Below you find a small X++ job that raises a CLRException by intention and catches it afterwards:
//Necessary if executed on the AOS
//This will cause an exception
//Access the last CLR Exception
//See AifUtil::getClrErrorMessage for another alternative
//how to parse the Exception object
//Revert CAS back to normal
For code executed on the AOS, don’t forget to do a Code Access Permission Assert for ClrInterop! And if you are looking for a more elegant way to work with the CLR Exception object, I suggest having a look at the existing X++ code in the method AifUtil::getClrErrorMessage().
Below you see the CLR Exception information we manually wrote to the InfoLog:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.FormatException: Input string was not in a correct format.
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at System.Int32.Parse(String s)
— End of inner exception stack trace —
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture)
at ClrBridgeImpl.InvokeClrStaticMethod(ClrBridgeImpl* , Char* pszClassName, Char* pszMethodName, Char* assemblyName, Int32 argsLength, ObjectWrapper** arguments, Boolean* argsAreByRef, Boolean* isException)
=> The part System.FormatException: Input string was not in a correct format. tells us exactly what went wrong