Friday, August 29, 2008

Starter Execution Method

I have found that half of my trouble in writing a Plugin has been to figure out what message to use and how to pull the GUID from the record that the Plugin was triggered off of. Most of the messages are reasonably straight forward (Assign, Create, Update, or Delete) but finding what to use when an Opportunity closes took some digging. You'd think it was the SetState message but that's not right (nor is it the SetStateDynamicEntity).

In order to help out with this I have written myself a "Starter Execution Method" that has most of the common messages that I tend to use. I haven't coded against every message as there are now a ton of options but some of the new messages available to us are pretty cool.

Below the starter Execution Method I have also posted the possible MessageName's from the SDK. Hopefully this helps some people started.


Starter Execution Method:

public void Execute(IPluginExecutionContext context)
{
string opportunityid = "";

switch(context.MessageName)
{
case "Create":
if (context.OutputParameters.Properties.Contains("id"))
{
opportunityid = context.OutputParameters.Properties["id"].ToString();
}
break;
case "Update":
if (context.InputParameters.Properties.Contains(ParameterName.Target) && context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)
{
DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];
opportunityid = ((Key)entity.Properties["opportunityid"]).Value.ToString();
}
break;
case "SetState":
if (context.InputParameters.Properties.Contains("EntityMoniker"))
{
if (context.InputParameters.Properties.Contains("State"))
{
entity = (Moniker)context.InputParameters.Properties[ParameterName.EntityMoniker];
opportunityid = entity.Id.ToString();
}
}
break;
case "SetStateDynamicEntity":
if (context.InputParameters.Properties.Contains("EntityMoniker"))
{
if (context.InputParameters.Properties.Contains("State"))
{
entity = (Moniker)context.InputParameters.Properties[ParameterName.EntityMoniker];
opportunityid = entity.Id.ToString();
}
}
break;
case "Win":
opportunityClose = (DynamicEntity)context.InputParameters["OpportunityClose"];
Lookup WonLook = (Lookup)opportunityClose.Properties["opportunityid"];
opportunityid = WonLook.Value.ToString();
break;
case "Lose":
opportunityClose = (DynamicEntity)context.InputParameters["OpportunityClose"];
Lookup LostLook = (Lookup)opportunityClose.Properties["opportunityid"];
opportunityid = LostLook.Value.ToString();
break;
case "Assign":
if (context.InputParameters.Properties.Contains("Assignee") && context.InputParameters.Properties["Assignee"] is SecurityPrincipal)
{
Moniker assignEntity = (Moniker)context.InputParameters.Properties["Target"];
opportunityid = assignEntity.Id.ToString();
}
break;
case "Delete":
if (context.InputParameters.Properties.Contains("Target"))
{
Moniker monikerentity = null;
monikerentity = (Moniker)context.InputParameters.Properties[ParameterName.Target];
opportunityid = monikerentity.Id.ToString();
}
break;
}
}

MessageName Class (CrmHelpers)

AddItem
AddMember
AddMembers
AddMembersByFetchXml
Assign
Book
Clone
CompoundCreate
Create
Delete
DeliverIncoming
DeliverPromote
ExecuteWorkflow
ExecuteWorkflow
GrantAccess
Handle
Lose
Merge
ModifyAccess
RemoveItem
RemoveMember
RemoveMembers
RemoveMembersByFetchXml
Reschedule
Retrieve
RetrieveExchangeRate
RetrieveMultiple
RetrievePrincipalAccess
RetrieveSharedPrincipalsAndAccess
RevokeAccess
Route
Send
SetState
SetStateDynamicEntity
Update
Win

Happy Plug-in coding!

David Fronk
Dynamic Methods Inc.

Friday, August 22, 2008

MergeRequest/Response Limitations

This week one of my team members and I have been working through cleaning up duplicate data inside of CRM in such a way that we limit the amount of human interaction with the list of duplicates. When you have more than 150,000 records having someone manually merge even 10% of that is 15,000 records. Even if a list is provided of all of the duplicates that's a full time job for the better part of a year.

By coming up with acceptable matchcodes we figured out how to programmatically merge the records so that there doesn't have to be any human interaction. It works really well, except for one point. When you manually merge two records you get a little checkbox that selects any fields that the child record has data in but the parent record does not and then it populates the parent record with those values from the child record. That bit of code is only in the script on that form. It is NOT in the SDK. I'm working on the best way to go through and push all of the fields that the child has but the parent doesn't to make them populate the parent but unfortunately the project I'm on is a little time sensitive and I may just have to hack through it for an entity and go back later and come up with a reusable solution.

Anyway, just wanted to get that out there in case anyone else was wondering like I was until I saw my first record merge through the SDK.

