Goodbye CRM


It's been a while since my last post ... in fact more than a year.

A lot changed for me since then. One of the main changes is that my struggle with CRM has ended and I've switched to developing with sharepoint. The main reason for that is that I changed my employer. I will now work exclusively with Sharepoint, MOSS and its WCM features. This is something very new and exciting for me. I really enjoy it since I got a little bored with MS CRM (and with my former company but that is a different story).

I will not delete this blog, but use it to let you know if I have anything interesting to write about my work with Sharepoint. But I guess since I'm a newbie with sharepoint there will be not much to say for the time being.

author: Guenter Wallnoefer | posted @ Saturday, December 22, 2007 10:46 PM | Feedback (1)

Change in MBS Master Requirements


Since July 1st Microsoft Dynamics aka MBS changed the requirements for CRM Master Certifications. Have a look here:

http://www.microsoft.com/learning/mcp/mct/guide/MBS_qualifiers.mspx

If you have partner access you can also have a look here:

https://mbs.microsoft.com/partnersource/communities/training/Certifications/master.htm

Maybe you are a MBS CRM Master and don't know it yet. :-)

Now you don't need to have the Exchange Certification anymore to get the MBS Master for CRM Installation and Configuration. For me this means that I received my third MBS Master CRM Certification, which was confirmed by MBS a few days ago. Since I already have the MBS Master for CRM Application and Development, this means I have 'em all. :-)

author: Guenter Wallnoefer | posted @ Tuesday, August 01, 2006 4:44 AM | Feedback (2)

Asynchronous Ajax calls using with MS CRM 3.0 OnSave event


Think of the following problem: You need to check some data on the server using javascript on the OnSave event of MS CRM. You would instantiate a ActiveX XMLHttp object. Next you would initialize all parameters including the onreadystatechangefunction and then send the request. The data would be returned in your onreadystatechangefunction where you could analyze it. Based on this analysis you would decide whether you would allow saving the CRM form or not. But wait a minute. You are now in your handler function and already left the scope of the OnSave handler. So there is no possibility for influencing the save behavior anymore. The problem is the asynchronous behavior of the ajax call.

I tried several possible solutions, e.g. playing around with window.setTimeout to build some wait function. But nothing accomplished the behavior I wanted:

1. Send the request

2. Get the data

3. Analyze it

4. Decide whether to allow saving or not.

I spend several hours thinking of a solution, but could not find any. But sometimes you have to break your own stupid way of thinking as a programmer and asking what you would call a non-programming person. I described my problem, he thought for a moment and instantly came up with the solution:

1. In the OnSave event send the request and then stop saving the form by setting event.returnValue = false.
2. In your onreadystatechangefunction receive the data, analyze it and then save the form using Save() or SaveAndClose() in your code or don't save it based on this analysis.
3. The only thing you have to consider is that saving the form in this way will again fire the event. To avoid an infinte loop you have to set some kind of flag and analyze it before firing the request.

I don't know if you will find this posting worth the reading, because you would have solved the problem this way from the beginning. But maybe there is someone out there that got stuck in his or her thinking the same way and does not have a colleague like my colleague Mario. :-)

Here is some basic sketch of the code you would insert in the OnSave event:

// write the original event mode into a global variable
eventMode=event.Mode;

// first check the flag
if(typeof notsave!='undefined')
{
 event.returnValue=true;
 return true;
}

window.HttpReq=new ActiveXObject('Microsoft.XMLHTTP');
if (window.HttpReq.overrideMimeType){window.HttpReq.overrideMimeType('text/xml')};
window.HttpReq.onreadystatechange=function(){saveMyEntity();}
window.HttpReq.open('POST', '/myurlthatdoessomething.aspx', true); 
window.HttpReq.send(); 

// the following two code lines are the important part
event.returnValue = false;
return false;

function saveMyEntity()
{
    if (window.HttpReq.readyState==4)
    {
    if (window.HttpReq.status==200)
    {      
 // here is the analysis of the server responce  
 res=window.HttpReq.responseXML.selectSingleNode('//check').text;
   
 if(res=='ok')   
 {
  // this is the flag. it is a global variable. as an alternative bind it to the window object
  notsave=1;
  
  if(eventMode==1)
  {
   crmForm.Save();
  }
  else
  {
   crmForm.SaveAndClose();
  }
 }
 else
 {
  alert('Some freaky error message that confuses the user and leaves him completely puzzeled');
 }
      }
    }
}

