The specified program requires a new version of Windows. (Exception from HRESULT:0x8007047E)

Scenario:
I have a custom list definition based on which I want to create some lists and associate OOB approval workflow with them for content approval.

In the schema.xml file for my custom list definition (based documents library i.e., basetype=1), I set ModeratedList=“True” attribute among others (see in the code snippet below)

<List xmlns:ows="Microsoft SharePoint" Title="TestList" Direction="$Resources:Direction;" Url="TestList" BaseType="1"
xmlns="http://schemas.microsoft.com/sharepoint/" EnableContentTypes="TRUE" EnableMinorVersions="TRUE" VersioningEnabled="TRUE" DraftVersionVisibility="2" ModeratedList="TRUE”>

Now, I added a ListAdded event receiver that listens to my custom list definition,so whenever any list is created based on my custom list definition, the OOB approval workflow should be associated with the list.

Here is the code snippet that associates the OOB approval workflow with the list


///
/// Associate OOB Approval workflow with the list
///
///
public static void AssociateApprovalWorklowWithList(SPList list)
{
  //variables
  Guid listId = list.ID;
  Guid webId = list.ParentWeb.ID;
  Guid siteId = list.ParentWeb.Site.ID;
  SPSite site = null;
  SPWeb web = null;

  try
  {
    site = new SPSite(siteId);
    web = site.OpenWeb(webId);
    bool allowUnsafeCurrent = web.AllowUnsafeUpdates;
    web.AllowUnsafeUpdates = true;
    list = web.Lists[listId];

    //Get Approval Workflow Template Base Id
    Guid workflowBaseId = new Guid("8ad4d8f0-93a7-4941-9657-cf3706f00"+ web.Language.ToString("X"));

    //If workflow is already associated, don't re-associate
    if (list.WorkflowAssociations.GetAssociationByBaseID(workflowBaseId) != null)
       return;

    //check if workflows feature is activated, if not activate the feature
    SPFeature workflowsFeature = web.Site.Features[WorkflowId];

    if (workflowsFeature == null)
      web.Site.Features.Add(WorkflowId);

    // Get workflow history and task list
    SPList historyList = EnsureHistoryList(web);
    SPList taskList = EnsureTasksList(web);
    string workflowAssociationName = string.Format("{0} - Approval", list.Title);
    SPWorkflowTemplate workflowTemplate = web.WorkflowTemplates.GetTemplateByBaseID(workflowBaseId);
    if (workflowTemplate == null)
    {
     //Log exception
     return;
    }

    workflowTemplate.AllowManual = false;

    // Create workflow association
    SPWorkflowAssociation workflowAssociation = SPWorkflowAssociation.CreateListAssociation(workflowTemplate,
    workflowAssociationName, taskList, historyList);

    var associationDataXml = XElement.Parse(workflowAssociation.AssociationData);
    // Add workflow association to my list
    list.WorkflowAssociations.Add(workflowAssociation);

    //Set workflow association data
    workflowAssociation.AssociationData = Add_Association_Data(web, associationDataXml);

    // Enable workflow
    if (!workflowAssociation.Enabled)
        workflowAssociation.Enabled = true;

    if (list.DraftVersionVisibility != DraftVisibilityType.Approver)
       list.DraftVersionVisibility = DraftVisibilityType.Approver;

    list.WorkflowAssociations.Update(workflowAssociation);
    list.DefaultContentApprovalWorkflowId = workflowAssociation.Id;
    list.Update();
    web.AllowUnsafeUpdates = allowUnsafeCurrent;
  }
  catch (Exception ex)
  {
    //Log Exception
  }
  finally
  {
    //Dispose the objects
    if (web != null)
       web.Dispose();
    if (site != null)
       site.Dispose();
   }
}