David Fronk
Dynamic Methods Inc.

Friday, August 15, 2008

Integration with LaserApp

LaserApp is an application that assists in filling out forms. Typically these are forms that need information from a given person and he/she needs to sign the document. LaserApp helps save time by taking saved contact information and pushing it into their large repository of legal, financial, and other forms. Since LaserApp is keeping track of contacts and then pushing it's own contact data into the form wouldn't it be nice if you could use your CRM system to feed the forms provided by LaserApp.

We have bridged this gap at Dynamic Methods and will be participating in the LaserApp conference this upcoming week.


Basically from a high level standpoint, a button gets added to your contact form (or whatever form you would like) and when you press it, you are asked what form(s) you would like to push data to, click submit and you are then the system opens the new LaserApp file that gets created. Opening the file opens the LaserApp client which in turn loads the specified forms with the appropriate data.


Here are some screenshots:



David Fronk
Dynamic Methods Inc.

Friday, August 08, 2008

MSCRM Server Performance Counter Errors

In the last week or two I've gotten bombarded with Performance Counter information. Some was voluntary, some was forced due to errors on client's servers. I am by no means an expert but I do have to say that I have gleaned a fair amount of information that I bet the typical MSCRM admin or consultant doesn't know about. There just isn't much information out there specifically about Performance Counters within MSCRM.