Hf :-)

author: Guenter Wallnoefer | posted @ Wednesday, July 05, 2006 1:02 PM | Feedback (5)

MS CRM 3.0: Add menu items to detail forms of entities that are not supported in ISV.config


For one of my projects I had to extend the menu of the detail form of email entitiy in MS CRM 3.0 Unfortunately I could not do this using ISV.config because it only supports a handful of entities. Email is not one of them.
Fortunately MS CRM 3.0 supports java scripting on the onload event of the page. So I injected some new elements into the DOM to add another menu item to the actions menu. Of course this kind of HTML code injection into CRM pages is not something new, but the keypoint here is to use the "action" attribute of the table row element. I tried several possible solutions but it seems that this is the only possible solution to trigger the click event on the menu item.

Figure out the details with the following code snippet:

if(IsOnline()) // is only needed if the action has to be made online
{
    if (crmForm.FormType==2) // i only wanted the new menu item to appear on update forms
    { 
        // first get the current emailid if needed
        str=location.href;       
        erg=str.search(/id=[\{\}\dA-Fa-f\-]/);
       
        add='';
       
        if(erg!=1)
        {
            add='?' + str.substring(erg);
        }
       
        // don't blame me vor chosing variable names - i was in a hurry :-)
        trSpacer=document.createElement('tr');
        trSpacer.setAttribute('class', 'mnuSpacer');
        
        tdSpacer1=document.createElement('td');
        tdSpacer2=document.createElement('td');
        tdSpacer2.setAttribute('class', 'mnuSpacer');
        tdSpacer2.colspan='2';
        brSpacer=document.createElement('br');
        tdSpacer1.appendChild(brSpacer);
        hrSpacer=document.createElement('hr');
        hrSpacer.setAttribute('class', 'mnuSpacer');
        tdSpacer2.appendChild(hrSpacer);   
       
        trSpacer.appendChild(tdSpacer1);   
        trSpacer.appendChild(tdSpacer2);       
      
        tr=document.createElement('tr');
       
        // the next line is the essential part
        // you can replace it with any javascript code you want
        // i did some redirection to my custom page
        // which messes a little bit around on the server
        // and then redirects back to the page 
        // of course an ajax based solution could be an alternative
 // separate several js statements with semcolons ;
        tr.action='location.replace(\'my.aspx' + add + '\');';       
       
        td1=document.createElement('td');
        td1.style.border='1px solid #dcdfe5';
        td1.innerHTML=' '
       
        td2=document.createElement('td');
        td2.style.border='1px solid #ffffff';
        td2.colspan='2';
        td2.innerHTML='Menu item name';
      
        tr.appendChild(td1);
        tr.appendChild(td2);
        document.getElementById('mnuaction').childNodes[1].appendChild(trSpacer);
        document.getElementById('mnuaction').childNodes[1].appendChild(tr);
   }
}

Maybe this is useful for someone out there.

Hf :-)

author: Guenter Wallnoefer | posted @ Tuesday, June 06, 2006 1:31 PM | Feedback (1)

When you create a MS CRM 3.0 queue and it does not work, don't forget to ...


Ok, today I got something to laugh about for you guys out there. I don't know why it hasn't happened before, but this week was the first time I had to create a CRM queue on a new 3.0 system. So what I did was to grab my copy of CRM Installation and configuration course documents and proceed the mentioned step by step instructions. I created the user, set the correct privileges and attributes, disabled the user account and added the queue in MS CRM application. Finally I sent an email to the queue ... but nothing happened. So I checked the event logs on both Exchange and CRM servers. But there were no entries in there. After some NG and google research I played around with user privileges and registry keys, but still nothing happened. Because I had restarted IIS, Exchange Router service and Exchange itself several times, the system administrator got mad at me because MOM (MS Operations Manager) was driving him crazy with alert messages. :-)
Of course I checked the procedure to add a queue several times, and I did it exactly as stated. In a sudden inspiration I checked the official Implementation guide and found the solution: as a last step you have to add mailbox rules to your queue user using the rule deployment wizard. It's that simple.
There are 2 reasons why I post this here: 1. maybe there is someone out there who is also relying on the offical course documents and encountering the same situation. Hopefully he reads this and saves some time. 2. it's another lesson for me: never trust information - even if its printed and packed in a fancy white ring binder with happy looking people on the cover. :-)

