Wednesday, September 17, 2008

MSCRM4 Diagnostic: Plug-in throwing exception: "The request failed with HTTP status 400: Bad Request"

This error:

• Could be caused by the internal URL being referred by the plug-in is incorrect
• When plug-in executing CrmService action (e.g. Update, Create, and etc.), the error will be thrown.

Details of the error:


Details of the error while debugging in Visual Studio:



Resolution:

1. On the server, run regedit.exe.
    Navigate to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM.

2. Add following values under the MSCRM node:
Name: LocalSdkHost
Type: String
Value: <web_server_name> (host name or host header)

Name: LocalSdkPort
Type: DWORD
Value: <web_server_port_number>
3. Retry the plug-in execution.

Wednesday, July 16, 2008

MSCRM 4.0 Client: Building FetchXML Query Using Advanced Find

Microsoft CRM 4 Advanced Find feature can be used for building FetchXML query:

1. Use Advanced Find to specify the criteria and select the desire columns, execute the query, and verify the results.

2. Press F11, key in javascript:prompt("Result",resultRender.FetchXml.value); into the address bar, and press <ENTER>.



3. A javascript prompt window will appear. The prompt result will be the FetchXML query:

Thursday, June 26, 2008

MSCRM4 Configuration: Configure MSCRM Services to run under Domain User Account

The MSCRM services can be configured to run under Domain User Account during installation. But it still can be changed after installation has been completed.

1. Create a Domain User Account (e.g. MSCRM4Service) using the dsa.msc tool (Active Directory Users and Computers).

2. On the MSCRM Web Server, launch the Internet Information Services (IIS) Manager, locate the CRMAppPool and right click Properties. Go to the Identity Tab, and change the Application Pool Identity to the Domain User Account.



3. Add the Domain User Account to the local group (on the MSCRM Web Server): CRM_WPG (CRM Worker Process Group).



4. Add the Domain User Account to the local group (on the MSCRM Web Server): IIS_WPG (IIS Worker Process Group).



5. Add the Domain User Account as a member to the SQLAccessGroup in the Active Directory (please take note of the proper GUID for the SQLAccessGroup).



6. Add the Domain User Account as a member to the PrivUserGroup in the Active Directory (please take note of the proper GUID for the PrivUserGroup).



7. Create a Login for the Domain User Account in the SQL Server.



8. Configure appropriate mappings for the Domain User Account for databases MSCRM_Config and Organization_MSCRM, assign the db_owner role for both.



9. Launch ADSI Edit (adsiedit.msc)
10. Expand node Domain [Domain Name]
11. Expand node start with DC= under the domain node
12. Expand node CN=Users

13. Locate the Domain User Account created in Step 1 (e.g. CN=MSCRM4Service), right click and select Properties



14. Locate the Attribute servicePrincipalName and click on Edit. Add following entries:
• HOST/HostName
• HOST/HostName.FQDN (Fully Qualified Domain Name)



15. Restart the MSCRM Server.

Friday, June 13, 2008

MSCRM4 Configuration: Configure IIS to support both Kerberos and NTLM protocols

To ensure the IIS supports both Kerberos and the NTLM protocols, the NTAuthenticationProviders metabase property must be set to "Negotiate, NTLM".

By default, the NTAuthenticationProviders metabase property will not be defined when IIS installed. The "Negotiate, NTLM" will be used as the default value when the NTAuthenticationProviders metabase property is not defined. So, the NTAuthenticationProviders metabase property will not need to be configured to use the "Negotiate,NTLM" property value unless the default value has been overwritten.

To check the current value for NTAuthenticationProviders metabase property:

1. Launch Internet Information Services (IIS) Manager, find out the Web Site ID:



2. Start command prompt
3. Go to directory C:\Inetpub\Adminscripts 4. Enter command: cscript adsutil.vbs get w3svc//root/NTAuthenticationProviders

If the NTAuthenticationProviders metabase property has not been configured, result below will be shown:
The parameter "NTAuthenticationProviders" is not set at this node.
else, the configured value will be shown (example):
NTAuthenticationProviders : (STRING) "Negotiate,NTLM"
To configure the NTAuthenticationProviders metabase property to support Kerberos and the NTLM protocols (Negotiate):
• At the command prompt, go to directory C:\Inetpub\Adminscripts, enter command cscript adsutil.vbs set w3svc//root/NTAuthenticationProviders "Negotiate,NTLM"
To remove the value for the NTAuthenticationProviders metabase property:
• At the command prompt, go to directory C:\Inetpub\Adminscripts, enter command cscript adsutil.vbs delete w3svc//root/NTAuthenticationProviders
Authentication Test Page
A Web Page such as below can be used to test the Authentication Mode currently used for the communication:


<%@ Page Language="C#" %>
<html>
<head>
<title></title>
</head>
<body>
<%= "LOGON_USER: " + Request.ServerVariables["LOGON_USER"] + "<br>" %>
<%= "AUTH_USER: " + Request.ServerVariables["AUTH_USER"] + "<br>" %>
<%= "AUTH_TYPE: " + Request.ServerVariables["AUTH_TYPE"] + "<br>" %>
<%= "HTTP_Authorization: " + Request.ServerVariables["HTTP_Authorization"] + "<br>" %>
</body>
</html>
 

Thursday, May 22, 2008

MSCRM4 Diagnostic: The user Id is invalid

This error can be caused by:

• The MSCRM 4 Server wasn't added to the PrivUserGroup
• The service account for MSCRM 4 Server wasn't able to impersonate the actual user

Details of the error:



Error Description:
The user Id is invalid.

Error Number:
0x80041D1F

Full Stack:

[CrmException: The user Id is invalid.]
   at Microsoft.Crm.BusinessEntities.SecurityLibrary.GetPrivilegedUserCallerAndBusinessGuidsFromThread(WindowsIdentity identity, IOrganizationContext context)
   at Microsoft.Crm.BusinessEntities.SecurityLibrary.GetCallerAndBusinessGuidsFromThread(WindowsIdentity identity, Guid organizationId)
   at Microsoft.Crm.Sdk.InProcessCrmService.Execute(Object request)
   at Microsoft.Crm.Application.Platform.ServiceCommands.PlatformCommand.ExecuteInternal()
   at Microsoft.Crm.Application.Platform.ServiceCommands.RetrieveCommand.Execute()
   at Microsoft.Crm.Application.Platform.DataSource.Retrieve(String entityName, Guid entityId, String[] columns)
   at Microsoft.Crm.Application.MasterSiteMap.LoadMasterSiteMapInternal(Guid organizationId, DynamicMetadataCache metadataCache, Boolean useReferenceXml)
   at Microsoft.Crm.Application.MasterSiteMap.LoadMasterSiteMap(Guid organizationId, DynamicMetadataCache metadataCache)
   at Microsoft.Crm.Caching.MasterSiteMapCacheLoader.LoadCacheData(MasterSiteMapCacheKey key, IOrganizationContext context)
   at Microsoft.Crm.Caching.BasicCrmCache`2.CreateEntry(TKey key, IOrganizationContext context)
   at Microsoft.Crm.Caching.BasicCrmCache`2.LookupEntry(TKey key, IOrganizationContext context)
   at Microsoft.Crm.Application.SiteMap.GetSiteMapForUser(IUser user, Client client, Boolean isOffline, String forceGroupProfiles)
   at Microsoft.Crm.Caching.UserSiteMapCacheLoader.LoadCacheData(UserSiteMapCacheKey key, IOrganizationContext context)
   at Microsoft.Crm.Caching.BasicCrmCache`2.CreateEntry(TKey key, IOrganizationContext context)
   at Microsoft.Crm.Caching.BasicCrmCache`2.LookupEntry(TKey key, IOrganizationContext context)
   at Microsoft.Crm.Caching.UserSiteMapCache.LookupEntry(Guid userId, IOrganizationContext context)
   at Microsoft.Crm.Web.Loader.ConfigurePage()
   at Microsoft.Crm.Application.Controls.AppUIPage.OnPreRender(EventArgs e)
   at Microsoft.Crm.Web.Loader.OnPreRender(EventArgs e)
   at System.Web.UI.Control.PreRenderRecursiveInternal()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

[HttpUnhandledException: Exception of type 'System.Web.HttpUnhandledException' was thrown.]
   at System.Web.UI.Page.HandleError(Exception e)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest()
   at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
   at System.Web.UI.Page.ProcessRequest(HttpContext context)
   at ASP.fish_loader_aspx.ProcessRequest(HttpContext context)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)


Resolution:

1. Add the computer as a member to the PrivUserGroup in the Active Directory (can be achieved using dsa.msc tool):

Monday, April 28, 2008

MSCRM4 Diagnostic: Cannot open database "MSCRM_CONFIG" requested by the login. The login failed.

This error can be caused by following scenarios:

• Redeployment
• Moving database to another server
• Upgrades
• Changing from multiple-server environment to single-server environment
• Install MSCRM 4 on existing MSCRM 4 database
• Install MSCRM 4 on existing MSCRM 4 multiple-server environment database
• or whatever activity that causing this issue

Details of the error:

Exception Details: System.Data.SqlClient.SqlException: Cannot open database "MSCRM_CONFIG" requested by the login. The login failed.
Login failed for user 'DOMAIN\SERVER$'.

Stack Trace:

[SqlException (0x80131904): Cannot open database "MSCRM_CONFIG" requested by the login. The login failed.
Login failed for user 'DOMAIN\SERVER$'.]
   System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject) +437
   System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) +82
   System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) +105
   System.Data.SqlClient.SqlConnection.Open() +111
   Microsoft.Crm.CrmDbConnection.Open() +213
   Microsoft.Crm.SharedDatabase.DatabaseMetadata.LoadMetadataXmlFromDatabase(CrmDBConnectionType connectionType, String connectionString, Int32& maxBlobSize) +89
   Microsoft.Crm.SharedDatabase.DatabaseMetadata.LoadCacheFromDatabase(CrmDBConnectionType connectionType, String connectionString) +22
   Microsoft.Crm.ConfigurationDatabase.ConfigurationMetadata.get_Cache() +76
   Microsoft.Crm.ConfigurationDatabase.ConfigurationDatabaseService.InitializeMetadataCache() +5
   Microsoft.Crm.SharedDatabase.DatabaseService.Initialize(String tableName) +20
   Microsoft.Crm.SharedDatabase.DatabaseService.Retrieve(String tableName, String[] columns, PropertyBag[] conditions) +297
   Microsoft.Crm.ServerLocatorService.GetSiteSettingIdFromDatabase() +107
   Microsoft.Crm.ServerLocatorService.GetSiteSettingId() +156
   Microsoft.Crm.ServerLocatorService.GetSiteSetting(String settingName) +32
   Microsoft.Crm.CrmTrace.get_RefreshTrace() +429

[TypeInitializationException: The type initializer for 'Microsoft.Crm.ApplicationSqmHelper' threw an exception.]
   Microsoft.Crm.MainApplication.Application_OnStart() +476


Resolution:

1. Add the computer as a member to the SQLAccessGroup in the Active Directory (can be achieved using dsa.msc tool):



2. Open Management Studio on the database server, expand Server>Security>Logins and right click on the NT AUTHORITY\NETWORK SERVICE and select Properties.

3. At the left, select User Mapping, and configure appropriate mapping for MSCRM_CONFIG and Organization_MSCRM, assign db_owner for both.



4. Restart MSCRM 4 Server.

Friday, April 25, 2008

Web Service: Enabling Web Service Test Form for Remote Client

To enable the Web Service Test Form for remote machine, edit following in the Web.Config:

<system.web>
...
...
...
    <webServices>
        <protocols>
            <add name="HttpPost" />
            <add name="HttpGet" />
        </protocols> 
    </webServices>
...
...
...
<system.web>


This should be enabled for debugging purposes only. For production, this should be disabled (by removing or commenting tags above).

Monday, March 10, 2008

MSCRM 4.0 Client: detachCloseAlert();

To disable alert for closing unsaved form :

   crmForm.detachCloseAlert();

Saturday, February 16, 2008

MSCRM 4.0 Client: Hiding Buttons by Id

To hide a button in MSCRM 4.0 with the button id:

    var objElement = document.getElementById('_MBcrmFormSaveAndClose');
    objElement.innerHTML='<SPAN><SPAN><SPAN><SPAN></SPAN></SPAN></SPAN></SPAN>';


A function can be created to show/hide buttons by providing the Button Id:

// show: bool. Show = true, Hide = false
// id: string. Button Id
// tags: string. Object to stored the HTML tags
crmForm.ShowButton = function(show, id, tags)
{
    // Retrieve relevant button element based on Id
    var objElement = document.getElementById(id);
    var returnValue = null;

    // Show Button
    if (show) 
    {
        // Ensure the HTML tags for showing button is available 
        if (tags != null) 
        {
            // Restore HTML tags for showing button
            objElement.innerHTML = tags;
        }
    }
    // Hide Button
    else
    {
        // Store the HTML tags so that it can be restored when showing button
        returnValue = objElement.innerHTML;
        objElement.innerHTML='<SPAN><SPAN><SPAN><SPAN></SPAN></SPAN></SPAN></SPAN>';
    }

    return returnValue;
}


