Showing posts with label Error Codes. Show all posts
Showing posts with label Error Codes. Show all posts

Friday, 27 April 2018

Plugins in the sandbox, and why you don't get System.Security.Permissions.SecurityPermission

A relatively common error with plugins is "Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed". This is the general message that you get when you have a plugin registered in the sandbox, and it is trying to do something that is not permitted. You may get variations on this error, depending on exactly what the code is trying to do, for example:
  • FileIOPermission - access the file system
  • InteropPermission - mostly likely using an external assembly (either directly, or having ILMerged into your plugin assembly)
  • System.Net.WebPermission - some for on network access, e.g. trying to access a network resource by IP address (the sandbox only allows by DNS name)
  • SqlClientPermission - accessing SQL Server
The list can go on, and on. Rather than trying to list everything you can't do, it's a lot simpler to list what you can, which is broadly:
  • Execute code that doesn't try to access any local resources (file system, event log, threading etc)
  • Call the CRM IOrganizationService using the context passed to the plugin
  • Access remote web resources as long as you:
    • Use http or https
    • Use a domain name, not an IP address
    • Do not use the .Net classes for authentication
All of which is pretty restrictive, but is understandable given the sandbox is designed to protect the CRM server. To me, the most annoying one is the last, which makes it pretty much impossible to call other Microsoft web services directly, such as SharePoint or Reporting Services.

So, what to do about it. If you have CRM OnPremise, the simple and only solution is to register the assembly outside the sandbox, so that it can run in FullTrust - i.e. do whatever it wants (though still subject to the permissions of the CRM service account or asynchronous service account that it runs under).

And if you've got CRM Online, then the normal solution is to offload the processing to an environment that you have more control over. The most common option is to offload the processing to Azure, using the Azure Service Bus or Azure Event Hub . The alternative, new to CRM 9, is to send the data to a WebHook, which can be hosted wherever you like. 

Monday, 29 April 2013

Understanding error codes

Error messages frequently include error codes; sometimes they also include useful text that describes the code, but sometimes they don’t, leaving you to discover what the code means. Here's how I decipher CRM and Windows error codes.

CRM Error Codes
The CRM error codes are (reasonably) well documented here in the CRM SDK. If you’re searching for the code, one thing to watch for is that the code may be referenced with a prefix of 0x (which indicates the code is represented in hex) – e.g. 0x80040201. If you search for the code, it’s best to remove the 0x prefix.

It is also possible that you may receive the code as an integer (e.g. -2147220991). If you do, convert if to hex (I use the Calculator application), then search for it.
Windows Error Codes
There is more variation in how you may identify a Windows error code, but they are ultimately numerical values starting from 1, and (as far as I’m aware) are consistent across versions of Windows. Newer versions of Windows may include error codes that don’t exist in previous versions, but the same error code should have the same meaning across versions.

There is a quick and easy way to get the message associated with a given code – go to a command prompt and enter NET HELPMSG - e.g.
NET HELPMSG 5

And you’ll get the result
“Access is denied”

This is the message for error code 5 (which is probably the most common code I encounter, though I don’t keep stats on this…)
So, that’s fine if you’ve been given the error code as an integer value (I don’t know what the highest valued error code is – it’s probably either in the high thousands, or maybe 5 digits), but it’s not always that easy.

The code may be in hex(aka hexadecimal). If it contains one of the characters a-f, then it’s in Hex and you’ll need to convert it to decimal. I use the Calculator application to do this. Also, the value may be provided in hex but comes out just as digits. So, if I have an error code, and the message seems entirely irrelevant, I normally convert the code as if it were in hex to decimal, then pass it to NET HELPMSG.
The code may be in hex, but supplied as a 32-bit (or maybe 64-bit) integer with some higher bit flags set, for example:
80070005
0x80000002
&H80040035
The prefixes 0x and &H are some ways to indicate the value is in hex, and these prefixes can be discarded. You can also discard all but the last 4 characters (in these examples 0005, 0002 and 0035) and convert them from hex to decimal (in these examples giving 5, 2 and 53 respectively).


