sharepoint-2010sharepoint-designersharepoint-workflowsharepoint-api

Debugging a SharePoint 2010 Custom Activity


I'm trying to build a custom activity for SharePoint 2010 to be used in a work flow. In the designer, I can see the activity in Action drop down menu on the ribbon under "Custom Activities". However, the element never materializes in the designer and I am at a loss to figure out how to debug this problem.

I have tried putting a Debugger.Break() into the event handlers but that results in an entry in the Application Event Log that says An unhandled exception ('Launch for user') occurred in w3wp.exe [xxxx]. Just-In-Time debugging this exception failed with the following error: Not enough storage is available to complete this operation. Additionally, attaching the debugger to the w3wp.exe process doesn't stop on any break points.

I've turned on verbose logging on inside of SharePoint 2010 (and the logging is quite verbose!) but it doesn't seem to contain any information about the component or why the designer isn't loading the component.

Here is the relevant snippet of code for this very basic activity. What am I doing wrong here?

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WorkflowActions;

namespace SharePoint.Activities.IO
{
    public partial class CopyFile : SequenceActivity
    {
        public CopyFile()
        {
            InitializeComponent();
        }

        public static DependencyProperty SourceDirectoryProperty = DependencyProperty.Register("SourceDirectory", typeof(string), typeof(CopyFile));
        public static DependencyProperty TargetDirectoryProperty = DependencyProperty.Register("TargetDirectory", typeof(string), typeof(CopyFile));
        public static DependencyProperty ResultProperty = DependencyProperty.Register("Result", typeof(string), typeof(CopyFile));
        public static DependencyProperty CreateDateProperty = DependencyProperty.Register("CreateDate", typeof(DateTime), typeof(CopyFile));
        public static DependencyProperty CompletionDateProperty = DependencyProperty.Register("CompletionDate", typeof(DateTime), typeof(CopyFile));
        public static DependencyProperty WFContextProperty = DependencyProperty.Register("WFContext", typeof(WorkflowContext), typeof(CopyFile));