To call the function to hide a button:

    crmForm.SaveAndCloseButtonTags = crmForm.ShowButton(false, 
        "_MBcrmFormSaveAndClose", crmForm.SaveAndCloseButtonTags);


To show the button:

    crmForm.SaveAndCloseButtonTags = crmForm.ShowButton(true, 
        "_MBcrmFormSaveAndClose", crmForm.SaveAndCloseButtonTags);


To remove the button completely:

    var objElement = document.getElementById('_MBcrmFormSaveAndClose');
    objElement.outerHTML='<SPAN><SPAN><SPAN><SPAN></SPAN></SPAN></SPAN></SPAN>';

Friday, February 15, 2008

MSCRM 4.0 Client: Hiding Buttons by ‘title’ Attribute

To hide a button in MSCRM 4.0 by the button title attribute:

var list = document.getElementsByTagName("LI");
var i = 0;

while (i < list.length) 
{
    if (list[i].getAttribute('title') == 'Save and Close') 
    {
        list[i].innerHTML='<SPAN><SPAN><SPAN><SPAN></SPAN></SPAN></SPAN></SPAN>';
    }
    i = i + 1;
}

Basically the button HTML will be as below (the button outerHTML property):
<LI class=ms-crm-Menu id=_MBcrmFormSaveAndClose title="Save and 
Close" onclick=window.execScript(action) tabIndex=-1 action="crmForm.SaveAndClose();"><SPAN 
class=ms-crm-Menu-Label><A class=ms-crm-Menu-Label onclick="return false;" 
tabIndex=-1 href="javascript:onclick();" target=_self><IMG class=ms-crm-Menu-ButtonFirst 
tabIndex=-1 alt="Save and Close" src="/_imgs/ico/16_saveClose.gif"><SPAN 
class=ms-crm-MenuItem-TextRTL tabIndex=0>Save and Close</SPAN></A></SPAN>

So, in order to hide the button, the tags can be replaced by:

    list[i].innerHTML='<SPAN><SPAN><SPAN><SPAN></SPAN></SPAN></SPAN></SPAN>';

If the button required to be shown again later (e.g. based on certain condition to show/hide), the HTML can be stored into a variable before replacing the tags with the codes shown above, for example:

    crmForm.CUST_SaveAndCloseHTML = list[i].innerHTML;
    list[i].innerHTML='<SPAN><SPAN><SPAN><SPAN></SPAN></SPAN></SPAN></SPAN>';

So, in order to show the button:
list[i].innerHTML = crmForm.CUST_SaveAndCloseHTML;

A function can be created to show/hide buttons (the function has been attached to the crmForm, so that it can be invoked by events triggered by any form element):

// show: bool. Show = true, Hide = false
// title: string. Button Title
// tags: string. Object to stored the HTML tags
// return value: HTML removed HTML tags (for hiding the button)
crmForm.ShowButton = function(show, title, tags)
{
    var returnValue = null;
    var list = document.getElementsByTagName("LI");
    var i = 0;
    
    // Show Button
    if (show) 
    {
        // Ensure the HTML tags for showing button is available 
        if (tags != null) 
        {
            while (i < list.length) 
            {
                if (list[i].getAttribute('title') == title) 
                {
                    // Restore HTML tags for showing button
                    list[i].innerHTML = tags;
                }
                i = i + 1;
            }
        }
    }
    // Hide Button
    else
    {
        while (i < list.length) 
        {
            if (list[i].getAttribute('title') == title) 
            {
                // Return the HTML tags has been removed for hiding 
                // the button so that it can be used to restore when 
                // showing the button
                returnValue = list[i].innerHTML;
                // Hide button
                list[i].innerHTML='<SPAN><SPAN><SPAN><SPAN></SPAN></SPAN></SPAN></SPAN>';
            }
            i = i + 1;
        }
    }

    return returnValue;
}

To call the function to hide a button (the return value has been stored into a property attached to crmForm, so that it can used again to restored the button for showing):

    crmForm.SaveAndCloseButtonTags = crmForm.ShowButton(false, 
            'Save and Close', crmForm.SaveAndCloseButtonTags);

To show the button (again, the return value has been stored into a property attached to crmForm):

    crmForm.SaveAndCloseButtonTags = crmForm.ShowButton(true, 
            'Save and Close', crmForm.SaveAndCloseButtonTags);

If the button need to be removed completely (instead of hiding which can be made visible again later), the hiding action should replace the outerHTML instead of innerHTML with 4 empty <SPAN> tags:

    list[i].outerHTML='<SPAN><SPAN><SPAN><SPAN></SPAN></SPAN></SPAN></SPAN>';

