Subscribe

UiPath Marketplace

UiPath Marketplace Guide

Licensing Implementation

This documentation section guides you through the process of embedding the licensing SDK library into your Activity package.

📘

Pre-requisites:

Please follow the next steps depending on whether your package was developed with or without the UiPath Activity Creator:

🚧

Your VS Solution needs to be at least .net461 to start.

In case your package was developed using the UiPath Activity Creator

  1. In the ExecuteAsync method of each of your activities, add the validation check unless you have a Scope Activity scenario.
    If you do have a Scope Activity scenario, you can add the validation check only to that activity if all your activities are subject to licensing.
protected override async Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken)
        {
            // Inputs
            var firstNumber = FirstNumber.Get(context);
            var secondNumber = SecondNumber.Get(context);

            // License validation
            #if !DEBUG
                //if your package is MyCompany.MyPackageName.Activities.1.0.0.nupkg
                await Validator.ValidateAsync(context, "MyCompany.MyPackageName.Activities");
            #endif

            ///////////////////////////
            // Add execution logic HERE
            ///////////////////////////

            // Outputs
            return (ctx) => {
            };
        }

📘

Note

  1. The context object can be passed only once and used only in the method it was passed into. For example, after the Validator.ValidateAsync call, it can not be used anymore in the ExecuteAsync method.
  2. Considering the above requirement, this also means that the input variables must be read before passing the context to the licensing method/library.
  1. Reference the Licensing.dll library into your .Activities project and add the using statement:

using UiPath.Marketplace.License.Validations;

  1. The setup is complete - build your package, and make sure that the Licensing.dll library is present inside the package contents (within the lib folder). Afterward, you can submit it on the Marketplace.

In case your package was not developed using the UiPath Activity Creator

If your initial activity looks like this:

using System;
using System.Activities;
using System.ComponentModel;

    namespace ClassMathCustomActivity
{
    public class SimpleFormula : CodeActivity
    {
        [Category("Input")]
        [RequiredArgument]
        public InArgument<double> FirstNumber { get; set; }

        [Category("Input")]
        public InArgument<double> SecondNumber { get; set; }

        [Category("Output")]
        public OutArgument<double> ResultNumber { get; set; }

        protected override void Execute(CodeActivityContext context)
        {
            var firstNumber = FirstNumber.Get(context);
            var secondNumber = SecondNumber.Get(context);
            var result = System.Math.Pow(firstNumber + secondNumber, 2);
            ResultNumber.Set(context, result);
        }
    }
}

The most important change that you need to make is to the execute method, which needs to be an ExecuteAsync.

The proposed solution is to create AsyncTaskCodeActivity.cs class that should look like this:

using System;
using System.Activities;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
namespace ClassMathCustomActivity
{
  internal struct AsyncTaskCodeActivityImplementation : IDisposable
  {
    private CancellationTokenSource _cancellationTokenSource;
    private bool _tokenDisposed;
    public void Cancel()
    {
      if (!_tokenDisposed)
      {
        _cancellationTokenSource?.Cancel();
        _cancellationTokenSource?.Dispose();
        _tokenDisposed = true;
      }
    }
    public IAsyncResult BeginExecute(AsyncCodeActivityContext context
      , Func<AsyncCodeActivityContext, CancellationToken, Task<Action<AsyncCodeActivityContext>>> onExecute
      , AsyncCallback callback, object state)
    {
      if (!_tokenDisposed)
      {
        _cancellationTokenSource?.Dispose();
      }
      _cancellationTokenSource = new CancellationTokenSource();
      _tokenDisposed = false;
      TaskCompletionSource<Action<AsyncCodeActivityContext>> taskCompletionSource = new TaskCompletionSource<Action<AsyncCodeActivityContext>>(state);
      Task<Action<AsyncCodeActivityContext>> task = onExecute(context, _cancellationTokenSource.Token);
      var cancellationTokenSource = _cancellationTokenSource;
      task.ContinueWith(t =>
      {
        if (t.IsFaulted)
        {
          taskCompletionSource.TrySetException(t.Exception.InnerException);
        }
        else if (t.IsCanceled || cancellationTokenSource.IsCancellationRequested)
        {
          taskCompletionSource.TrySetCanceled();
        }
        else
        {
          taskCompletionSource.TrySetResult(t.Result);
        }
        callback?.Invoke(taskCompletionSource.Task);
      });
      return taskCompletionSource.Task;
    }
    public void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
      Task<Action<AsyncCodeActivityContext>> task = (Task<Action<AsyncCodeActivityContext>>)result;
      if (task.IsFaulted)
      {
        ExceptionDispatchInfo.Capture(task.Exception.InnerException).Throw();
      }
      if (task.IsCanceled)
      {
        context.MarkCanceled();
      }
      task.Result?.Invoke(context);
      if (!_tokenDisposed)
      {
        _cancellationTokenSource?.Dispose();
        _tokenDisposed = true;
      }
    }
    private bool _disposed; // To detect redundant calls
    public void Dispose()
    {
      if (!_disposed)
      {
        if (!_tokenDisposed)
        {
          if (_cancellationTokenSource != null)
            _cancellationTokenSource.Dispose();
          _tokenDisposed = true;
        }
        _disposed = true;
      }
    }
  }
  public abstract class AsyncTaskCodeActivity : AsyncCodeActivity, IDisposable
  {
    private AsyncTaskCodeActivityImplementation _impl = new AsyncTaskCodeActivityImplementation();
    protected override void Cancel(AsyncCodeActivityContext context)
    {
      _impl.Cancel();
      base.Cancel(context);
    }
    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
      return _impl.BeginExecute(context, ExecuteAsync, callback, state);
    }
    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
      _impl.EndExecute(context, result);
    }
    protected abstract Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken);
    #region IDisposable Support
    private bool _disposed = false; // To detect redundant calls
    protected virtual void Dispose(bool disposing)
    {
      if (!_disposed)
      {
        if (disposing)
        {
        }
        _impl.Dispose(); //structs are not garbage collected so they fit in the unmanaged bucket
        _disposed = true;
      }
    }
    public void Dispose()
    {
      // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
      Dispose(true);
      GC.SuppressFinalize(this);
    }
    #endregion
  }
  public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T>, IDisposable
  {
    private AsyncTaskCodeActivityImplementation _impl = new AsyncTaskCodeActivityImplementation();
    protected override void Cancel(AsyncCodeActivityContext context)
    {
      _impl.Cancel();
      base.Cancel(context);
    }
    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
      return _impl.BeginExecute(context, ExecuteAsync, callback, state);
    }
    protected override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
      _impl.EndExecute(context, result);
      return Result.Get(context);
    }
    protected abstract Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken);
    #region IDisposable Support
    private bool _disposed = false; // To detect redundant calls
    protected virtual void Dispose(bool disposing)
    {
      if (!_disposed)
      {
        if (disposing)
        {
        }
        _impl.Dispose(); //structs are not garbage collected so they fit in the unmanaged bucket
        _disposed = true;
      }
    }
    public void Dispose()
    {
      // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
      Dispose(true);
      GC.SuppressFinalize(this);
    }
    #endregion
  }
}

The changed activity would need to look like this:

using System;
using System.Threading.Tasks;
using UiPath.Marketplace.License.Validations;
using System.ComponentModel;
using System.Activities;
using System.Threading;
namespace ClassMathCustomActivity
{
  public class SimpleFormula : AsyncTaskCodeActivity
  {
    [Category("Input")]
    [RequiredArgument]
    public InArgument<double> FirstNumber { get; set; }
    [Category("Input")]
    public InArgument<double> SecondNumber { get; set; }
    [Category("Output")]
    public OutArgument<double> ResultNumber { get; set; }
    protected override async Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken)
    {
      var firstNumber = FirstNumber.Get(context);
      var secondNumber = SecondNumber.Get(context);
      // check for license after reading input arguments
      #if !DEBUG
                //if your package is MyCompany.MyPackageName.Activities.1.0.0.nupkg
                await Validator.ValidateAsync(context, "MyCompany.MyPackageName.Activities");
      #endif
      return (ctx) =>
      {
        var result = System.Math.Pow(firstNumber + secondNumber, 2);
        ResultNumber.Set(ctx, result);
      };
    }
  }
}

📘

Note:

It is important for line 24 to be executed right after reading all the input arguments. In the above example, lines 20 and 21 are used for reading the input arguments, and lines 26 to 30 are used for setting the output argument.

One last thing - when creating your .nupkg package, please make sure that the Licensing.dll library is present inside the package contents (within the lib folder).

FAQs

What happens after you have implemented these changes?
After you have implemented the changes and built the .nupkg, you need to submit it to the Marketplace for publishing.

Can I obtain the license file for testing purposes?
No, in order to test the activities, you can use the C# preprocessor directive for debugging (#if !DEBUG)

How will customers be obtaining the license file?
After someone made a purchase, a license .lic file will be generated and made available for them on the Marketplace. They will be able to download the file and then follow the necessary steps for using the activities.

Updated 28 days ago


Licensing Implementation


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.