CRM 2011 Custom Workflow Continually in 'Waiting' Status - c#

I am working on a Custom Workflow within CRM 2011. I have created the workflow to create a couple of records (invoice and invoice product) once I get a particular type of Activity (a custom activity). During the testing I passed the particular GUID of the entity I would be working with (the creation of said entity would be the trigger for the workflow). The workflow works fine when I pass in a GUID for the record I want to work with. However, once I load the dll File inside CRM and attempt to trigger the workflow it goes into a waiting status and stays there. I have try catch blocks on all of my functions with a throw new InvalidPluginExecutionException("Error occurred in MethodName:" + ex.Message);. It does not fail or stop but just continues in a waiting status.
I have tried to Reset :
IIS
async service
Sync Services
(Have Tried Edit:
Adding Version Control
Uninstall / Reinstall assembly)
Currently I am attempting to pull the activity id of the PrimaryEntityId as my primary entity is the record I need to use for the workflow. The only thing that I need from that record is the ID.
public String GetFeeId(WorkFlowHelper workFlowHelper, CodeActivityContext executionContext)
{
String feeRecordId = string.Empty;
try
{
var primaryEntity = workFlowHelper.workFlowContext.PrimaryEntityId;
if (primaryEntity != null)
{
feeRecordId = workFlowHelper.workFlowContext.PrimaryEntityId.ToString();
}
if (primaryEntity == null)
{
workFlowHelper.WorkFlowError("Primary Entity is null");
}
}
catch (Exception ex)
{
if (workFlowHelper.debugMessagesOn == true)
{
Console.WriteLine("Id is blank!");
}
workFlowHelper.WorkFlowError(ex.ToString());
throw new InvalidPluginExecutionException("Error occured in ConnectionInfo Method:" + ex.Message);
}
return feeRecordId;
Any ideas on what could be causing this?
Thanks,

It sounds like CRM may be having a problem loading your assembly, or confusing it for an older version of the same assembly.
Are you versioning your workflow assembly while you develop and deploy it, and is the assembly signed with a strong-name key?
In Visual Studio on the Project tab, make sure you increment the Build/Revision number. See this article: http://gonzaloruizcrm.blogspot.com/2011/08/assembly-versioning-in-crm-2011.html
If you don't increment the Build/Revision, CRM may not see your updated assembly as an "update" and may be trying to use an older, cached version, which can cause all kinds of problems.
You may also try unregistering the assembly completely and then registering your latest version. You might need to update your workflows as well.

Here's the most simplest way (taken out your WorkFlowHelper)..
public String GetFeeId(CodeActivityContext executionContext)
{
// Create the context
var context = executionContext.GetExtension<IWorkflowContext>();
var feeRecordId = context.PrimaryEntityId
}

Related

Post Journal Entries to EPICOR 905 using C#

I'm trying to connect to Epicor905 and post a journal entry programmatically.
I found the below code which connects to Epicor. However, I am unable to locate any info on accessing the GL Journal Entry module. I'm fairly new to C# and just want someone to point me in the right direction logically/technically. I understand that the core of it is working with DLLs and the business objects. But beyond that I am clueless. Here is the code I found to connect to EPICOR:
using Ice.Core;
using Erp.Common;
try
{
Session obj = new Session("manager", "manager", Session.LicenseType.Default, #"C:\Epicor\E10Pilot.sysconfig");
if (obj != null)
{
MessageBox.Show("Sesion valida");
obj.Dispose();
obj = null;
}
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
The easiest way to identify the calls required is to start client tracing and run through the process you wish to automate in the UI. This will record the calls that the UI makes for your particular process. You should then be able to replicate them in your code.
You will need to reference the contract assembly for each BO required from your client directory.
This will take some experimentation to identify the right calls but this is exactly how the CSG team in Epicor would approach this.

Cannot get ValidStates of WindowsInstaller features

I've encountered a problem when I try to retrieve the valid states of all features within an MSI package using the Microsoft.Deployment.WindowsInstaller.Installer class.
I want to copy the ValidStates property of each FeatureInfo within a Session. However when doing so I get a "Handle is in an invalid state." exception.
If I print each of these values out using Console.WriteLine() or step through the code in Visual Studio there is no exception.
I am at a loss as to what is preventing me from doing this.
Thanks in advance!
My Code:
var featureDictionary = new Dictionary<string, string[]>();
if (string.IsNullOrWhiteSpace(mPath))
return featureDictionary;
try
{
Installer.SetInternalUI(InstallUIOptions.Silent);
using (var session = Installer.OpenPackage(mPath, true))
{
foreach (var feature in session.Features)
{
try
{
var states = feature.ValidStates.Select((state) => state.ToString());
featureDictionary.Add(feature.Name, states.ToArray());
}
catch (InstallerException ex)
{
Debug.WriteLine(ex.Message);
}
}
}
}
catch (InstallerException) { }
return featureDictionary;
The basic problem appears to be that you are opening the MSI as a file. Since you haven't posted its declaration, or how it is set, I'm assuming that mpath means it's a path to the file. Your OpenPackage method parameters seem to indicate this too. You're getting that error because you are trying to open the MSI file as a file during the actual install, and failing.
The way to get hold of the database for the running install is to use Session.Database.
You can't open the running MSI as a file during the install perhaps for the same reason you can't run an MSI file that you have open with Orca, a simple file sharing violation. When you step through with Visual Studio you're simply accessing the static file and getting default values and the file isn't being used for an install. The other issue is that there can only be one Session object per process (as the OpenPackage docs say) and you are attempting to get a second one while there is already a Session object associated with the handle of the install.
As a custom action it needs to be sequenced after CostFinalize.
Windows Installer conditional expressions such as !feature-state will tell you what state the feature is in, because it's usually better to avoid code where Windows Installer will just give you the answer.

CRM 2013 - Qualifuy a Quote To Order Issue

In CRM 2013, when a Lead is set as Qualified by the user, the lead is converted into a Opportunity. When this happens a record is also created in the entity Contact and Account. When a plugin is involved in the process it is necessary in the plugin to use the following code
if (context.MessageName.ToLower() == "create" && entity.Attributes.Contains("originatingleadid") && entity["originatingleadid"] != null)
{
return;
}
else
{
//plugin code
}
So that the plugin only executes when a contact / account is created and not when a lead is being converted to a opportunity
My question is how is this achievable when a Quote is being converted to a Order as when I am doing this process my plugin for Order is being activated and is throwing a business process error as 'the given key is not present in the dictionary'
You should create a plugin which is registered to Pre/Post Operation of Order creation. Then, in your plugin in you should check for quoteid:
if (entity.Attributes.Contains("quoteid") &&
entity["quoteid"] != null)
{
return;
}
else
{
//plugin code
}
Hope it helps!

Attempted to read or write protected memory in a Windows Forms app

As a preface, I've looked at every StackOverflow question matched by searching for this error (25 of them or so), and none of them seemed to address the problem I'm having.
I'm building up a PermissionsDialog that inherits from System.Windows.Form. Within the method that calls dialogPermissions.ShowDialog() I am retrieving some Role objects from the database and loading them into a couple of ListBoxes. That was working just fine, but now I need to override one of the properties of the Role objects I'm adding to the listboxes using this pseudocode process:
iterate over the List of Roles
look up a matching item out of a List of Profiles using List<T>.Find()
look up a property on the Profile
build up a new Role and set the Name property as needed
add the Role to a list of Roles for the PermissionsDialog
All of that goes smoothly, but when I call dialogPermissions.ShowDialog() the underlying framework code throws an AccessViolationException.
Here is what I believe to be the relevant code:
List<UserProfile> userProfiles = new List<UserProfile>();
List<Role> allRoles = new List<Role>();
dialogData.AllRoles = new List<Role>();
using (var objectContext = this.SiteContext.CreateObjectContext())
{
userProfiles = rs.UserProfiles.FindAll().ToList();
allRoles = rs.Roles.FindAll();
}
foreach (Role role in allRoles.Where(role => role.BuiltInId == (int)BuiltInRoleId.UserProfileRole)) {
var userProfile = userProfiles.Find(up => role.Id == up.Id);
var roleToAdd = new Role {
BuiltInId = role.BuiltInId,
Description = role.Description,
DirectoryEntry = role.DirectoryEntry,
Id = role.Id,
IsBuiltInRole = role.IsBuiltInRole,
Name = null != profile ? profile.DisplayName:role.Name
};
dialogData.AllRoles.Add(roleToAdd);
}
My suspicion is that this is somehow a deferred execution issue, but triggering execution by calling ToList() on dialogData.AllRoles before calling ShowDialog() doesn't resolve the issue. When I replace profile.DisplayName with a constant string I do not get the error.
Any clues what's going on under the covers here, or how to find out what's going on, or how to approach the problem differently so I can avoid it? All suggestions welcome ;-)
CONCLUSION
So here's the actual issue, I think:
Setting the Name property of the Role to null is just fine, but when the dialog tries to create a ListBoxItem out of the Role and uses the Role.Name property for the ListBoxItem's Content property (which is an Object), that can't be set as null and throws down in the framework code that's building up the dialog. Checking to make sure I had a value in there fixes the issue.
Seems like s strange exception to throw, but there you have it....
You test for profile != null, but don't test for profile.DisplayName != null so that's the first thing that comes to mind from just looking at your sample.
Standard exception finder is to go to Debug\Exceptions and check the box for break on thrown so you can see all the state when the exception is thrown.
You can stare at this code for a week and never find the reason for the AccessViolationException. Managed code does not die from processor faults like this one.
You'll need to dig out as much info you can get from the actual exception. That requires first of all that you enable unmanaged code debugging so that you can see the stack frames in the native code. Project + Properties, Debug tab, tick the "Enabled unmanaged code debugging" option.
Next, you want to make sure that you have .pdb file for any of the native Windows DLLs. Including the ones for Active Directory, somewhat suspect in this case. Tools + Options, Debugging, Symbols and enable the Microsoft Symbol Server. Press F1 if you have an older version of Visual Studio that doesn't make it a simple checkbox click.
Reproduce the crash, the call stack should give you a good hint what native code is suspect. Post it in your question if you can't make hay of it.

Use C# to interact with Windows Update

Is there any API for writing a C# program that could interface with Windows update, and use it to selectively install certain updates?
I'm thinking somewhere along the lines of storing a list in a central repository of approved updates. Then the client side applications (which would have to be installed once) would interface with Windows Update to determine what updates are available, then install the ones that are on the approved list. That way the updates are still applied automatically from a client-side perspective, but I can select which updates are being applied.
This is not my role in the company by the way, I was really just wondering if there is an API for windows update and how to use it.
Add a Reference to WUApiLib to your C# project.
using WUApiLib;
protected override void OnLoad(EventArgs e){
base.OnLoad(e);
UpdateSession uSession = new UpdateSession();
IUpdateSearcher uSearcher = uSession.CreateUpdateSearcher();
uSearcher.Online = false;
try {
ISearchResult sResult = uSearcher.Search("IsInstalled=1 And IsHidden=0");
textBox1.Text = "Found " + sResult.Updates.Count + " updates" + Environment.NewLine;
foreach (IUpdate update in sResult.Updates) {
textBox1.AppendText(update.Title + Environment.NewLine);
}
}
catch (Exception ex) {
Console.WriteLine("Something went wrong: " + ex.Message);
}
}
Given you have a form with a TextBox this will give you a list of the currently installed updates. See http://msdn.microsoft.com/en-us/library/aa387102(VS.85).aspx for more documentation.
This will, however, not allow you to find KB hotfixes which are not distributed via Windows Update.
The easiest way to do what you want is using WSUS. It's free and basically lets you setup your own local windows update server where you decide which updates are "approved" for your computers. Neither the WSUS server nor the clients need to be in a domain, though it makes it easier to configure the clients if they are. If you have different sets of machines that need different sets of updates approved, that's also supported.
Not only does this accomplish your stated goal, it saves your overall network bandwidth as well by only downloading the updates once from the WSUS server.
If in your context you're allowed to use Windows Server Update Service (WSUS), it will give you access to the Microsoft.UpdateServices.Administration Namespace.
From there, you should be able to do some nice things :)
P-L right. I tried first the Christoph Grimmer-Die method, and in some case, it was not working. I guess it was due to different version of .net or OS architecture (32 or 64 bits).
Then, to be sure that my program get always the Windows Update waiting list of each of my computer domain, I did the following :
Install a serveur with WSUS (may save some internet bandwith) : http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=5216
Add all your workstations & servers to your WSUS server
Get SimpleImpersonation Lib to run this program with different admin right (optional)
Install only the administration console component on your dev workstation and run the following program :
It will print in the console all Windows updates with UpdateInstallationStates.Downloaded
using System;
using Microsoft.UpdateServices.Administration;
using SimpleImpersonation;
namespace MAJSRS_CalendarChecker
{
class WSUS
{
public WSUS()
{
// I use impersonation to use other logon than mine. Remove the following "using" if not needed
using (Impersonation.LogonUser("mydomain.local", "admin_account_wsus", "Password", LogonType.Batch))
{
ComputerTargetScope scope = new ComputerTargetScope();
IUpdateServer server = AdminProxy.GetUpdateServer("wsus_server.mydomain.local", false, 80);
ComputerTargetCollection targets = server.GetComputerTargets(scope);
// Search
targets = server.SearchComputerTargets("any_server_name_or_ip");
// To get only on server FindTarget method
IComputerTarget target = FindTarget(targets, "any_server_name_or_ip");
Console.WriteLine(target.FullDomainName);
IUpdateSummary summary = target.GetUpdateInstallationSummary();
UpdateScope _updateScope = new UpdateScope();
// See in UpdateInstallationStates all other properties criteria
_updateScope.IncludedInstallationStates = UpdateInstallationStates.Downloaded;
UpdateInstallationInfoCollection updatesInfo = target.GetUpdateInstallationInfoPerUpdate(_updateScope);
int updateCount = updatesInfo.Count;
foreach (IUpdateInstallationInfo updateInfo in updatesInfo)
{
Console.WriteLine(updateInfo.GetUpdate().Title);
}
}
}
public IComputerTarget FindTarget(ComputerTargetCollection coll, string computername)
{
foreach (IComputerTarget target in coll)
{
if (target.FullDomainName.Contains(computername.ToLower()))
return target;
}
return null;
}
}
}

Categories

Resources