author: Guenter Wallnoefer | posted @ Wednesday, May 31, 2006 9:23 AM | Feedback (3)

MS CRM 3.0 ExchangeSinkServiceException


Since I encountered this exception now at 2 customer installations, I have decided to say some things about it here: I don't know why - but in both cases MS CRM Exchange Router stopped working after 2 weeks (this is no joke). Of cource users were complaining that emails didn't show up in CRM anymore. When I checked the event log, I found many error messages of the same type. They were all some kind of
Microsoft.Crm.Tools.ExchangeConnectorService.ExchangeSinkServiceException because of unauthorized access (401). After some google and NG search I tried several workarounds like changing the service credentials, checking security groups (PrivUserGroup), giving mailbox rights, or playing around with delegation. But the only effect was that I got another ExchangeSinkServiceException. This exception stated that the email message that should be attached to CRM could not be checked.
The next thing I did was to find out how Exchange Router and CRM process emails. It turned out the router calls a webservice on CRM server (CheckIncoming ) with the Message ID. Since the second exception was some kind of server error (500) I figured out that CRM could have problems connecting to Exchange Router email box. So I decided to run the Exchange Router Service with the credentials of the user who owns the MS CRM Exchange Router mail box (because this user should not have any problems connecting to his own mail box). I don't know if this is correct, but CRM seems to connect back to Exchange and does some checking involving the CRM tracking token (or something completely different ;-). In addition I added the user to the PrivUserGroup so that the user account has sufficient privileges to create emails in CRM. Doing that the first webservice call did work correctly. Using a second webservice (DeliverIncoming) Exchange Router then created the email in CRM.
So if you encounter this exception and don't find a solution, try the following:

1. Add the user account owning the CRM Exchange Router mail box to PrivUserGroup.
2. Change the credentials of CRM Exchange Email Router Service to this user account.

Hf ;-)

P.S. I really don't know if the above explanation has something to do with reality. Probably it is complete nonsense. :-)

author: Guenter Wallnoefer | posted @ Tuesday, May 23, 2006 11:56 AM | Feedback (4)

Red Gate SQL Prompt for Free - SQL IntelliSense for your favorite SQL Editor


Ok, it has nothing to do with MS CRM ... but it seems that everyone is blogging about this great tool today : SQL Prompt. And there is a reason for it. It is sooooooo cool. Everyone in my company is excited about this tool. And the best thing about it: it's for free ... at least until Sept. 1, 2006. If you are a database developer, I think this is a must-have:

http://www.red-gate.com/products/SQL_Prompt/index.htm

Hf ;-)

author: Guenter Wallnoefer | posted @ Tuesday, May 23, 2006 11:54 AM | Feedback (1)

How to programmatically access MS CRM Reports


MS CRM Reports can be accessed programmatically. But since MS CRM uses SQL Reporting Services, accessing reports is no CRM feature. Fortunately SRS exposes a webservice interface that you can use to do that. Further information can be found here:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rsprog/htm/rsp_prog_intro_1pia.asp

There are several possible use cases. E.g. if you want to trigger report creation and delivery by MS CRM workflow. For this you have to create a .NET assembly that will be used in your workflow. Next you have to add a reference to ReportServer/ReportService.asmx. You can figure out the rest looking at the following code snippet:

reportingserver.ReportingService rs=new reportingserver.ReportingService();
rs.Url="
http://yourhoust/ReportServer/ReportService.asmx";
rs.Credentials=System.Net.CredentialCache.DefaultCredentials;
 // or: new NetworkCredential("user", "pwd", "domainname");
    
byte[] result=null;
string reportPath="/yourorganizationname/reportname"; // adapt
string format="PDF"; // or XML, CSV, IMAGE, PDF, HTML4.0, HTML3.2, MHTML, EXCEL, and HTMLOWC
string historyID=null;
string
devInfo=@"<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>";