My post last week talked about running the lodctr /R command. I found a post that stated that running just lodctr /R will rebuild all system Performance Counters and thinking this would clean things up I ran it on a server...not smart. At least not smart in the MSCRM world. Everything worked just fine, including CRM, until I ran an IISRESET later in the day and then CRM just died. I found articles on how to manually rebuild Performance Counters (http://support.microsoft.com/kb/300956) and that works just fine for Windows Server Performance Counters just fine. But not MSCRM. There is even a KB article in PartnerSource about Performance Counters but it says that running a repair would work just fine and resolve the problem. Well, I'd tried that before I'd even found the article and about 70% of the way through the repair it would error out with (the all too familiar error to me by now):

The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly.

Only this was on the server and what I did to fix the client was what caused the problem. So, running it again (which I tried) was zero help.

After many, many hours of deliberation I finally spoke with Adam in Australia from MS Support and he helped clear up everything and demistify Performance Counters for me. There are 4 important registry keys that need to be set up correctly in order for Performance Counters to work properly in MSCRM. Oh, and for whatever reason, if the Performance Counters fail then the whole application fails (as if I didn't realize that by now). But most other applications just allow them to fail and move on. They aren't system critical, they just track the performance of the application. Adam has submitted the request of Performance Counters not being a system requirement in future versions, hopefully they listen to his request and/or read this post.

Anyway, back to the fix. In the registry there are 3 keys that need to exist, all are under the following paths:

My Computer\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services (add the last two hives of each hive listed out below to get to the actual keys)

1. CRM Async Service
2. CRM Authentication\Performance
3. CRM Discovery\Performance
4. CRM LocatorService\Performance
5. CRM OutlookSync\Performance
6. CRM Platform\Performance
7. CRM Server\Performance
8. CRM Sql Governor\Performance
9. CRM Sql Governor Multi Instance

(Basically and hive under the Services hive that has CRM in it is what you need to check.)
Oh and one more for workflow performance

10. Windows Workflow Foundation 3.0.0.0

The keys that must be there and their values are:

Key--------------------------Value
Library----------------------netfxperf.dll
First Counter----------------(any value is fine, it just needs to exist)
Last Coutner----------------(any value is fine, it just needs to exist)

Running a repair on CRM will fix all of the Performance Counters for the CRM hives but it won't fix the Workflow hive. So, if you get the error regarding Performance Counters in the middle of your repair I can almost guarantee that your problem is that you are missing keys in your Workflow Performance hive. That's what I had to do and according to Adam (who has written the majority of the Performance Counter KB articles for the MSCRM Support team) only the Workflow Performance Counters would cause the repair to fail since the repair rebuilds them.

Now that we know what needs fixed, how do we fix it? If its CRM Performance Counters, just run a repair on the server. If you error during the repair with a Performance Counter error then you will need to reload the Library, First and Last Counter keys for Workflow Foundation. In order to do this you must use the lodctr command. DO NOT recreate these manually. I haven't tried it so I can't speak to what bad things could occur if you did I just know that you should rebuild them using the lodctr command.

You need to open a command prompt and go to the following directory:

%SystemRoot%:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation

Then run the following command:

lodctr perfcounter.ini

That will rebuild the Workflow Foundation Performance Counters (which MSCRM is dependant on). If you look at the registry you should now see the keys there with correct values. Continue your repair, or try it again and you should be all set. I would recommend a reboot after the repair in this case to get everything reset and you should be back up and running.

For those of you who don't know what Performance Counters are used for, they can be very useful in showing the performance of the server and where the majority of the load is so that you can appropriately manage the server/network and correct the bottleneck of CRM. To view them open a run prompt and type "perfmon". This will open the Performance Monitor. By default it shows Pages/sec (if IIS is installed), Avg. Disk Queue Length, and % Processor Time. You can add other Counters to this list by right clicking in the list of counters at the bottom of the main section of Performance Monitor. Choose "Add Counters..." In the new window that comes up expand the "Performance object" dropdown and pick the application/object you want to monitor. Below that will be all of the counters you can monitor. Take a look at those and you'd be amazed at what you can track for CRM. There are also a lot of really good SQL ones for your SQL server. If you are looking for metrics to show companies the performance increase by going to MSCRM 4.0, or moving to a 64 bit SQL box, this will give you a great comparison and actually show the big wigs that their decision made a big difference.

I have learned a lot about Performance Counters and hopefully my lengthy post has helped someone else glean a little more information as well.

David Fronk
Dynamic Methods Inc.

Sunday, August 03, 2008

Outlook client Offline Performance Counter error when attempting to go Offline

When going offline users see the following message:

The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly.

Event Log Errors:

Event Type: Error
Event Source: LoadPerf
Event Category: None
Event ID: 3013
Date: 7/29/2008
Time: 11:38:09 AM
User: N/A
Computer: LFISKNBXP
Description:
Unable to update the performance counter strings of the 009 language ID. The Win32 status returned by the call is the first DWORD in Data section.

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
Data:
0000: 43 02 00 c0 17 0d 00 00 C..À....

Event Type: Error
Event Source: LoadPerf
Event Category: None
Event ID: 3009
Date: 7/29/2008
Time: 11:38:09 AM
User: N/A
Computer: LFISKNBXP
Description:
Installing the performance counter strings for service CRM Client (%2) failed. The Error code is the first DWORD in Data section.

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
Data:
0000: 43 02 00 c0 f6 13 00 00 C..Àö...

Event Type: Error
Event Source: MSCRMPerfCounters
Event Category: None
Event ID: 17188
Date: 7/29/2008
Time: 11:38:09 AM
User: N/A
Computer: LFISKNBXP
Description:
The description for Event ID ( 17188 ) in Source ( MSCRMPerfCounters ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer. You may be able to use the /AUXSOURCE= flag to retrieve this description; see Help and Support for details. The following information is part of the event: Client.

Event Type: Error
Event Source: MSCRMOfflineSync
Event Category: None
Event ID: 6000
Date: 7/30/2008
Time: 10:30:28 AM
User: N/A
Computer: LFISKNBXP
Description:
An error occurred during Offline Synchronization. Try going offline again, or restart Microsoft Outlook. Exception happened The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly..
Stack Trace Info:
at System.Diagnostics.PerformanceCounter.Initialize()
at System.Diagnostics.PerformanceCounter..ctor(String categoryName, String counterName, String instanceName, Boolean readOnly)
at System.Diagnostics.PerformanceCounter..ctor(String categoryName, String counterName, Boolean readOnly)
at Microsoft.Crm.CrmPerformanceCounterFactory.LoadCounters(CounterContainer counterContainer, PerformanceCounterLoadSetting settings, String component)
at Microsoft.Crm.CrmPerformanceCounterFactory.LoadCounters(PerformanceCounterLoadSetting settings, String component)
at Microsoft.Crm.Performance.OutlookPerformanceCounterTracker.Initialize()
at Microsoft.Crm.Performance.OutlookPerformanceCounterTracker.Increment(OutlookPerformanceCounterType type)
at Microsoft.Crm.Application.Outlook.OfflineSync.SyncData.SyncInfoArray(String syncInfo)
at Microsoft.Crm.Application.Outlook.OfflineSync.SyncData.MoveData(String syncInfo, ITransferDataProvider transferDataProvider, Int32 progressSteps)
at Microsoft.Crm.Application.Outlook.OfflineSync.SyncData.Sync(ITransferDataProvider transferDataProvider, Int32 callPriority)
at Microsoft.Crm.Application.SMWrappers.OfflineSync.SyncThreadMethod() .

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.



Fix:

Uninstalled CRM client and then ran “lodctr /R:loadperf” on client machine from a command prompt. Reinstalled CRM client and went offline successfully.


Hope this helps someone else out as well.

David Fronk
Dynamic Methods Inc.