RPA PAD (Power Automate Desktop) Component Development

RPA PAD (Power Automate Desktop) Component Development

Where there is a door, there is a world; now there is a door.

Last updated 5/30/2022 9:24 PM
蓝创精英团队
10 min read
Category
.NET
Tags
.NET C# RPA

This article is contributed by netizen Blue Creative Elite Team. Welcome to reprint and share.

Original Author: Blue Creative Elite Team

Original Link: https://blog.csdn.net/i2blue/article/details/125040323


Actually, the official documentation for PAD does not yet provide component-based or plugin-based development interfaces.

However, there are some like-minded friends, such as (Pan Chun), Mr. Pan, who are still very impressive in the RPA field.

As long as there is a door, there will be a world. Now there is already a door (after all, it's .NET Framework, so research and reference become much easier).

Component Development Environment

The default location for components is under this directory within the current application:

C:\Program Files (x86)\Power Automate Desktop\custom-modules

The application path should be determined based on your own installation.

Additionally, the plugin DLL needs to be code-signed.

By default, a personal signature is used and placed in the system's Trusted Root Certification Authorities.

If you have the budget, you can purchase a code signing certificate.

Currently, I don't have other good methods. There are roughly two types of signatures: domain certificate (SSL) and code signing. They are different and cannot be interchanged.

The directory structure is roughly as follows:

The Simplest Demo

Let's go through the development process step by step, and then summarize.

Create a Blank Solution

Create a new solution (blank solution)

Add a Component Library

The component DLL name must satisfy the following rules:

"?*.Modules.?*.dll",
"Modules.?*.dll"

The official component package looks like this:

Microsoft.Flow.RPA.Desktop.Modules.System.Actions.dll

So I will follow the official standard.

Name it YZG.Modules.HelloWorld.Actions (Note: Names like demo, test, etc., may cause recognition issues, so it is recommended to use special names.)

Hello World Component Project

Start with the usual Hello World.

Reference Development DLL Packages

Reference the following DLL packages from the installation directory C:\Program Files (x86)\Power Automate Desktop:

Microsoft.Flow.RPA.Desktop.Modules.SDK.dll
 Microsoft.Flow.RPA.Desktop.Modules.SDK.Extended.dll

If you encounter prompts for missing packages during development, you can also include them.

After referencing, the effect is as follows:

Add Hello World Logic

Here is the complete logic code:

[Action(Id = "SayHello")]
[Icon(Code = "EFF7")]
[Throws("MyError")]
public class SayHello : ActionBase
{
    [InputArgument]
    public string UserName { get; set; }

    [OutputArgument]
    public string Result { get; set; }
    public override void Execute(ActionContext context)
    {
        try
        {
            this.Result = $"{UserName}, China welcomes you! -{DateTime.Now}";
        }
        catch (ActionException ex)
        {
            throw new ActionException("MyError", ex.Message, ex.InnerException);
        }
    }
}

Add Internationalization Support

There is no need to add foreign languages; just add Chinese support.

First, add a Chinese resource file (you can also refer to the language pack under the official path to analyze the internal structure):

General Rules for Component Display

Where does the component name come from?

It comes from the AssemblyTitle in the assembly information. By default, this name is in English, but it can be localized.

Also, it's best that this name is different from the Action's name for easier display.

General Rules for Component Content Display

By splitting based on component name or type and class properties.

Add:

  1. _FriendlyName
  2. _Description
  3. _Summary

FriendlyName is the main name of each component, Description is like a tooltip, and Summary contains key information. The role is quite clear. Internally, template engine variables <PROPERTY_NAME_IN_UPPERCASE> are used to dynamically display information.

A general example is as follows:

Close_Connection_Description = "Handle for a new SQL connection"
Close_Connection_FriendlyName = "SQL connection"
Close_Description = "Close an open connection to the database"
Close_FriendlyName = "Close SQL connection"
Close_Summary = "Close SQL connection <CONNECTION>"
ConnectAndExecute_Description = "Connect to the database and execute SQL statement"
ConnectAndExecute_Summary = "<if(RESULT)>\r\nExecute SQL statement <STATEMENT> and store query result in <RESULT><else>\r\nExecute SQL statement <STATEMENT><endif>"
Connect_ConnectionString_Description = "Connection string used to connect to the database"
Connect_ConnectionString_FriendlyName = "Connection string"
Connect_Connection_Description = "Handle for a new SQL connection"
Connect_Connection_FriendlyName = "SQL connection"
Connect_Description = "Open a new connection to the database"
Connect_FriendlyName = "Open SQL connection"
Connect_Summary = "<if(CONNECTION)>\r\nOpen SQL connection <CONNECTIONSTRING> and store it in <CONNECTION><else>\r\nOpen SQL connection <CONNECTIONSTRING><endif>"
Database_Description = "Connect to the database and execute SQL statement"
Database_FriendlyName = "Database"
ErrorMessage_CannotConnect = "Cannot connect to data source"
ErrorMessage_CannotConnectError = "Cannot connect to data source {0}"
ErrorMessage_InvalidConnectionString = "Invalid connection string"
ErrorMessage_StatementError = "Error in SQL statement {0}"
ErrorMessage_UniniatializedConnection = "SQL connection is uninitialized. Please double-check that you have specified the correct SQL connection and that it is used after 'Open SQL connection' (not after it has been closed)"
Error_ConnectToDataSourceError_Description = "Indicates a problem connecting to the data source"
Error_ConnectToDataSourceError_FriendlyName = "Cannot connect to data source"
Error_InvalidConnectionStringError_Description = "Indicates the specified connection string is invalid"
Error_InvalidConnectionStringError_FriendlyName = "Invalid connection string"
Error_SqlStatementError_Description = "Indicates an error in the given SQL statement"
Error_SqlStatementError_FriendlyName = "Error in SQL statement"
ExecuteSqlStatement_ConnectionString_Description = "Connection string used to connect to the database"
ExecuteSqlStatement_ConnectionString_FriendlyName = "Connection string"
ExecuteSqlStatement_Connection_Description = "Handle for a new SQL connection"
ExecuteSqlStatement_Connection_FriendlyName = "SQL connection"
ExecuteSqlStatement_Description = "Connect to the database and execute SQL statement"
ExecuteSqlStatement_FriendlyName = "Execute SQL statement"
ExecuteSqlStatement_GetConnection_Description = "Specifies whether to create a new connection from a given connection string or to select an already opened connection"
ExecuteSqlStatement_GetConnection_FriendlyName = "Way to get connection"
ExecuteSqlStatement_Result_Description = "Result from the database, in the form of a data table containing rows and columns"
ExecuteSqlStatement_Result_FriendlyName = "Query result"
ExecuteSqlStatement_Statement_Description = "SQL statement to execute on the database"
ExecuteSqlStatement_Statement_FriendlyName = "SQL statement"
ExecuteSqlStatement_Timeout_Description = "Maximum time to wait for results from the database"
ExecuteSqlStatement_Timeout_FriendlyName = "Timeout"
Execute_Description = "Connect to the database and execute SQL statement"
Execute_Summary = "<if(RESULT)>\r\nExecute SQL statement <STATEMENT> on <CONNECTION> and store query result in <RESULT><else>\r\nExecute SQL statement <STATEMENT> on <CONNECTION><endif>"
GetSQLConnectionBy_ConnectionString_FriendlyName = "Connection string"
GetSQLConnectionBy_SQLConnectionVariable_FriendlyName = "SQL connection variable"
Message_SqlConnection = "SQL connection"
SqlConnectionHandle_FriendlyName = "SQL connection"
SqlConnectionHandle_FriendlyNamePlural = "SQL connections"

Based on the above information, we will now fill in the Chinese content for the Hello World program.

Actual Chinese Content

I added the following content:

Sign the Component Project

If you have the budget, obtain a code signing certificate. If not, follow my temporary self-signed certificate approach for now.

Create a Temporary Certificate

Create a new signature (make sure to run VS in administrator mode – start it as administrator):

Then, a pfx signature file is created:

Sign the Component DLL

Now we need to use the signtool.exe tool for signing. It comes with Visual Studio.

I will also provide it.

A batch signing script (default signature password is 123456):

Basically, only these two assemblies need signing; the referenced NuGet packages are not required.

Mainly YZG.Modules.HelloWorld.Actions.dll and zh-Hans\YZG.Modules.HelloWorld.Actions.resources.dll should be placed in the signing location:

Double-click the bat to sign:

The signing is successful. You can see the signature information by right-clicking on the DLL:

Install the Certificate on the Target Machine

If your certificate is purchased, installation is not required; it is automatically trusted. Otherwise, you still need to install the certificate.

Installing the certificate is very simple: double-click, enter the password, and select the appropriate location.

Click Next:

Next:

Select "Trusted Root Certification Authorities":

Then finish, confirm "Yes" and "OK".

Run the CMD command certmgr.msc to see your certificate under the specified group.

The certificate installation is now complete.

Component Deployment

Prerequisite: The application service must be exited:

Otherwise, the DLL will be locked.

Then, place the signed project into the designated plugin directory under the installation directory, roughly as shown below.

Also, note that since this is the C drive, there may be permission issues. It is recommended to install to another drive if possible.

Then, run the PAD application, create a new flow, or edit any flow.

If the following issue occurs, it means the certificate has not been installed on the target machine. Install it.

Under normal circumstances, when you open the PAD designer view, it will look like this:

A new feature has been added: Test Case -> Say Hello, along with a new action.

Let's test it:

After saving, it looks like this:

Finally, you can see the actual action. The effect is quite good.

Troubleshooting

  • First: If Chinese is not displayed, add a Chinese language pack. Ensure the names inside match the code. Refer to the example for specifics.
  • Second: If the component fails to load and shows an error, modify according to the error message, or add missing reference packages.
  • Third: For more details, keep digging and experimenting.

Extended Component Parameter Information

Based on the summary from netizen (Pan Chun) and my own summary, I will also output a document like this.

Relevant parameters required by ActionBase:

And built-in related types:

Thanks to Pan Chun for the summary.

Conclusion

That's all, folks! Writing this was not easy, especially because PAD can have various issues when recognizing your component.

I had to retry many, many times.

But thankfully, based on this extensible component, I have already written a Sqlite component and will also share it in the example for reference.

Reference

https://github.com/kesshei/PADDemo

Keep Exploring

Related Reading

More Articles