ParameterValue[] parameters=null;

DataSourceCredentials[] credentials=null;
string showHideToggle=null;
string encoding;
string mimeType;
Warning[] warnings=null;
ParameterValue[] reportHistoryParameters=null;
string[] streamIDs=null;
SessionHeader sh=new SessionHeader();
rs.SessionHeaderValue=sh;

try
{
 result=rs.Render(reportPath, format, historyID, devInfo, parameters, credentials,
  showHideToggle, out encoding, out mimeType, out reportHistoryParameters, out warnings,
  out streamIDs);
}
catch(SoapException e)
{
 // ... 
}

// Write the contents of the report to a PDF file and mail it

try
{
 string filename=Path.Combine(Path.GetTempPath(), "report.pdf");
 FileStream stream=File.Create(filename, result.Length);
 stream.Write(result, 0, result.Length);
 stream.Close();

 MailMessage mm=new MailMessage();
 mm.To="
whoever@wants.it"; 
 mm.From="
reports@crm.com";
 mm.Subject="Report";
 mm.Body="... bla ... ";    
 MailAttachment att=new MailAttachment(filename);
 mm.Attachments.Add(att);

 SmtpMail.Send(mm);
}
catch ( Exception e )
{
 // ...
}


Hf :-)

author: Guenter Wallnoefer | posted @ Monday, May 22, 2006 12:43 PM | Feedback (3)

MS CRM workflow: use unsigned assemblies


There is no need to use strong named assemblies in your MS CRM workflow processes. If you want to use unsigned assemblies, you just have to add allowunsignedassemblies to the root element of workflow.config:

<workflow.config xmlns="http://microsoft.com/mscrm/workflow/" allowunsignedassemblies="true">

Of course this is mentioned in the SDK. But since people keep asking this in newsgroups, I thought I could point it out here for those who don't know.

Hf ;-)

author: Guenter Wallnoefer | posted @ Wednesday, April 26, 2006 12:28 PM | Feedback (2)

MSCRM 3.0 + Reporting Services + Cannot create a connection to data source + rsProcessingAborted + rsErrorOpeningConnection


When you open Reports from within MS CRM 3.0 after installation, it is possible to receive an error stating something like "An error has occurred during report processing. (rsProcessingAborted) Cannot create a connection to data source 'xxx'. (rsErrorOpeningConnection)". The solution to this error is not easy because there are many possible reasons. Since I encountered this error several times now,  I developed a solution strategy that works for me. Since there are also other peoples encountering these problems, I decided to post this solution here. But use this at your own risk!

Step 1:

If CRM Server and SQL Server are running on two different servers, you should check if both servers are in "Pre-Windows 2000 Compatible Access" group. If not, add them.

Step 2:

Open AD Users and Computers MMC Snap-In (dsa.msc) and search for CRM server. Select properties and then click on Delegation tab. Make sure the option "Trust this computer for delegation to specified services only" is selected. If it doesn't exist, add SQL Service from your SQL Server below (Mssqlsvc usually on port 1433). If the service is not listed you must add a SPN. If you don't know how to do that, you may take a look here (or go to Step 6):

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/adminsql/ad_security_2gmm.asp

Step 3:

If there are multiple DCs, force replication.

Step 4:

Wait a little bit, drink some coffee, take a nap. :-)

Step 5:

Check if reports work. If yes, stop here. If not, proceed to Step 6.

Step 6:

Open up SRS Report Manager. Usually it is installed on CRM Server in the virtual directory "Reports", so open IE and type "http://<CRM server URL and port>/Reports". Then choose your organization and click on "Show details". In the list search for the data source. It should be named "MSCRM_Datasource" or something like that. Click it and look at the connectionstring. Make sure it points to the correct CRM databases containing your organization name. In addition make sure "Connect using Windows NT Integrated Security" is checked. If you changed something go to Step 5. If you didn't change anything proceed to Step 7.

Step 7:

If your SQL Server runs with Windows Authentication only (which it should): Create a user account in AD with a password that does not change. Then you have several possibilities: you may add this user to AD "SQLAccessGroup" or you may add a specific login to SQL Server for this user and add this login to CRM databases. Choose db_owner for database roles (these are the roles for the MS CRM standard logins, so don't blame me). Then go back to SRS Report Manager and manipulate the data source. Choose "Connect using Credentials stored securely in the report server", enter the credentials and check "Use as windows credentials when connecting to the database".
If your SQL Server is running in mixed mode (which it should not), you do not have to create a user account in AD. You are able to create a SQL login and use this. Add this login to the correct databases and roles (see above). Using SQL login you must NOT check "Use as windows credentials when connecting to the database".
In both cases also check "Impersonate the authenticated user after a connection has been made to the database". This is very important for CRM security mechanisms!

Step 8:

Reports should work now. I didn't encounter a situation where this did not function. Please be aware that Step 7 is not a real solution, but it works.

Have fun ;-)

P.S. If you have problems understanding how to perform any of those steps, ask your system administrator or database administrator for assistance. Right know I don't have the time to explain it in more detail or add some screenshots (weekend is near).

author: Guenter Wallnoefer | posted @ Friday, April 21, 2006 9:46 AM | Feedback (7)

How to manipulate Lookup fields in CRM URL addressable forms from another window


CRM has a quite cool feature that can save you a lot of programming: URL addressable forms. If you want to let users create or update records, you can use the CRM standard forms. The only thing you have to do: open an new window with the specific URL. If you want to edit a record, you only have to append the id of the specific record. In edition you are able to manipulate every form field in the new window and pre-fill them. (I do not mention x-domain-scripting here).

If you want to manipulate lookup fields, you have to do this in a special way to make it work. I did not find it anywhere, so I will post here how to do it. Just look at the following JS snippet to figure out how. In the following example the source campaign of a new lead record is pre-filled:

var newwin;

// first you have to open an new window. call the following function from anywhere on your web page
function OpenNewLead()
{
 newwin=window.open('/sfa/leads/edit.aspx', 'newlead',
  'width=830,height=600,directories=no,menubar=no,resizable=yes,titlebar=no,toolbar=no');
    
 window.setTimeout("PreFillSourceCampaign()", 1000);
}

function PreFillSourceCampaign()
{
 if(!newwin.document.crmForm.all.campaignid.DataValue)
 {  
  var ar=new Array(1);
  ar[0]=new Object;   
  ar[0].id='{00000000-0000-0000-0000-000000000000}'; // fill in here the correct GUID of your object
  ar[0].type='4400'; // adapt the typecode for your entity type
  ar[0].name='My Campaign Name';   // adapt the correct object name
  newwin.document.crmForm.all.campaignid.DataValue=ar;
 }
 else
 {
  window.setTimeout("PreFillSourceCampaign()", 1000);
 }
}

P.S. I use window.setTimeout here to make sure the DOM of the new child window is loaded before I manipulate the field.

Have fun! :-)

author: Guenter Wallnoefer | posted @ Tuesday, April 18, 2006 10:05 AM | Feedback (3)

Programmatically change SiteMap XML or any other entity or ISV.config using MS CRM 3.0 web services (supported version)


Since Jeffry van de Vuurst pointed out that there is a supported way to achieve manipulating SiteMap XML, ISV.config, Entity manipulation, etc. (thanks for mentioning it) and I have some time, I will update the little code snippet I posted in a previous post. Please use this version. Additional information can be found in the new SDK 3.0.4 (ISV Programming Guide).

To run this code you have to reference all the helper classes found in the new SDK 3.0.4 in the sdk samples\isvreadiness sub folder.

CrmService service=new CrmService();
service.Url="
http://localhost/mscrmservices/2006/crmservice.asmx";
service.Credentials=System.Net.CredentialCache.DefaultCredentials;
ExportXmlRequest export=new ExportXmlRequest();
export.ParameterXml="<export><entities></entities><nodes><node>sitemap</node></nodes></export>";

ExportXmlResponse entities = (ExportXmlResponse)service.Execute(export);
XmlDocument siteMapXml=new XmlDocument();
siteMapXml.PreserveWhitespace=true;
siteMapXml.LoadXml(entities.ExportXml);
Microsoft.Crm.Sdk.IsvReadiness.SiteMapCustomizer siteMapEditor=
 new Microsoft.Crm.Sdk.IsvReadiness.SiteMapCustomizer(siteMapXml);

