使用操作 SDK 创建 Power Automate 桌面版操作
本文描述了如何在 Power Automate 桌面版中创建自定义操作。
创建自定义操作
重要提示
保留关键字不能用作操作名称和/或操作属性。 使用保留关键字作为操作名称和/或操作属性会导致错误的行为。 更多信息:桌面流中的保留关键字
首先创建一个新的类库 (.NET Framework) 项目。 选择 .NET Framework 版本 4.7.2。
若要在所创建的自定义模块中生成操作,请执行以下操作:
- 删除自动生成的 Class1.cs 文件。
- 在项目内创建新类以表示自定义操作,并为其指定一个不同的名称。
- 包括 Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK 和 Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK.Attributes 命名空间。
- 所有表示操作的类都应该在类之上有一个 [Action] 属性。
- 该类应具有公共访问权限并继承自 ActionBase 类。
using System;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK.Attributes;
namespace Modules.MyCustomModule
{
[Action(Id = "CustomAction")]
public class CustomAction : ActionBase
{
public override void Execute(ActionContext context)
{
throw new NotImplementedException();
}
}
}
大部分操作具有参数(输入或输出)。 输入和输出参数由经典 C# 属性来表示。
每个属性都应该有一个合适的 C# 特性,即 [InputArgument]
或 [OutputArgument]
,来指示其类型以及它们在 Power Automate 桌面版中的呈现方式。
输入参数也可以具有默认值。
using System.ComponentModel;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK.Attributes;
namespace Modules.MyCustomModule
{
[Action(Id = "CustomAction")]
public class CustomAction : ActionBase
{
[InputArgument, DefaultValue("Developer")]
public string InputName { get; set; }
[OutputArgument]
public string DisplayedMessage { get; set; }
public override void Execute(ActionContext context)
{
DisplayedMessage = $"Hello, {InputName}";
}
}
}
向自定义操作添加描述
为模块和操作添加描述和友好名称,以便 RPA 开发人员知道如何最好地利用它们。
Power Automate 桌面版设计器显示友好名称和描述。
您可以在模块项目的 Properties 文件夹中创建一个“Resources.resx”文件。 新的“.resx”文件应命名为“Resources.resx”。
模块和操作的说明格式应该如下所示:
名称字段中分别为“Module_Description”或“Action_Description”和“Module_FriendlyName”或“Action_FriendlyName”。 值字段中的说明。
我们还建议您为参数提供描述和友好的名称。 其格式应如下所示:“Action_Parameter_Description”、“Action_Parameter_FriendlyName”。
小费
建议在评论字段中注明您正在描述的内容(例如模块、操作等)
这些也可以使用 [InputArgument]
、[OutputArgument]
和 [Action]
属性的 FriendlyName 和 Description 属性进行设置。
下面是自定义模块的 Resources.resx 文件示例。
另一种快速向操作和参数添加友好名称和说明的方法是使用 [Action]、[InputArguement] 和 [OutputArguement] 属性中的 FriendlyName 和 Description 属性。
备注
要向模块添加友好名称和说明,必须修改相应的 .resx 文件或添加各自的 C# 属性。
向自定义操作添加错误处理
要在操作中定义自定义异常,请使用自定义操作类上方的 [Throws("ActionError")]
属性。 要定义的每个异常案例都应有其自己的属性。
在 catch 块中,使用以下代码:
throw new ActionException("ActionError", e.Message, e.InnerException);
确保 ActionException
名称与您在 Throws
属性中提供的名称匹配。 对每个异常情况使用 throw new ActionException
,并将其与相应的 Throws
属性名称匹配。 使用 Throws
属性定义的所有异常在设计器的“操作错误处理”选项卡中都是可见的。
在条件操作一节中可以找到这样的例子。
资源本地化
Power Automate 桌面版中模块的默认语言假定为英语。
该 Resources.resx 文件应该为英文。
可以使用额外的 Resources.{locale}.resx 文件添加任何其他语言以进行本地化。 例如,Resources.fr.resx。
自定义模块类别
模块可以包含类别和子类别,用于更好地组织操作。
为了分隔类别、子类别中的自定义操作,请按以下方式修改表示自定义操作的类之前的 Action 属性:
[Action(Category = "category.subcategory")]
备注
模块可能具有多个类别。 同样,类别也可以由子类别组成。 此结构可能不明确。
Order 属性决定了在设计器中预览操作的顺序。
Action1
属于类别“TestCategory”,它是模块的第一个操作(这样您就可以通过示例解释顺序和类别)。
[Action(Id = "Action1", Order = 1, Category = "TestCategory")]
条件操作
条件操作是返回“True”或“False”的操作。 标准库的“如果文件存在”Power Automate 桌面版操作是条件操作的一个很好的例子。
条件操作示例:
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK.Attributes;
using System;
using System.ComponentModel;
namespace Modules.CustomModule
{
[ConditionAction(Id = "ConditionalAction1", ResultPropertyName = nameof(Result))]
[Throws("ActionError")] // TODO: change error name (or delete if not needed)
public class ConditionalAction1 : ActionBase
{
#region Properties
public bool Result { get; private set; }
[InputArgument]
public string InputArgument1 { get; set; }
#endregion
#region Methods Overrides
public override void Execute(ActionContext context)
{
try
{
//TODO: add action execution code here
}
catch (Exception e)
{
if (e is ActionException) throw;
throw new ActionException("ActionError", e.Message, e.InnerException);
}
}
#endregion
}
}
请注意结果布尔变量。
如果文件存在操作没有输出参数。 它返回的是 true 或 false,具体取决于布尔变量结果保留的内容。
自定义操作选择器
在某些特定情况下,自定义操作可能需要具有多个变体。
来自标准操作库的“启动 Excel”操作便是一个示例。
使用“使用空白文档”选择器,流会启动一个空白 Excel 文档,而使用“并打开以下文档”选择则需要打开文件的文件路径。
上面提到的两个操作是“启动 Excel”基本操作的两个选择器。
创建自定义操作时,不必重写功能。
您可以创建一个“基本”动作,设置其输入和输出参数,然后使用操作选择器选择在每种风格中可见的内容。
通过操作选择器,可以在单个操作上添加一个抽象级别,并允许从单个“基本”操作中检索特定功能,而不必每次都重写代码以形成相同操作的新变体。
将选择器视为选择,筛选单个操作并根据各自的选择器仅显示所需的信息。
若要形成新的操作选择器,请首先创建一个要由选择器利用的基本操作。
集中操作需要布尔值或枚举属性作为输入 C# 参数。
此属性的值确定使用的具体选择器。
最常见的方法是使用枚举。 特别是当需要两个以上的选择器时,枚举是唯一的选择。
对于两个选择器的情况,可以使用布尔值。
此属性(也称为约束参数)必须具有默认值。
集中操作被声明为经典操作。
注意,第一个属性(输入参数)是枚举。 根据该属性的值,相应的选择器将变为可用选择器。
备注
要以您想要的方式对参数进行排序,您可以在 InputArgument 属性旁边设置“顺序”值。
using System.ComponentModel;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Desktop.Actions.SDK;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Desktop.Actions.SDK.Attributes;
namespace Modules.CustomModule
{
[Action(Id = "CentralCustomAction")]
public class CentralCustomAction : ActionBase
{
#region Properties
[InputArgument, DefaultValue(SelectorChoice.Selector1)]
public SelectorChoice Selector { get; set; }
[InputArgument(Order = 1)]
public string FirstName { get; set; }
[InputArgument(Order = 2)]
public string LastName { get; set; }
[InputArgument(Order = 3)]
public int Age { get; set; }
[OutputArgument]
public string DisplayedMessage { get; set; }
#endregion
#region Methods Overrides
public override void Execute(ActionContext context)
{
if (Selector == SelectorChoice.Selector1)
{
DisplayedMessage = $"Hello, {FirstName}!";
}
else if (Selector == SelectorChoice.Selector2)
{
DisplayedMessage = $"Hello, {FirstName} {LastName}!";
}
else // The 3rd Selector was chosen
{
DisplayedMessage = $"Hello, {FirstName} {LastName}!\nYour age is: {Age}";
}
}
#endregion
} // you can see below how to implement an action selector
}
使用枚举的自定义操作选择器
本示例中,您将创建三个选择器。 一个简单的枚举每次都会指示适当的选择器:
public enum SelectorChoice
{
Selector1,
Selector2,
Selector3
}
选择器由类来表示。
这些类必须继承 ActionSelector<TBaseActionClass>
类。
备注
TBaseActionClass 是基本操作类名称。
在 UseName() 方法中,会声明操作选择器的名称。 这用作解析资源的操作的名称。
public class Selector1 : ActionSelector<CentralCustomAction>
{
public Selector1()
{
UseName("DisplayOnlyFirstName");
Prop(p => p.Selector).ShouldBe(SelectorChoice.Selector1);
ShowAll();
Hide(p => p.LastName);
Hide(p => p.Age);
// or
// Show(p => p.FirstName);
// Show(p => p.DisplayedMessage);
}
}
备注
选择器类不应声明为操作。 唯一的操作是集中操作。 选择器充当筛选器。
在此特定示例中,我们只想显示一个参数,因此必须筛选掉其他参数。这同样适用于 Selector2:
public class Selector2 : ActionSelector<CentralCustomAction>
{
public Selector2()
{
UseName("DisplayFullName");
Prop(p => p.Selector).ShouldBe(SelectorChoice.Selector2);
ShowAll();
Hide(p => p.Age);
}
}
和 Selector3 类:
public class Selector3 : ActionSelector<CentralCustomAction>
{
public Selector3()
{
UseName("DisplayFullDetails");
Prop(p => p.Selector).ShouldBe(SelectorChoice.Selector3);
ShowAll();
}
}
最终执行通过位于中心操作中的 Execute(ActionContext context) 方法来实现。 根据选择器,将显示筛选的各个值。
public override void Execute(ActionContext context)
{
if (Selector == SelectorChoice.Selector1)
{
DisplayedMessage = $"Hello, {FirstName}!";
}
else if (Selector == SelectorChoice.Selector2)
{
DisplayedMessage = $"Hello, {FirstName} {LastName}!";
}
else // The 3rd Selector was chosen
{
DisplayedMessage = $"Hello, {FirstName} {LastName}!\nYour age is: {Age}";
}
}
使用布尔值的自定义操作选择器
以下是一个利用布尔值而非枚举的示例。
using System.ComponentModel;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK.ActionSelectors;
using Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK.Attributes;
namespace Modules.CustomModule
{
[Action]
public class CentralCustomActionWithBoolean : ActionBase
{
#region Properties
[InputArgument, DefaultValue(true)]
public bool TimeExpired { get; set; }
[InputArgument]
public string ElapsedTime { get; set; }
[InputArgument]
public string RemainingTime { get; set; }
[OutputArgument]
public string DisplayedMessage { get; set; }
#endregion
#region Methods Overrides
public override void Execute(ActionContext context)
{
DisplayedMessage = TimeExpired ? $"The timer has expired. Elapsed time: {ElapsedTime}" : $"Remaining time: {RemainingTime}";
}
#endregion
}
public class NoTime : ActionSelector<CentralCustomActionWithBoolean>
{
public NoTime()
{
UseName("TimeHasExpired");
Prop(p => p.TimeExpired).ShouldBe(true);
ShowAll();
Hide(p => p.RemainingTime);
}
}
public class ThereIsTime : ActionSelector<CentralCustomActionWithBoolean>
{
public ThereIsTime()
{
UseName("TimeHasNotExpired");
Prop(p => p.TimeExpired).ShouldBe(false);
ShowAll();
Hide(p => p.RemainingTime);
}
}
}
设置自定义操作选择器的说明
要为选择器创建描述和摘要,请在自定义模块的 .resx 文件中使用以下格式。
SelectorName_Description
SelectorName_Summary
也可以在选择器中使用 WithDescription 和 WithSummary 方法完成此操作。
重要提示
应该使用您的组织信任的数字证书对描述自定义操作的 .dll 文件、其 .dll 依赖项和包含所有内容的 .cab 文件进行正确签名。 此证书还应安装在在其上创作/修改/执行具有自定义操作依赖项的桌面流的每台计算机上,这些流位于受信任的根证书颁发机构下。
自定义模块 ID
每个模块都有自己的 ID(程序集名称)。 创建自定义模块时,请确保设置唯一的模块 ID。 若要设置模块的程序集名称,请修改 C#项 目属性的“常规”部分下的程序集名称属性。
警告
在流中包含具有相同 ID 的模块将导致冲突
自定义模块名称约定
若要可通过 Power Automate 桌面版读取自定义模块,AssemblyName 的文件名必须遵循以下模式:
?*.Modules.?*
Modules.?*
例如,模块.ContosoActions.dll
项目设置中的 AssemblyTitle 指定模块 ID。 它只能包含字母数字字符和下划线,并且必须以字母开头。
签署自定义模块内的所有 DLL
重要提示
必须使用可信证书对包含自定义模块(生成的程序集及其所有依赖项)的所有 .dll 文件进行签名
要完成自定义模块的创建,必须对项目的 bin/release 或 bin/Debug 文件夹下生成的所有 .dll 文件进行签名。
在 Visual Studio 开发人员命令提示符中运行以下命令(针对每个 .dll 文件),使用受信任的证书对所有 .dll 文件签名:
在 Visual Studio 开发人员命令提示符中运行以下命令(针对每个 .dll),使用受信任的证书对 .dll 文件签名:
Signtool sign /f {your certificate name}.pfx /p {your password for exporting the certificate} /fd
SHA256 {path to the .dll you want to sign}.dll
或者通过运行以下命令(通过创建 Windows PowerShell Script .ps1),该命令将迭代访问所有 .dll 文件,然后使用提供的证书对每个文件签名:
Get-ChildItem {the folder where dll files of custom module exist} -Filter *.dll |
Foreach-Object {
Signtool sign /f {your certificate name}.pfx /p {your password for exporting the certificate} /fd SHA256 $_.FullName
}
备注
数字证书必须具有可导出的私钥和代码签名功能
将一切打包在 Cabinet 文件中
包含自定义操作及其所有依赖项(.dll 文件)的 .dll 必须打包在 Cabinet 文件 (.cab) 中。
备注
命名 .cab 文件时,请遵循 Windows 操作系统的文件和文件夹命名约定。 请勿使用空格或特殊字符,如 < > : " / \ | ? *
。
创建一个包含以下行的 Windows PowerShell 脚本 (.ps1):
param(
[ValidateScript({Test-Path $_ -PathType Container})]
[string]
$sourceDir,
[ValidateScript({Test-Path $_ -PathType Container})]
[string]
$cabOutputDir,
[string]
$cabFilename
)
$ddf = ".OPTION EXPLICIT
.Set CabinetName1=$cabFilename
.Set DiskDirectory1=$cabOutputDir
.Set CompressionType=LZX
.Set Cabinet=on
.Set Compress=on
.Set CabinetFileCountThreshold=0
.Set FolderFileCountThreshold=0
.Set FolderSizeThreshold=0
.Set MaxCabinetSize=0
.Set MaxDiskFileCount=0
.Set MaxDiskSize=0
"
$ddfpath = ($env:TEMP + "\customModule.ddf")
$sourceDirLength = $sourceDir.Length;
$ddf += (Get-ChildItem $sourceDir -Filter "*.dll" | Where-Object { (!$_.PSIsContainer) -and ($_.Name -ne "Microsoft.PowerPlatform.PowerAutomate.Desktop.Actions.SDK.dll") } | Select-Object -ExpandProperty FullName | ForEach-Object { '"' + $_ + '" "' + ($_.Substring($sourceDirLength)) + '"' }) -join "`r`n"
$ddf | Out-File -Encoding UTF8 $ddfpath
makecab.exe /F $ddfpath
Remove-Item $ddfpath
然后,可以使用此 Windows PowerShell 脚本创建 .cab 文件,方法是在 Windows PowerShell 中调用它并提供:
- 要压缩的 .dll 文件的目录。
- 放置生成的 .cab 文件的目标目录。
使用以下语法调用脚本:
.\{name of script containing the .cab compression directions}.ps1 "{absolute path to the source directory containing the .dll files}" "{target dir to save cab}" {cabName}.cab
示例:
.\makeCabFile.ps1 "C:\Users\Username\source\repos\MyCustomModule\bin\Release\net472" "C:\Users\Username\MyCustomActions" MyCustomActions.cab
备注
- 创建 .cab 文件时,请确保实际的自定义操作 .dll 文件位于目标路径的根级别,而不是在子文件夹中。
- .cab 文件也必须签名。 其中包含的未签名的 .cab 文件和/或未签名的 .dll 将无法在桌面流中使用,并且会在包含过程中导致错误。