Finally, you may get a 32-bit integer with some higher bit flags set, receive the value in decimal, rather than hex. These almost always have the highest bit of a 32-bit value set, which means that in decimal they come out around 2 147 000 000 (or more commonly as a negative number, as they are typically signed integers). So, if I got an error code of -2147024891, I would:
  1. Convert it to hex, giving 80070005
  2. Discard all but the last 4 characters, giving 0005
  3. Convert it back to decimal, giving 5
  4. Run NET HELPMSG 5, and find that I’ve got another ‘Access is denied’ message

Friday, 26 April 2013

The given key was not present in the dictionary - what it means, and how to avoid it

A common error posted on the CRM Development forum is ‘the given key was not present in the dictionary’. This is a relatively easy error to diagnose and fix, provided you know what it means. It will also help to identify the line in the code at which the error occurs, which is most easily determined by debugging.

The error refers to a ‘dictionary’, and a ‘key’. The ‘dictionary’ is a type of collection object (i.e. it can contain many values), and the ‘key’ is the means by which you specify which value you want. The following two lines of code both show an example:
Entity e = context.InputParameters["Target"];
string name = e.Attributes["name"];  // Note that this is equivalent to: string name = e["name"];

In the first line, InputParameters is the dictionary, and "Target" is the key. In the second line, Attributes is the dictionary, and "name" is the key. The error ‘The given key is not present in the dictionary’ simply means that the dictionary does not have a value that corresponds to the key. So, this error would occur in the first line if InputParameters does not contain a key called "Target", or in the second line if there were no "name" in Attributes.

The way to avoid these errors is simple; test if the key exists before trying to use it. Different collection classes can provide different ways to perform this test, but the collection classes in the CRM SDK assemblies all inherit from an abstract DataCollection class that exposes a Contains method, so you can use a consistent approach across these collection classes.
if (context.InputParameters.Contains("Target"))
{
 Entity e = context.InputParameters["Target"];
 if (e.Attributes.Contains("name"))
 {
  string name = e.Attributes["name"];
 }
}


There are a few common reasons of the use of CRM collection classes where a key might not be present when you expect it:
  • Within a plugin, the values in context.InputParameters and context.OutputParameters depend on the message and the stage that you register the plugin on. For example, "Target" is present in InputParameters for the Create and Update messages, but not the SetState message. Also, OutputParameters only exist in a Post stage, and not in a Pre stage. There is no single source of documentation that provides the complete set of InputParameters and OutputParameters by message and stage, though this post provides a list of the most common ones for CRM 4, and most of these still apply in CRM 2011
  • The Attributes collection of an Entity will only contain values for attributes that have a value. You may get the Entity from a Retrieve or RetrieveMultiple having specified a ColumnSet with the attribute you want, but this attribute will not be present in the Attributes collection if there were no data in that attribute for that record
  • Within a plugin, the Attributes collection of an Entity that you obtain from the "Target" InputParameter will only contain attributes that were modified in the corresponding Create or Update method. Using the example above, if this were in a plugin registered on the Update message, the "name" attribute would only be present if the "name" attribute was changed as part of the Update; the "Target" InputParameter will not contain all the attributes for the entity

Thursday, 30 July 2009

Some undocumented CRM attribute types

I was working on some custom plugin registration tools recently, and came across some undocumented attribute types. Several customisation entities (e.g. PluginAssembly, PluginType, SavedQuery) have an attribute called CustomizationLevel which has a value of 0 for system data, and 1 if it is custom or customised (according to the SDK).

The SDK documentation states this attribute is of type CrmNumber, so when writing a QueryExpression that selects only records with a CustomizationLevel = 1, it would seem reasonable to use something like:

qe.Criteria.AddCondition(new ConditionExpression("customizationlevel", ConditionOperator.Equal, 1));

However, this gives the error “Condition for attribute 'customizationlevel': expected argument(s) of type 'System.Byte' but received 'System.Int32' " (code 0x80040203 - Invalid Argument). Digging deeper I found that the field for the CustomizationLevel is stored in SQL as a tinyint (i.e. a single-byte integer), and that the AttributeTypes table in CRM has a corresponding AttributeType of tinyint.