Microsoft.Crm.Sdk.IsvReadiness.SupportingItems.SiteMap.SubArea subArea=
 new Microsoft.Crm.Sdk.IsvReadiness.SupportingItems.SiteMap.SubArea();
subArea.Id="nav_sometest";
subArea.Title="Click me for happiness";
subArea.Icon="some.gif";
subArea.Url="/someurl.aspx";

siteMapEditor.AddSubArea("Settings", "Settings", subArea);

ImportXmlRequest import=new ImportXmlRequest();
import.ParameterXml="<import><entities></entities><nodes><node>sitemap</node></nodes></import>";
import.CustomizationXml=siteMapXml.OuterXml;
service.Execute(import);

Please make sure you also add the correct using statements for the web service.

Hf :-)

author: Guenter Wallnoefer | posted @ Friday, March 31, 2006 10:08 AM | Feedback (1)

Programmatically change SiteMap XML or any other entity or ISV.config using MS CRM 3.0 web services


IMPORTANT: You should look at the comment posted by Jeffry van de Vuurst first. In the meantime there was a supported way added to SDK 3.0.4 which describes to change entities, ISV.config, ...

SiteMap XML is a really cool new feature in MS CRM 3.0. The most exciting thing for me is the ability to restrict access to Areas of the SiteMap by specific privileges in MS CRM. In most cases you would modify the SiteMap XML using the supported way by manually exporting, modifying and then importing the XML using the MS CRM UI. But there may be cases when you need to do this programmatically (e.g. using an installer). And there is a way ... but it's not documented in the SDK (at least I didn't find it). So this is totally unsupported and you must use it on your own risk blabla.
There are two web services used for this located in MSCRMServices: ExportXmlWebService and ImportXmlWebService. Just look at the code sample to figure out how to do it:

ExportXmlWebService exportService=new ExportXmlWebService();
exportService.Credentials=System.Net.CredentialCache.DefaultCredentials;  
exportService.Url="
http://localhost/mscrmservices/exportxml.asmx";    

// get the sitemap xml
XmlDocument sitemapDoc=new XmlDocument();
sitemapDoc.LoadXml(
 exportService.Export("<export><entities></entities><nodes><node>sitemap</node></nodes></export>")
);

// add new sub area
XmlElement subArea=sitemapDoc.CreateElement("SubArea");
XmlAttribute attrId=sitemapDoc.CreateAttribute("Id");
attrId.Value="nav_sometest";
XmlAttribute attrTitle=sitemapDoc.CreateAttribute("Title");
attrTitle.Value="Click me for happiness";
XmlAttribute attrIcon=sitemapDoc.CreateAttribute("Icon");
attrIcon.Value="some.gif";
XmlAttribute attrUrl=sitemapDoc.CreateAttribute("Url");
attrUrl.Value="/someurl.aspx";
XmlAttribute attrClient=sitemapDoc.CreateAttribute("Client");
attrClient.Value="Web";

subArea.Attributes.Append(attrId);
subArea.Attributes.Append(attrTitle);
subArea.Attributes.Append(attrIcon);
subArea.Attributes.Append(attrUrl);
subArea.Attributes.Append(attrClient);

sitemapDoc.SelectSingleNode(
 "/ImportExportXml/SiteMap/SiteMap/Area[./@Id = 'Settings']/Group[./@Id = 'Settings']"
).AppendChild(subArea);

ImportXmlWebService importService=new ImportXmlWebService();
importService.Credentials=System.Net.CredentialCache.DefaultCredentials;
importService.Url="
http://localhost/mscrmservices/importxml.asmx"; 


// reimport the sitemap xml
importService.Import("<import><entities></entities><nodes><node>sitemap</node></nodes></import>",
 sitemapDoc.OuterXml);

For isv.config use: <export><entities></entities><nodes><node>isvconfig</node></nodes></export>
For any entity use: <export><entities><entity>account</entity></entities><nodes></nodes></export>

Of course you are able to combine any nodes and entities.

P.S. If you copy and paste this code sample, don't forget to add the two web references and the correct using statements in VS.