        [Description("Path the files to perform the operation on")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string SourceDirectory
        {
            get { return (string)GetValue(SourceDirectoryProperty); }
            set { SetValue(SourceDirectoryProperty, value); }
        }
        [Description("Path the files are copied to")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string TargetDirectory
        {
            get { return (string)GetValue(TargetDirectoryProperty); }
            set { SetValue(TargetDirectoryProperty, value); }
        }
        [Description("Once the the operation completes, this is set to OK or the exception information")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string Result
        {
            get { return (string)GetValue(ResultProperty); }
            set { SetValue(ResultProperty, value); }
        }
        [Description("When the request was created")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public DateTime CreateDate
        {
            get { return (DateTime)GetValue(CreateDateProperty); }
            set { SetValue(CreateDateProperty, value); }
        }
        [Description("When execution stoped")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public DateTime CompletionDateDate
        {
            get { return (DateTime)GetValue(CompletionDateProperty); }
            set { SetValue(CompletionDateProperty, value); }
        }

        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public WorkflowContext WFContext { get { return (WorkflowContext)GetValue(WFContextProperty); } set { SetValue(WFContextProperty, value); } }

        protected override void Initialize(IServiceProvider provider)
        {
            TraceIn();
            base.Initialize(provider);
            TraceOut();
        }


        protected override void Uninitialize(IServiceProvider provider)
        {
            TraceIn();
            base.Uninitialize(provider);
            TraceOut();
        }
        public override string ToString()
        {
            TraceIn();
            var result = base.ToString();
            TraceOut();
            return result;
        }
        public override int GetHashCode()
        {
            TraceIn();
            // ReSharper disable BaseObjectGetHashCodeCallInGetHashCode
            var result = base.GetHashCode();
            // ReSharper restore BaseObjectGetHashCodeCallInGetHashCode
            TraceOut();
            return result;
        }
        public override bool Equals(object obj)
        {
            TraceIn();
            // ReSharper disable BaseObjectEqualsIsObjectEquals
            var result = base.Equals(obj);
            // ReSharper restore BaseObjectEqualsIsObjectEquals
            TraceOut();
            return result;
        }
        //This code causes the compiler to blow up! :)
        //protected override void Dispose(bool disposing)
        //{
        //    TraceIn();
        //    base.Dispose(disposing);
        //}
        protected override void SetBoundValue(ActivityBind bind, object value)
        {
            TraceIn();
            base.SetBoundValue(bind, value);
            TraceOut();
        }
        protected override void OnWorkflowChangesCompleted(ActivityExecutionContext executionContext)
        {
            TraceIn();
            base.OnWorkflowChangesCompleted(executionContext);
            TraceOut();
        }
        protected override void OnSequenceComplete(ActivityExecutionContext executionContext)
        {
            TraceIn();
            base.OnSequenceComplete(executionContext);
            TraceOut();
        }
        protected override void OnListChanging(ActivityCollectionChangeEventArgs e)
        {
            TraceIn();
            base.OnListChanging(e);
            TraceOut();
        }
        protected override void OnListChanged(ActivityCollectionChangeEventArgs e)
        {
            TraceIn();
            base.OnListChanged(e);
            TraceOut();
        }
        protected override void OnClosed(IServiceProvider provider)
        {
            TraceIn();
            base.OnClosed(provider);
            TraceOut();
        }
        protected override void OnActivityExecutionContextUnload(IServiceProvider provider)
        {
            TraceIn();
            base.OnActivityExecutionContextUnload(provider);
            TraceOut();
        }
        protected override void OnActivityExecutionContextLoad(IServiceProvider provider)
        {
            TraceIn();
            base.OnActivityExecutionContextLoad(provider);
            TraceOut();
        }
        protected override void OnActivityChangeRemove(ActivityExecutionContext executionContext, Activity removedActivity)
        {
            TraceIn();
            base.OnActivityChangeRemove(executionContext, removedActivity);
            TraceOut();
        }
        protected override void OnActivityChangeAdd(ActivityExecutionContext executionContext, Activity addedActivity)
        {
            TraceIn();
            base.OnActivityChangeAdd(executionContext, addedActivity);
            TraceOut();
        }
        protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
        {
            TraceIn();
            var result = base.HandleFault(executionContext, exception);
            TraceOut();
            return result;
        }
        protected override object GetBoundValue(ActivityBind bind, Type targetType)
        {
            TraceIn();
            var result = base.GetBoundValue(bind, targetType);
            TraceOut();
            return result;
        }
        protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext)
        {
            TraceIn();
            var result = base.Cancel(executionContext);
            TraceOut();
            return result;
        }
        protected override void InitializeProperties()
        {
            TraceIn();
            base.InitializeProperties();
            TraceOut();
        }
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            TraceIn();

            var isSiteAdmin = false;
            CreateDate = DateTime.Now;
            try
            {
                // Needs administrative credentials to get the web application properties ino
                SPSecurity.RunWithElevatedPrivileges(delegate
                                                         {
                                                             using (var site = new SPSite(WFContext.Site.ID))
                                                             {
                                                                 using (var web = site.AllWebs[WFContext.Web.ID])
                                                                 {
                                                                     isSiteAdmin = web.CurrentUser.IsSiteAdmin;
                                                                     if (string.IsNullOrEmpty(SourceDirectory)) throw new ArgumentException("SourceDirectory cannot be null or empty");
                                                                     if (!Directory.Exists(SourceDirectory)) throw new DirectoryNotFoundException("Could not find source directory: \"" + SourceDirectory + "\"");

                                                                     if (string.IsNullOrEmpty(TargetDirectory)) throw new ArgumentException("TargetDirectory cannot be null or empty");
                                                                     if (!Directory.Exists(TargetDirectory)) throw new DirectoryNotFoundException("Could not find target directory: \"" + TargetDirectory + "\"");
                                                                     // Do something 

                                                                 }
                                                             }
                                                         });

                Result = "OK";
            }
            catch (Exception ex)
            {
                Result = isSiteAdmin ? ex.ToString() : ex.Message;
            }
            CompletionDateDate = DateTime.Now;

            var result = base.Execute(executionContext);
            TraceOut();
            return result;
        }

        private static void TraceIn()
        {
            Trace("Entering");
        }

        private static void TraceOut()
        {
            Trace("Exiting");
        }

        private static void Trace(string direction)
        {
            Debugger.Launch();
            var stackTrace = new StackTrace(2, false);
            var frame = stackTrace.GetFrame(0);
            Debug.WriteLine(direction + " " + frame.GetMethod().Name);
        }
    }
}

With the tracing built in, any time I try to add the activity to the workflow in the designer, I see calls to GetHashCode() and Equals() and nothing else.

Here is the .actions file for it.

<?xml version="1.0" encoding="utf-8" ?>
<WorkflowInfo>
    <Actions
        Sequential="then"
        Parallel="and">
        <Action
            Name="IO Activities"
            ClassName="SharePoint.Activities.IO"
            Assembly="SharePoint.Activities.IO, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abfb622251cf6982"
            Category="Custom Actions"
            AppliesTo="all">
            <RuleDesigner
                Sentence="CopyFile %1">
                <FieldBind
                    Field="SourceDirectory,TargetDirectory,Result,CreateDate,CompletionDate"
                    Text="Copy the files"
                    DesignerType="CopyFile"
                    Id="1"/>
            </RuleDesigner>
            <Parameters>
                <Parameter
                    Name="SourceDirectory"
                    Type="System.String, mscorlib"
                    Direction="In"
                    DesignerType="StringBuilder"
                    Description="Directory where the source files are to move" />
                <Parameter
                    Name="TargetDirectory"
                    Type="System.String, mscorlib"
                    Direction="In"
                    DesignerType="StringBuilder"
                    Description="Directory where the files will be placed" />
                <Parameter
                    Name="Result"
                    Type="System.String, mscorlib"
                    Direction="Out" />
                <Parameter
                    Name="CreateDate"
                    Type="System.DateTime, mscorlib"
                    Direction="Out" />
                <Parameter
                    Name="CompletionDate"
                    Type="System.DateTime, mscorlib"
                    Direction="Out" />

                <Parameter 
                    Name="WFContext" 
                    Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" 
                    Direction="In" 
                    DesignerType="Hide" />
            </Parameters>
        </Action>
    </Actions>
</WorkflowInfo>

Solution

  • We never find a means to do debug the issue and it seems that SharePoint just fails silently.

    It turns out that the reason for the failure was simple, and rather embarrassing.

    <Action
        Name="IO Activities"
        ClassName="SharePoint.Activities.IO"
        Assembly="SharePoint.Activities.IO, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abfb622251cf6982"
        Category="Custom Actions"
        AppliesTo="all">
    

    Of interest is the ClassName="SharePoint.Activities.IO". What's that missing? The actual class name. It should read ClassName="SharePoint.Activities.IO.FileCopy". After going thing and making sure the class name was correct across the project in the web.config and the .actions file, the action now loads and renders in the designer.