Sunday, February 10, 2008

MSCRM 4.0 Workflow: Workflow forever at the Waiting state

If encountered workflows never get executed. First step is to create a simple workflow (for whatever entity, e.g. Update a value upon creation), and perform an action that can trigger this workflow.

Secondly check the System Jobs to find out whether there is any issue with the workflow. For example, the workflow probably at the Waiting state (Status Reason) for long period of time (or forever):


Use Advanced Find to reveal the Error Code and Message Columns:


If the Message column shows the error:
System.Net.WebException: The request failed with HTTP status 400: Bad Request.

This could be due to the Async Service was unable to connect to the correct URL. To verify, connect to the database: MSCRM_CONFIG, and execute statements below:

SELECT NVarCharColumn
FROM deploymentproperties
WHERE columnname = 'AsyncSdkRootdomain'

SELECT NVarCharColumn
FROM deploymentproperties
WHERE columnname = 'ADSdkRootdomain'

SELECT NVarCharColumn
FROM deploymentproperties
WHERE columnname = 'ADWebApplicationRootdomain'

Ensure the NVarCharColumn column shows the correct URL, else it can be fixed with the following statements (replace server and port with the appropriate values):

UPDATE DeploymentProperties SET 
        NVarCharColumn = '<server>:<port>'
WHERE ColumnName = 'AsyncSdkRootdomain'

UPDATE DeploymentProperties SET 
        NVarCharColumn = '<server>:<port>'
WHERE ColumnName = 'ADSdkRootdomain'

UPDATE DeploymentProperties SET 
        NVarCharColumn = '<server>:<port>'
WHERE ColumnName = 'ADWebApplicationRootdomain'

Monday, January 28, 2008

MSCRM 4.0 Plug-in: Quick Start Guide Part 2 (Debugging)

How to debug plug-in with Visual Studio 2005:

1. Compile the assembly, copy following files to the MSCRM server assembly folder
(e.g. C:\Program Files\Microsoft Dynamics CRM\Server\bin\assembly):

     *.dll
     *.pdb

2. When registering the plug-in, make sure Disk is selected as the Location for the assembly.


3. Back to Visual Studio. Under Debug menu, select Attach Process… And attach to the relevant process which will trigger the debugger:


4. Back to Visual Studio and set break points:


5. Back to MSCRM, and perform an action which will trigger the debugger (based on the message registered for the entity)

6. The Local window will be able to show some information for debugging:


Sunday, January 27, 2008

MSCRM 4.0 Plug-in: Quick Start Guide Part 1 (Creation)

How to create plug-in for MSCRM 4.0:

1. Create a Class Library project.


2. Assign a key to the plug-in. Plug-in assembly must be strong-named and signed with a key. If no existing key available, a new key can be generated:


3. Add following MSCRM 4.0 SDK references:

       microsoft.crm.sdk.dll
       microsoft.crm.sdktypeproxy.dll

       using Microsoft.Crm.Sdk;
       using Microsoft.Crm.SdkTypeProxy;

4. Plug-in must implement the IPlugin interface, for example:

       public class CaseNumber: IPlugin
       {

       }

5. Sample code for generating Case Number (ticketnumber):

public class CaseNumber: IPlugin
{
    public void Execute(IPluginExecutionContext context)
    {
        // Ensure DynamicEntity is available
        if (context.InputParameters.Properties.Contains("Target") &&
            context.InputParameters.Properties["Target"]is DynamicEntity)
        {
            // Retrieve the DynamicEntity
            DynamicEntity entity =
                (DynamicEntity)context.InputParameters.Properties["Target"];
// Ensure the DynamicEntity is incident if (entity.Name == EntityName.incident.ToString()) { // Update the ticketnumber Property entity.Properties["title"]= DateTime.Now.ToString(); } } } }
6. Compile and register the plug-in with the MSCRM Plug-in Registration Tool:


7. The plug-in can be stored (deployed) to MSCRM database, the server hard disk or the GAC. If the Disk option is selected, the assembly file must be placed into C:\Program Files\Microsoft Dynamics CRM\Server\bin\assembly at the server (assuming this is the MSCRM installation path).


8. Register new step for the plug-in:


9. For example, the assembly subscribed to Create event (Message), incident as the Primary Entity, and the Stage is Pre Stage:


10. If Pre-Image or Post-Image is required by the plug-in, a new Image can be registered:


11. Once the plug-in registered, it can be tested by performing relevant actions to trigger the plug-in event.