author: Guenter Wallnoefer | posted @ Tuesday, March 14, 2006 7:54 AM | Feedback (4)

MS CRM 3.0 + HttpModule + AspNetHostingPermission in Reporting Services


Yesterday I encountered an interesting problem on a MS CRM 3.0 installation running as default website in IIS. This was due to an upgrade from CRM 1.2 to 3.0. The installation included customizations one of them in form of a HttpModule. Everything worked fine, but viewing reports threw a SecurityException stating that "Request for the permission of type System.Web.AspNetHostingPermission ... failed".

I didn't know the reason for this exception, so I did a bit of research. I found out that Reporting Services uses its own security policy files. There is a good article about this on MSDN:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/RSPROG/htm/rsp_prog_extend_install_25ia.asp

After experimenting a bit, I found the following solution to my problem (this is for use with private assemblies, for global assemblies in GAC see below):

1. First I made a backup of the following two files (do not skip this step !!!):

C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportManager\rsmgrpolicy.config
C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportServer\rssrvpolicy.config

2. I inserted a new code group into both files after "Microsoft_Strong_Name" code group:

<CodeGroup class="UnionCodeGroup"
   version="1"
   PermissionSetName="FullTrust"
   Name="CRMHttpModuleCodeGroup"  
   Description="Code group for CRM HTTP module">
      <IMembershipCondition class="UrlMembershipCondition"
         version="1"
         Url="C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportServer\bin\[yourassembly].dll" />
</CodeGroup>

Do not forget to match 'Url' attribute with the path to ReportManager directory for rsmgrpolicy.config.

If you have multiple HttpModules, you can do this for every assembly.

This procedure works for HttpModule assemblies that do not reside in GAC. But you have to make sure that your private HttpModule assembly resides in all necessary bin directories. This includes:

- bin directory of CRM web aplication.
- mscrmservices\bin directory of CRM web aplication
- C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportManager\bin
- C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportServer\bin

Placing your HttpModule in GAC is much more convenient, because you don't have to copy it to four different locations. First you have to "strong name" your assembly and place it into GAC (if you don't know how to do it, ask my friend google). Then you have to add a different code group into Reporting Services security policy files:

<CodeGroup class="UnionCodeGroup"
   version="1"
   PermissionSetName="FullTrust"
   Name="CRMHttpModuleCodeGroup"  
   Description="Code group for CRM HTTP module">
      <IMembershipCondition class="StrongNameMembershipCondition"
         version="1" PublicKeyBlob="..." />
</CodeGroup>

(if you don't know how to get your PublicKeyBlob: use sn.exe with -e and -tp options with the strong name key file you used when creating your assembly)

If your HttpModule is then in GAC, you may still receive a Configuration Error stating "Parser Error Message: Assembly someassembly.dll security permission grant set is incompatible between appdomains." I don't know why this exception is thrown because it does not seem to make any sense. The actual reason for this exception is that the HttpModule assembly is not found. Make sure you use the fully qualified name when you add your assembly to web.config:

<httpModules>
   <add type=".NET Class, Assembly [,Version=version number] [,Culture=culture] [,PublicKeyToken=token]" name="CRMHttpModule" />
</httpModules>

If you still get an exception, there may be missing dependencies to your HttpModule assembly in GAC. To get rid of these use FUSLOGVW.exe.

Hope this helps someone out there.

author: Guenter Wallnoefer | posted @ Thursday, March 23, 2006 8:51 AM | Feedback (3)

Never trust MS CRM error messages


Yesterday we experienced a quite interesting phenomenon. A colleague of mine tried to create products with the API but received an error from time to time. The error stated that the product number already existed in CRM. But in fact, the product number was unique and did not yet exist in CRM.

So we started SQL profiler to dig a little deeper. We found out that the problem occurred from passing a string to a custom attribute which exceeded its maximum length. The SQL server error message stated that the data would be truncated.

What do we learn from that? CRM error messages might mislead you in your error resolving strategy. If an error cannot be resolved with the information you get from CRM, try to get information from other sources (e.g. SQL profiler).

author: Guenter Wallnoefer | posted @ Wednesday, March 08, 2006 7:18 AM | Feedback (2)