For the explanation of retrieving the workflow template id (line#23), please refer to my previous post here Associating OOB Approval workflow with custom list for different Locale (LCIDs)

Problem:
Once I have deployed the solution and everything is hooked up, I create the list based on my custom list definition and the process ends with the following exception:

PastedGraphic-1

But when I go to the site, the list is created and workflow is associated properly. 

Solution:
After narrowing down the problem, I removed the ModeratedList attribute from List element in schema.xml file for custom list definition (shown in the first code snippet), enabled the content approval for the list in the code and redeployed the solution and everything worked !

Here is the code for enabling content approval through code (add this in the function defined above after setting DraftVersionVisibility property)


  if(!list.EnableModeration)
    list.EnableModeration = true;

Cheers

Associating OOB Approval workflow with custom list for different Locale (LCIDs)

Scenario: I have a custom web template that is used to provision the site. Among other things (branding etc), a custom documents library is provisioned whenever a new site is created (also based on my custom documents library template). Besides creating the custom library, I need to associate the OOB Approval workflow with the library that will be used as a publishing workflow. So if any user publishes the major version of the document it goes through the approval process.

Problem/Limitation: Associating the workflow with any list or library is fairly straight forward. Here are the steps:

  1. Fetch the workflow template from parent web where list exist
  2. Get the target list
  3. Create the workflow association
  4. Check if workflow is already associated with the list
  5. If workflow is not associated, add the workflow association to the list

Here is the code snippet for performing this task.

private void AssociateApprovalWorklowWithList(SPList list)
{
SPWeb web = list.ParentWeb;
SPSite site = web.Site;
try
{
bool allowUnsafeCurrent = web.AllowUnsafeUpdates;
web.AllowUnsafeUpdates = true;

//Associate the approval workflow with libraries
//based on Custom Documents template only
if (list != null && list.BaseTemplate.ToString() == "10050")
{
//Get Approval Workflow Template Base Id
Guid workflowBaseId = new Guid("8ad4d8f0-93a7-4941-9657-cf3706f00409");

//If workflow is already associated, don't re-associate
if (list.WorkflowAssociations.GetAssociationByBaseID(workflowBaseId) != null)
return;

// Get workflow history and task list
SPList historyList = EnsureHistoryList(web);
SPList taskList = EnsureTasksList(web);

//set the name of workflow association
string workflowAssociationName = string.Format("{0} - Approval", list.Title);

//Get workflow template by Base Id
SPWorkflowTemplate workflowTemplate = web.WorkflowTemplates.GetTemplateByBaseID(workflowBaseId);
if (workflowTemplate == null)
{
//Log - no template found and return
return;
}

// Create workflow association
SPWorkflowAssociation workflowAssociation =
SPWorkflowAssociation.CreateListAssociation(workflowTemplate, workflowAssociationName,
taskList, historyList);

// Add workflow association to custom list
list.WorkflowAssociations.Add(workflowAssociation);

workflowAssociation.AssociationData = String.Empty;

//.. details omitted
list.WorkflowAssociations.Update(workflowAssociation);
list.DefaultContentApprovalWorkflowId = workflowAssociation.Id;
list.Update();

web.AllowUnsafeUpdates = allowUnsafeCurrent;
}
}
catch (Exception ex)
{
//Log exception
}
finally
{
//Dispose objects
}
}

The above code snippet will work just fine. But there is a little problem.

What if the site is created on language other than English? One case ask, why would there be any problem as we are fetching the workflow template by Base Id. Well, it turned out that the Base Id is not the same across different languages, it varies. So it failed if site has different locale.

There is also another method GetWorkflowTemplateByName but it didn’t work out for me as well, even though I set the right culture.. So that means, we can not use both the methods.

Solution: Trying both the methods to get the template didn’t work and I didn’t get anything by googling it either. So the only way out of this problem was the following:

Looking at the BaseIds of different sites, a pattern emerged. Let’s examine the BaseIds from English and Swedish sites, as we saw previously

8ad4d8f0-93a7-4941-9657-cf3706f00409 (English, LCID: 1033)
8ad4d8f0-93a7-4941-9657-cf3706f0041D (Swedish, LCID: 1053)

If you look closely, almost entire Id is same except last three digits Let’s take some more examples:

8ad4d8f0-93a7-4941-9657-cf3706f00415 (Polish, LCID: 1045)
8ad4d8f0-93a7-4941-9657-cf3706f00406 (Danish, LCID: 1030)

as you can see, only the last three digits vary from language to language.

Now take the LCID and convert it to hexadecimal representation (thanks to my colleague Matthias Einig for breaking this code 😉 )

Lets say,  for:

LCID = 1033 => hex = 409

LCID = 1053 => hex = 41d

LCID = 1045 => hex = 415

So, it became clear that the last three digits that differ are basically the hexadecimal representation of LCID. Having identified this pattern, I simply converted the LCID to hex value and there we have it, the BaseId for approval workflow template for current language

So,  if we change the line #15 in our code snippet above to the following:

Guid workflowBaseId = new Guid("8ad4d8f0-93a7-4941-9657-cf3706f00"+ web.Language.ToString("X"));

With this change, now no matter which locale the site has, it will construct the right workflow template id and will fetch the workflow template for association.

Hope it helps some folks out there 🙂