使用操作 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”操作选择器的屏幕截图

上面提到的两个操作是“启动 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 将无法在桌面流中使用,并且会在包含过程中导致错误。

后续步骤

上传自定义操作