So, despite the attribute being identified as a CrmNumber, any condition expressions need to pass values as a single-byte integer, not the documented four-byte integer. This is easily done by using the following:

qe.Criteria.AddCondition(new ConditionExpression("customizationlevel", ConditionOperator.Equal, (Byte) 1));

Being somewhat nosey, I thought to see what other attribute types there were. These can be easily found with the following SQL query:

Select * from AttributeTypes

In addition to tinyint, 2 similar types caught my attention – smallint and bigint (SQL Server data type names are not that imaginative). Following on from this, the following SQL query lists attributes of these types, which may cause similar problems to those above:

select e.name as Entity, a.name as Attribute, at.description as [Type]
from attribute a join entity e on a.entityid = e.entityid
join attributetypes at on a.attributetypeid = at.attributetypeid
where at.description in ('tinyint', 'smallint', 'bigint')
order by at.description, e.name, a.name

This yields the following results. The bigint attributes aren’t a concern, as these 2 attributes aren’t available via the CRM platform, but you could encounter some of the smallint types on UserSettings, in which case I expect you’d have to cast values to Int16.

EntityAttributeType
AsyncOperationsequencebigint
Subscriptioncompletedsyncversionnumberbigint
Organizationtokenexpirysmallint
UserSettingsadvancedfindstartupmodesmallint
UserSettingstimezonecodesmallint
UserSettingstimezonedaylightdaysmallint
UserSettingstimezonedaylightdayofweeksmallint
UserSettingstimezonedaylighthoursmallint
UserSettingstimezonedaylightminutesmallint
UserSettingstimezonedaylightmonthsmallint
UserSettingstimezonedaylightsecondsmallint
UserSettingstimezonedaylightyearsmallint
UserSettingstimezonestandarddaysmallint
UserSettingstimezonestandarddayofweeksmallint
UserSettingstimezonestandardhoursmallint
UserSettingstimezonestandardminutesmallint
UserSettingstimezonestandardmonthsmallint
UserSettingstimezonestandardsecondsmallint
UserSettingstimezonestandardyearsmallint
Organizationfiscalyeardisplaycodetinyint
Organizationtagmaxaggressivecyclestinyint
Organizationtrackingtokeniddigitstinyint
OrganizationUIcustomizationleveltinyint
PluginAssemblycustomizationleveltinyint
PluginTypecustomizationleveltinyint
SavedQuerycustomizationleveltinyint
SdkMessagecustomizationleveltinyint
SdkMessageFiltercustomizationleveltinyint
SdkMessagePaircustomizationleveltinyint
SdkMessageProcessingStepcustomizationleveltinyint
SdkMessageProcessingStepImagecustomizationleveltinyint
SdkMessageProcessingStepSecureConfigcustomizationleveltinyint
SdkMessageRequestcustomizationleveltinyint
SdkMessageRequestFieldcustomizationleveltinyint
SdkMessageRequestInputcustomizationleveltinyint
SdkMessageResponsecustomizationleveltinyint
SdkMessageResponseFieldcustomizationleveltinyint

Tuesday, 6 January 2009

CRM Error Codes

Something that's cropped up on a couple of CRM forums recently is how to get more information about a CRM error code. Error codes can be displayed in one of three formats:
  1. Decimal, e.g. -2147206371
  2. Hex with the 0x prefix - e.g. 0x80043B1D
  3. Hex without the prefix - e.g. 80043B1D

The CRM SDK documentation has a reasonable set of information about each error here, but only refers to error codes in the last of the listed formats (e.g. 80043B1D). To convert the second format to the third format, just remove the 0x prefix.

The easiest way to convert the first (decimal) format is to use the Windows calculator utility (calc.exe), as follows:

  1. Open the calculator in scientific mode
  2. Check the Decimal (Dec) option is set
  3. Paste in the error code - e.g. -2147206371
  4. Select 'Hex' to convert it to Hex, this will then look like: FFFFFFFF80043B1D
  5. Remove the leading FFFFFFFF. You can then find the resulting code, e.g. 80043B1D, in the error codes