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:
_FriendlyName_Description_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.