March 13, 2007

How to copy an annotation

To copy notes wit or without attachments I have prepared this snippet.

//Copy attributes
annotation newAnnotation = new annotation();
newAnnotation.langid = sourceAnnotation.langid;
newAnnotation.notetext = sourceAnnotation.notetext;
newAnnotation.subject = sourceAnnotation.subject;
newAnnotation.objecttypecode = new EntityNameReference();
newAnnotation.objecttypecode.Value = destinationEntityName;

//Set the destination entity ID
newAnnotation.objectid = new Lookup();
newAnnotation.objectid.type = destinationEntityName;
newAnnotation.objectid.Value = destinationEntityId;

//Create annotation
Guid newAnnotationId = _crmService.Create(newAnnotation);
if (sourceAnnotation.isdocument.Value)
byte[] data = GetAnnotationAttachment(sourceAnnotation.annotationid.Value);
string base64data = System.Convert.ToBase64String(data);
AddAttachment(newAnnotationId, base64data, sourceAnnotation.mimetype, sourceAnnotation.filename);

The code for GetAnnotationAttachment is here.

The code for AddAttachment is here.

How to add an attachment

To upload an attachment I have prepared the following function:

private void AddAttachment(Guid parentAnntotationId, string attachmentBase64Data, string mimeType, string attachmentFileName)
UploadFromBase64DataAnnotationRequest upload = new UploadFromBase64DataAnnotationRequest();
upload.AnnotationId = parentAnntotationId;
upload.FileName = attachmentFileName;
upload.MimeType = mimeType;
upload.Base64Data = attachmentBase64Data;


How to get mime type? Previous post

Finding the file's mime type (MimeType)

I was looking for this for some time now. Simple, but beautiful solution:

public string GetMimeType(byte[] fileBytes)
ContentInfo info = new ContentInfo(fileBytes);
return info.ContentType.Value;
return "application/octet-stream";

How to get (download) an attachment

If you want to get an attachment from a note, then you have to get it using HTTP call. The following function returns an array of bytes that can be saved into a stream or you can convert it to Base64String (System.Convert.ToBase64String(myBytes)) if you want to save it again in CRM.

private byte[] GetAnnotationAttachment(Guid annotationId)
Guid attachid = annotationId;
int objecttypecode = 5;
//Annotation attachment OTC
string url = _serverUrl + "Activities/Attachment/download.aspx?AttachmentType=" + objecttypecode.ToString() + "&AttachmentId=" + attachid.ToString();
System.Net.WebClient myWebClient = new System.Net.WebClient();
myWebClient.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
return myWebClient.DownloadData(url);

March 12, 2007

HTTP status 401: Unauthorized error

I wrote some callouts that use CRM web service for reading some additional data.

Man I was furious! On the test system it worked, on the production I got the 401 unauthorized error.

I Goggled a bit and then I tried the simplest solution:

  • Comment out the code: crmWebService.PreAuthenticate = true;

And it worked!

Guess what? This code was copied from Microsoft CRM 3.0 SDK.

Callout setting files

I created a callout, but I wanted to have an additional XML settings file that can be changed on live system without restarting all necessary services.

You have two options:

  • You can hardcode the full path to the file (bad solution)
  • You can read the file from the "running folder"

The default running folder is:
%windows install folder%/system32/inetsrv.

So put your XML file in this folder and read it from the callout using:

XmlDocument settings = new XmlDocument();

That's it folks!