- Release Notes
- Overview
- Getting Started
- Marketplace Vendors
- Marketplace Customers
- Publishing Guidelines
- Publishing Guidelines for Ready-to-go Automations
- Publishing Guidelines for Solution Accelerators
- Publishing Guidelines for Integration Service Connectors
- Security & IP Protection
- Other UiPath Listings
- Node-RED
- Setup
- Teams
- Microsoft Teams Scope
- Create Team
- Create Team From Group
- Get Team
- Get Teams
- Channels
- Create Channel
- Delete Channel
- Get Channel
- Get Channels
- Update Channel
- Chats
- Get Chat
- Get Chats
- Get Chat Members
- Messages
- Get Message
- Get Messages
- Get Message Replies
- Reply To Message
- Send Message
- Events
- Create Event
- Delete Event
- Get Event
- Get Events
- Users
- Get User Presence
- How It Works
- Technical References
- Get Started
- About
- Setup
- Technical References
- Azure Form Recognizer Scope
- Activities
- Analyze Form
- Analyze Form Async
- Get Analyze Form Result
- Analyze Receipt
- Analyze Receipt Async
- Get Analyze Receipt Result
- Analyze Layout
- Analyze Layout Async
- Get Analyze Layout Result
- Train Model
- Get Models
- Get Model Keys
- Get Model Info
- Delete Model
- Connectors
- How to Create Activities
- Build Your Integration
Licensing Implementation
This documentation section guides you through the process of embedding the licensing SDK library into your Activity package.
Please follow the next steps depending on whether your package was developed with or without the UiPath Activity Creator:
The onus of licensing implementation for .NET 6 or Windows compatible custom activities lies with the vendor. Marketplace dosen't provide .NET6 compatible licensing library. It provides Windows-Legacy compatible licensing library.
-
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) => { }; }
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:- 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.
- Considering the above requirement, this also means that the input variables must be read before passing the context to the licensing method/library.
-
Reference the Licensing.dll library into your .Activities project and add the using statement:
using UiPath.Marketplace.License.Validations;
- 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.
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);
}
}
}
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
}
}
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);
};
}
}
}
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);
};
}
}
}
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).
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?
#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.