From ad44f0fabc755853a0dc822f5bbb4248c2c6b5d2 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Wed, 19 Mar 2025 10:53:16 +0800 Subject: [PATCH 01/19] chore: update interop to framework 4.0.0 --- Directory.Packages.props | 2 +- .../Interop/AgentClient/MaaAgentClient.cs | 41 +++++++++ .../Interop/AgentClient/MaaAgentClientDef.cs | 14 +++ .../Interop/AgentServer/MaaAgentServer.cs | 40 +++++++++ .../packages.lock.json | 88 +++++++++---------- src/MaaFramework.Binding/MaaMsg.cs | 2 +- src/MaaFramework/packages.lock.json | 84 +++++++++--------- 7 files changed, 183 insertions(+), 88 deletions(-) create mode 100644 src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClient.cs create mode 100644 src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs create mode 100644 src/MaaFramework.Binding.Native/Interop/AgentServer/MaaAgentServer.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 4d30807..cd287b8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,7 +5,7 @@ - + diff --git a/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClient.cs new file mode 100644 index 0000000..fbb2b60 --- /dev/null +++ b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClient.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记 +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace MaaFramework.Binding.Interop.Native; + +public static partial class MaaAgentClient +{ + [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)] + public static partial MaaAgentClientHandle MaaAgentClientCreate(); + + [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)] + public static partial void MaaAgentClientDestroy(MaaAgentClientHandle client); + + [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaAgentClientBindResource(MaaAgentClientHandle client, MaaResourceHandle res); + + [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaAgentClientCreateSocket(MaaAgentClientHandle client, MaaStringBufferHandle identifier); + + [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaAgentClientConnect(MaaAgentClientHandle client); + + [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaAgentClientDisconnect(MaaAgentClientHandle client); +} diff --git a/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs new file mode 100644 index 0000000..1823216 --- /dev/null +++ b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs @@ -0,0 +1,14 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记 +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + +global using MaaAgentClientHandle = nint; + diff --git a/src/MaaFramework.Binding.Native/Interop/AgentServer/MaaAgentServer.cs b/src/MaaFramework.Binding.Native/Interop/AgentServer/MaaAgentServer.cs new file mode 100644 index 0000000..e0cf4c8 --- /dev/null +++ b/src/MaaFramework.Binding.Native/Interop/AgentServer/MaaAgentServer.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记 +#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace MaaFramework.Binding.Interop.Native; + +public static partial class MaaAgentServer +{ + [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaAgentServerRegisterCustomRecognition(string name, MaaCustomRecognitionCallback recognition, nint transArg); + + [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaAgentServerRegisterCustomAction(string name, MaaCustomActionCallback action, nint transArg); + + [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.U1)] + public static partial bool MaaAgentServerStartUp(string identifier); + + [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)] + public static partial void MaaAgentServerShutDown(); + + [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)] + public static partial void MaaAgentServerJoin(); + + [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)] + public static partial void MaaAgentServerDetach(); +} diff --git a/src/MaaFramework.Binding.UnitTests/packages.lock.json b/src/MaaFramework.Binding.UnitTests/packages.lock.json index feecfca..c6089ff 100644 --- a/src/MaaFramework.Binding.UnitTests/packages.lock.json +++ b/src/MaaFramework.Binding.UnitTests/packages.lock.json @@ -42,33 +42,33 @@ }, "Maa.Framework.Runtime.linux-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "xobEEAtdwH/TCtVVeqzyZwWpgmBj9/G+YKDhw8otumXFo6gzTFwwm917sc4La6LZfztolbhEP3HjHdkBXZAlnA==" + "resolved": "4.0.0", + "contentHash": "UO0WrFCkSAzW3CGj0N6ah+Znn4MTkd7CeWrdxJiSyRkRtjgIl2lPf67ZbbdYdCpXMF7PxQdwJaqo1A895kI/Qg==" }, "Maa.Framework.Runtime.linux-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "lezMomm+OM83YIIRMeuH4kxp1dkGzi9ufRhI8HNAmWd5mQGVScR75Gz55dZlGQ0UPmtwPP2PT26/4D92zN8Tow==" + "resolved": "4.0.0", + "contentHash": "DAhsxoDXPJN7A96gZTUAWnub92xDKtd8MXLx0mxipvBPRJ/Qw4wes2IKYididKkMNBj1XNiqQ3ENQAvjgm05Bw==" }, "Maa.Framework.Runtime.osx-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "fmsehoumgpe5Il2BGl8FrHczUiWr42qUJXYM/GagSW5MlB/hDlxGwA+Apmks8KerNpDAqv1NsuPXRQxnOuvMpw==" + "resolved": "4.0.0", + "contentHash": "yYMbg9TRzlpe9YB96fT1oTpWcX7Yg7dNGjmk7mfSZ+nm3ZRY95Uy7GJ3apk+kgCim89KEt8LsV5mmLgCNXAhpg==" }, "Maa.Framework.Runtime.osx-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "afAeMiMiBOvuQfgXaiOiF3NAtWWKpigybM6pdxpAXupK5lyBrwyMgxh6hgzpduD3D+ohzdf4a5sdWdxLbyrdxw==" + "resolved": "4.0.0", + "contentHash": "5SvGaS2CMXECIC0QVzzr9hPUZQscIHIn1nddr8d7zyISTokkXMwQvaZRSu6hAibECXIg44yiowFYPPnS7SvOnQ==" }, "Maa.Framework.Runtime.win-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "tl9tEzViM5vwmuLE1ozMP+YdVBK2A8E1RdXCqk4zXRoaYgLqH24V9NVjACDIKDE9MArv4Yqask0s3JMJick1dg==" + "resolved": "4.0.0", + "contentHash": "h2QLNIn2m7w2uzN1696n0v5HPyK+aR0DexyiCBbjYQTdOvfXC1V5QcP9boCf0VTrQMhN7Wr+qtCbbwb5qOiXJA==" }, "Maa.Framework.Runtime.win-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "RxHzvnyVer26AMJfyME+EWFf2TKK8Ka8a3fBJQMJGZgvK+g06MTxW5LbumQp4F5ylMJvHr/+SVwo9sxZObsUBg==" + "resolved": "4.0.0", + "contentHash": "KkP36yP1s03GOcTDnm9HscOzZa5U2IeMcfV4gEZ1cg/Yji7ynKKtV2mRIsZnpPs2deFuTL3Qn22uPr9C8mHIqw==" }, "Microsoft.ApplicationInsights": { "type": "Transitive", @@ -161,7 +161,7 @@ "type": "Project", "dependencies": { "Maa.Framework.Native": "[1.0.0-dev, )", - "Maa.Framework.Runtimes": "[3.0.4, )" + "Maa.Framework.Runtimes": "[4.0.0, )" } }, "Maa.Framework.Binding": { @@ -194,16 +194,16 @@ }, "Maa.Framework.Runtimes": { "type": "CentralTransitive", - "requested": "[3.0.4, )", - "resolved": "3.0.4", - "contentHash": "F9As/LIRRxXvrkubxgHxExoxCqVzYkfo1Ph3kHYxCYXrdUAZVXESr3o99L+NPv2rUZ9VkupKeL+Xomk4hXCcVw==", + "requested": "[4.0.0, )", + "resolved": "4.0.0", + "contentHash": "hHYtVnknsGZJj22TO4gEfrqUlhq6BfF9DBIM1w0U0dv0mz4fzijawmZJ9QpRX0UldOXq7HN8TBC2lSLyz00FAA==", "dependencies": { - "Maa.Framework.Runtime.linux-arm64": "3.0.4", - "Maa.Framework.Runtime.linux-x64": "3.0.4", - "Maa.Framework.Runtime.osx-arm64": "3.0.4", - "Maa.Framework.Runtime.osx-x64": "3.0.4", - "Maa.Framework.Runtime.win-arm64": "3.0.4", - "Maa.Framework.Runtime.win-x64": "3.0.4" + "Maa.Framework.Runtime.linux-arm64": "4.0.0", + "Maa.Framework.Runtime.linux-x64": "4.0.0", + "Maa.Framework.Runtime.osx-arm64": "4.0.0", + "Maa.Framework.Runtime.osx-x64": "4.0.0", + "Maa.Framework.Runtime.win-arm64": "4.0.0", + "Maa.Framework.Runtime.win-x64": "4.0.0" } } }, @@ -248,33 +248,33 @@ }, "Maa.Framework.Runtime.linux-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "xobEEAtdwH/TCtVVeqzyZwWpgmBj9/G+YKDhw8otumXFo6gzTFwwm917sc4La6LZfztolbhEP3HjHdkBXZAlnA==" + "resolved": "4.0.0", + "contentHash": "UO0WrFCkSAzW3CGj0N6ah+Znn4MTkd7CeWrdxJiSyRkRtjgIl2lPf67ZbbdYdCpXMF7PxQdwJaqo1A895kI/Qg==" }, "Maa.Framework.Runtime.linux-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "lezMomm+OM83YIIRMeuH4kxp1dkGzi9ufRhI8HNAmWd5mQGVScR75Gz55dZlGQ0UPmtwPP2PT26/4D92zN8Tow==" + "resolved": "4.0.0", + "contentHash": "DAhsxoDXPJN7A96gZTUAWnub92xDKtd8MXLx0mxipvBPRJ/Qw4wes2IKYididKkMNBj1XNiqQ3ENQAvjgm05Bw==" }, "Maa.Framework.Runtime.osx-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "fmsehoumgpe5Il2BGl8FrHczUiWr42qUJXYM/GagSW5MlB/hDlxGwA+Apmks8KerNpDAqv1NsuPXRQxnOuvMpw==" + "resolved": "4.0.0", + "contentHash": "yYMbg9TRzlpe9YB96fT1oTpWcX7Yg7dNGjmk7mfSZ+nm3ZRY95Uy7GJ3apk+kgCim89KEt8LsV5mmLgCNXAhpg==" }, "Maa.Framework.Runtime.osx-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "afAeMiMiBOvuQfgXaiOiF3NAtWWKpigybM6pdxpAXupK5lyBrwyMgxh6hgzpduD3D+ohzdf4a5sdWdxLbyrdxw==" + "resolved": "4.0.0", + "contentHash": "5SvGaS2CMXECIC0QVzzr9hPUZQscIHIn1nddr8d7zyISTokkXMwQvaZRSu6hAibECXIg44yiowFYPPnS7SvOnQ==" }, "Maa.Framework.Runtime.win-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "tl9tEzViM5vwmuLE1ozMP+YdVBK2A8E1RdXCqk4zXRoaYgLqH24V9NVjACDIKDE9MArv4Yqask0s3JMJick1dg==" + "resolved": "4.0.0", + "contentHash": "h2QLNIn2m7w2uzN1696n0v5HPyK+aR0DexyiCBbjYQTdOvfXC1V5QcP9boCf0VTrQMhN7Wr+qtCbbwb5qOiXJA==" }, "Maa.Framework.Runtime.win-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "RxHzvnyVer26AMJfyME+EWFf2TKK8Ka8a3fBJQMJGZgvK+g06MTxW5LbumQp4F5ylMJvHr/+SVwo9sxZObsUBg==" + "resolved": "4.0.0", + "contentHash": "KkP36yP1s03GOcTDnm9HscOzZa5U2IeMcfV4gEZ1cg/Yji7ynKKtV2mRIsZnpPs2deFuTL3Qn22uPr9C8mHIqw==" }, "Microsoft.ApplicationInsights": { "type": "Transitive", @@ -367,7 +367,7 @@ "type": "Project", "dependencies": { "Maa.Framework.Native": "[1.0.0-dev, )", - "Maa.Framework.Runtimes": "[3.0.4, )" + "Maa.Framework.Runtimes": "[4.0.0, )" } }, "Maa.Framework.Binding": { @@ -400,16 +400,16 @@ }, "Maa.Framework.Runtimes": { "type": "CentralTransitive", - "requested": "[3.0.4, )", - "resolved": "3.0.4", - "contentHash": "F9As/LIRRxXvrkubxgHxExoxCqVzYkfo1Ph3kHYxCYXrdUAZVXESr3o99L+NPv2rUZ9VkupKeL+Xomk4hXCcVw==", + "requested": "[4.0.0, )", + "resolved": "4.0.0", + "contentHash": "hHYtVnknsGZJj22TO4gEfrqUlhq6BfF9DBIM1w0U0dv0mz4fzijawmZJ9QpRX0UldOXq7HN8TBC2lSLyz00FAA==", "dependencies": { - "Maa.Framework.Runtime.linux-arm64": "3.0.4", - "Maa.Framework.Runtime.linux-x64": "3.0.4", - "Maa.Framework.Runtime.osx-arm64": "3.0.4", - "Maa.Framework.Runtime.osx-x64": "3.0.4", - "Maa.Framework.Runtime.win-arm64": "3.0.4", - "Maa.Framework.Runtime.win-x64": "3.0.4" + "Maa.Framework.Runtime.linux-arm64": "4.0.0", + "Maa.Framework.Runtime.linux-x64": "4.0.0", + "Maa.Framework.Runtime.osx-arm64": "4.0.0", + "Maa.Framework.Runtime.osx-x64": "4.0.0", + "Maa.Framework.Runtime.win-arm64": "4.0.0", + "Maa.Framework.Runtime.win-x64": "4.0.0" } } } diff --git a/src/MaaFramework.Binding/MaaMsg.cs b/src/MaaFramework.Binding/MaaMsg.cs index 9642a42..3e0d8ca 100644 --- a/src/MaaFramework.Binding/MaaMsg.cs +++ b/src/MaaFramework.Binding/MaaMsg.cs @@ -12,7 +12,7 @@ namespace MaaFramework.Binding.Notification; -//MaaApiDocument Version: (main) v3.0.4 +//MaaApiDocument Version: (main) v4.0.0 /// /// A callback consists of a message and a payload. /// The message is a string that indicates the type of the message. diff --git a/src/MaaFramework/packages.lock.json b/src/MaaFramework/packages.lock.json index 38658bb..ce93f8b 100644 --- a/src/MaaFramework/packages.lock.json +++ b/src/MaaFramework/packages.lock.json @@ -4,16 +4,16 @@ "net7.0": { "Maa.Framework.Runtimes": { "type": "Direct", - "requested": "[3.0.4, )", - "resolved": "3.0.4", - "contentHash": "F9As/LIRRxXvrkubxgHxExoxCqVzYkfo1Ph3kHYxCYXrdUAZVXESr3o99L+NPv2rUZ9VkupKeL+Xomk4hXCcVw==", + "requested": "[4.0.0, )", + "resolved": "4.0.0", + "contentHash": "hHYtVnknsGZJj22TO4gEfrqUlhq6BfF9DBIM1w0U0dv0mz4fzijawmZJ9QpRX0UldOXq7HN8TBC2lSLyz00FAA==", "dependencies": { - "Maa.Framework.Runtime.linux-arm64": "3.0.4", - "Maa.Framework.Runtime.linux-x64": "3.0.4", - "Maa.Framework.Runtime.osx-arm64": "3.0.4", - "Maa.Framework.Runtime.osx-x64": "3.0.4", - "Maa.Framework.Runtime.win-arm64": "3.0.4", - "Maa.Framework.Runtime.win-x64": "3.0.4" + "Maa.Framework.Runtime.linux-arm64": "4.0.0", + "Maa.Framework.Runtime.linux-x64": "4.0.0", + "Maa.Framework.Runtime.osx-arm64": "4.0.0", + "Maa.Framework.Runtime.osx-x64": "4.0.0", + "Maa.Framework.Runtime.win-arm64": "4.0.0", + "Maa.Framework.Runtime.win-x64": "4.0.0" } }, "SonarAnalyzer.CSharp": { @@ -24,33 +24,33 @@ }, "Maa.Framework.Runtime.linux-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "xobEEAtdwH/TCtVVeqzyZwWpgmBj9/G+YKDhw8otumXFo6gzTFwwm917sc4La6LZfztolbhEP3HjHdkBXZAlnA==" + "resolved": "4.0.0", + "contentHash": "UO0WrFCkSAzW3CGj0N6ah+Znn4MTkd7CeWrdxJiSyRkRtjgIl2lPf67ZbbdYdCpXMF7PxQdwJaqo1A895kI/Qg==" }, "Maa.Framework.Runtime.linux-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "lezMomm+OM83YIIRMeuH4kxp1dkGzi9ufRhI8HNAmWd5mQGVScR75Gz55dZlGQ0UPmtwPP2PT26/4D92zN8Tow==" + "resolved": "4.0.0", + "contentHash": "DAhsxoDXPJN7A96gZTUAWnub92xDKtd8MXLx0mxipvBPRJ/Qw4wes2IKYididKkMNBj1XNiqQ3ENQAvjgm05Bw==" }, "Maa.Framework.Runtime.osx-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "fmsehoumgpe5Il2BGl8FrHczUiWr42qUJXYM/GagSW5MlB/hDlxGwA+Apmks8KerNpDAqv1NsuPXRQxnOuvMpw==" + "resolved": "4.0.0", + "contentHash": "yYMbg9TRzlpe9YB96fT1oTpWcX7Yg7dNGjmk7mfSZ+nm3ZRY95Uy7GJ3apk+kgCim89KEt8LsV5mmLgCNXAhpg==" }, "Maa.Framework.Runtime.osx-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "afAeMiMiBOvuQfgXaiOiF3NAtWWKpigybM6pdxpAXupK5lyBrwyMgxh6hgzpduD3D+ohzdf4a5sdWdxLbyrdxw==" + "resolved": "4.0.0", + "contentHash": "5SvGaS2CMXECIC0QVzzr9hPUZQscIHIn1nddr8d7zyISTokkXMwQvaZRSu6hAibECXIg44yiowFYPPnS7SvOnQ==" }, "Maa.Framework.Runtime.win-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "tl9tEzViM5vwmuLE1ozMP+YdVBK2A8E1RdXCqk4zXRoaYgLqH24V9NVjACDIKDE9MArv4Yqask0s3JMJick1dg==" + "resolved": "4.0.0", + "contentHash": "h2QLNIn2m7w2uzN1696n0v5HPyK+aR0DexyiCBbjYQTdOvfXC1V5QcP9boCf0VTrQMhN7Wr+qtCbbwb5qOiXJA==" }, "Maa.Framework.Runtime.win-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "RxHzvnyVer26AMJfyME+EWFf2TKK8Ka8a3fBJQMJGZgvK+g06MTxW5LbumQp4F5ylMJvHr/+SVwo9sxZObsUBg==" + "resolved": "4.0.0", + "contentHash": "KkP36yP1s03GOcTDnm9HscOzZa5U2IeMcfV4gEZ1cg/Yji7ynKKtV2mRIsZnpPs2deFuTL3Qn22uPr9C8mHIqw==" }, "Maa.Framework.Binding": { "type": "Project" @@ -78,16 +78,16 @@ "net9.0": { "Maa.Framework.Runtimes": { "type": "Direct", - "requested": "[3.0.4, )", - "resolved": "3.0.4", - "contentHash": "F9As/LIRRxXvrkubxgHxExoxCqVzYkfo1Ph3kHYxCYXrdUAZVXESr3o99L+NPv2rUZ9VkupKeL+Xomk4hXCcVw==", + "requested": "[4.0.0, )", + "resolved": "4.0.0", + "contentHash": "hHYtVnknsGZJj22TO4gEfrqUlhq6BfF9DBIM1w0U0dv0mz4fzijawmZJ9QpRX0UldOXq7HN8TBC2lSLyz00FAA==", "dependencies": { - "Maa.Framework.Runtime.linux-arm64": "3.0.4", - "Maa.Framework.Runtime.linux-x64": "3.0.4", - "Maa.Framework.Runtime.osx-arm64": "3.0.4", - "Maa.Framework.Runtime.osx-x64": "3.0.4", - "Maa.Framework.Runtime.win-arm64": "3.0.4", - "Maa.Framework.Runtime.win-x64": "3.0.4" + "Maa.Framework.Runtime.linux-arm64": "4.0.0", + "Maa.Framework.Runtime.linux-x64": "4.0.0", + "Maa.Framework.Runtime.osx-arm64": "4.0.0", + "Maa.Framework.Runtime.osx-x64": "4.0.0", + "Maa.Framework.Runtime.win-arm64": "4.0.0", + "Maa.Framework.Runtime.win-x64": "4.0.0" } }, "SonarAnalyzer.CSharp": { @@ -98,33 +98,33 @@ }, "Maa.Framework.Runtime.linux-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "xobEEAtdwH/TCtVVeqzyZwWpgmBj9/G+YKDhw8otumXFo6gzTFwwm917sc4La6LZfztolbhEP3HjHdkBXZAlnA==" + "resolved": "4.0.0", + "contentHash": "UO0WrFCkSAzW3CGj0N6ah+Znn4MTkd7CeWrdxJiSyRkRtjgIl2lPf67ZbbdYdCpXMF7PxQdwJaqo1A895kI/Qg==" }, "Maa.Framework.Runtime.linux-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "lezMomm+OM83YIIRMeuH4kxp1dkGzi9ufRhI8HNAmWd5mQGVScR75Gz55dZlGQ0UPmtwPP2PT26/4D92zN8Tow==" + "resolved": "4.0.0", + "contentHash": "DAhsxoDXPJN7A96gZTUAWnub92xDKtd8MXLx0mxipvBPRJ/Qw4wes2IKYididKkMNBj1XNiqQ3ENQAvjgm05Bw==" }, "Maa.Framework.Runtime.osx-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "fmsehoumgpe5Il2BGl8FrHczUiWr42qUJXYM/GagSW5MlB/hDlxGwA+Apmks8KerNpDAqv1NsuPXRQxnOuvMpw==" + "resolved": "4.0.0", + "contentHash": "yYMbg9TRzlpe9YB96fT1oTpWcX7Yg7dNGjmk7mfSZ+nm3ZRY95Uy7GJ3apk+kgCim89KEt8LsV5mmLgCNXAhpg==" }, "Maa.Framework.Runtime.osx-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "afAeMiMiBOvuQfgXaiOiF3NAtWWKpigybM6pdxpAXupK5lyBrwyMgxh6hgzpduD3D+ohzdf4a5sdWdxLbyrdxw==" + "resolved": "4.0.0", + "contentHash": "5SvGaS2CMXECIC0QVzzr9hPUZQscIHIn1nddr8d7zyISTokkXMwQvaZRSu6hAibECXIg44yiowFYPPnS7SvOnQ==" }, "Maa.Framework.Runtime.win-arm64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "tl9tEzViM5vwmuLE1ozMP+YdVBK2A8E1RdXCqk4zXRoaYgLqH24V9NVjACDIKDE9MArv4Yqask0s3JMJick1dg==" + "resolved": "4.0.0", + "contentHash": "h2QLNIn2m7w2uzN1696n0v5HPyK+aR0DexyiCBbjYQTdOvfXC1V5QcP9boCf0VTrQMhN7Wr+qtCbbwb5qOiXJA==" }, "Maa.Framework.Runtime.win-x64": { "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "RxHzvnyVer26AMJfyME+EWFf2TKK8Ka8a3fBJQMJGZgvK+g06MTxW5LbumQp4F5ylMJvHr/+SVwo9sxZObsUBg==" + "resolved": "4.0.0", + "contentHash": "KkP36yP1s03GOcTDnm9HscOzZa5U2IeMcfV4gEZ1cg/Yji7ynKKtV2mRIsZnpPs2deFuTL3Qn22uPr9C8mHIqw==" }, "Maa.Framework.Binding": { "type": "Project" From 0682bd944e3ff5ed18c0bc8984ecf9746afa40f5 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Fri, 21 Mar 2025 18:15:12 +0800 Subject: [PATCH 02/19] feat: add wrapper of MaaAgent --- .../MaaAgentClient.cs | 109 ++++++++++++++++++ .../MaaAgentServer.cs | 53 +++++++++ .../Exceptions/MaaOperationException.cs | 4 +- src/MaaFramework.Binding/IMaaAgentClient.cs | 46 ++++++++ src/MaaFramework.Binding/IMaaAgentServer.cs | 43 +++++++ src/MaaFramework.Binding/IMaaResource.cs | 17 +-- 6 files changed, 262 insertions(+), 10 deletions(-) create mode 100644 src/MaaFramework.Binding.Native/MaaAgentClient.cs create mode 100644 src/MaaFramework.Binding.Native/MaaAgentServer.cs create mode 100644 src/MaaFramework.Binding/IMaaAgentClient.cs create mode 100644 src/MaaFramework.Binding/IMaaAgentServer.cs diff --git a/src/MaaFramework.Binding.Native/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/MaaAgentClient.cs new file mode 100644 index 0000000..7f72f1b --- /dev/null +++ b/src/MaaFramework.Binding.Native/MaaAgentClient.cs @@ -0,0 +1,109 @@ +using MaaFramework.Binding.Abstractions; +using MaaFramework.Binding.Interop.Native; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using static MaaFramework.Binding.Interop.Native.MaaAgentClient; +using static MaaFramework.Binding.Interop.Native.MaaBuffer; + +namespace MaaFramework.Binding; + +/// +/// A wrapper class providing a reference implementation for . +/// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public class MaaAgentClient : MaaDisposableHandle, IMaaAgentClient +{ + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => $"{{{GetType().Name} {{ Disposed = {IsInvalid} }}}}"; + + /// + /// Creates a instance. + /// + /// + /// Wrapper of . + /// + public MaaAgentClient() + : base(invalidHandleValue: MaaAgentClientHandle.Zero) + { + var handle = MaaAgentClientCreate(); + SetHandle(handle, needReleased: true); + } + + /// The resource. + /// The dispose options. + /// + [SetsRequiredMembers] + public MaaAgentClient(MaaResource resource, DisposeOptions disposeOptions) + : this() + { + Resource = resource; + DisposeOptions = disposeOptions; + } + + /// + public required DisposeOptions DisposeOptions { get; set; } + + /// + /// + /// Wrapper of . + /// + protected override void ReleaseHandle() + { + // Cannot destroy Instance before disposing Resource. + + if (DisposeOptions.HasFlag(DisposeOptions.Resource)) + { + Resource.Dispose(); + } + + MaaAgentClientDestroy(Handle); + } + + /// + IMaaResource IMaaAgentClient.Resource + { + get => Resource; + set => Resource = (MaaResource)value; + } + + /// + /// + /// Wrapper of . + /// + public required MaaResource Resource + { + get => field; + set + { + ArgumentNullException.ThrowIfNull(value); + _ = MaaAgentClientBindResource(Handle, value.Handle).ThrowIfFalse(MaaInteroperationException.ResourceBindingFailedMessage); + field = value; + } + } + + /// + /// + /// Wrapper of . + /// + public bool CreateSocket(string identifier) + { + var handle = MaaStringBufferCreate(); + var ret = MaaStringBufferSetEx(Handle, identifier) && MaaAgentClientCreateSocket(Handle, handle); + MaaStringBufferDestroy(handle); + return ret; + } + + /// + /// + /// Wrapper of . + /// + public bool LinkStart() + => MaaAgentClientConnect(Handle); + + /// + /// + /// Wrapper of . + /// + public bool LinkStop() + => MaaAgentClientDisconnect(Handle); +} + diff --git a/src/MaaFramework.Binding.Native/MaaAgentServer.cs b/src/MaaFramework.Binding.Native/MaaAgentServer.cs new file mode 100644 index 0000000..846ce74 --- /dev/null +++ b/src/MaaFramework.Binding.Native/MaaAgentServer.cs @@ -0,0 +1,53 @@ +using MaaFramework.Binding.Custom; +using MaaFramework.Binding.Interop.Native; +using static MaaFramework.Binding.Interop.Native.MaaAgentServer; + +namespace MaaFramework.Binding.Native; + +/// +/// A wrapper class providing a reference implementation for . +/// +public class MaaAgentServer : IMaaAgentServer +{ + private readonly MaaMarshaledApiRegistry _actions = new(); + private readonly MaaMarshaledApiRegistry _recognitions = new(); + + /// + public bool Register(string name, T custom) where T : IMaaCustomResource + { + custom.Name = name; + return Register(custom); + } + + /// + /// + /// Wrapper of and . + /// + public bool Register(T custom) where T : IMaaCustomResource => custom switch + { + IMaaCustomAction res + => MaaAgentServerRegisterCustomAction(res.Name, res.Convert(out var callback), nint.Zero) + && _actions.Register(res.Name, callback), + IMaaCustomRecognition res + => MaaAgentServerRegisterCustomRecognition(res.Name, res.Convert(out var callback), nint.Zero) + && _recognitions.Register(res.Name, callback), + _ + => throw new NotImplementedException($"Type '{typeof(T)}' is not implemented."), + }; + + /// + public bool StartUp(string identifier) + => MaaAgentServerStartUp(identifier); + + /// + public void ShutDown() + => MaaAgentServerShutDown(); + + /// + public void Join() + => MaaAgentServerJoin(); + + /// + public void Detach() + => MaaAgentServerDetach(); +} diff --git a/src/MaaFramework.Binding/Exceptions/MaaOperationException.cs b/src/MaaFramework.Binding/Exceptions/MaaOperationException.cs index d3d4710..6a0bd4e 100644 --- a/src/MaaFramework.Binding/Exceptions/MaaOperationException.cs +++ b/src/MaaFramework.Binding/Exceptions/MaaOperationException.cs @@ -11,12 +11,12 @@ public class MaaInteroperationException : MaaException /// /// Resource binding failed message. /// - public const string ResourceBindingFailedMessage = "MaaTasker failed to bind MaaResource."; + public const string ResourceBindingFailedMessage = "Failed to bind MaaResource."; /// /// Controller binding failed message. /// - public const string ControllerBindingFailedMessage = "MaaTasker failed to bind MaaController."; + public const string ControllerBindingFailedMessage = "Failed to bind MaaController."; /// /// Resource modified message. diff --git a/src/MaaFramework.Binding/IMaaAgentClient.cs b/src/MaaFramework.Binding/IMaaAgentClient.cs new file mode 100644 index 0000000..4cedc2f --- /dev/null +++ b/src/MaaFramework.Binding/IMaaAgentClient.cs @@ -0,0 +1,46 @@ +using MaaFramework.Binding.Abstractions; + +namespace MaaFramework.Binding; + +/// +/// An interface defining wrapped members for MaaAgentClient with generic handle. +/// +/// The type of handle. +public interface IMaaAgentClient : IMaaAgentClient, IMaaDisposableHandle; + +/// +/// An interface defining wrapped members for MaaAgentClient. +/// +public interface IMaaAgentClient : IMaaDisposable +{ + /// + /// Gets or sets whether disposes the when was invoked. + /// + DisposeOptions DisposeOptions { get; set; } + + /// + /// Gets or sets a resource that binds to the . + /// + /// + /// + IMaaResource Resource { get; set; } + + /// + /// Creates a socket connection with the specified identifier. + /// + /// The connection identifier. + /// if the socket was created successfully; otherwise, . + bool CreateSocket(string identifier); + + /// + /// Starts the connection. + /// + /// if the connection was started successfully; otherwise, . + bool LinkStart(); + + /// + /// Stops the connection. + /// + /// if the connection was stopped successfully; otherwise, . + bool LinkStop(); +} diff --git a/src/MaaFramework.Binding/IMaaAgentServer.cs b/src/MaaFramework.Binding/IMaaAgentServer.cs new file mode 100644 index 0000000..53e6661 --- /dev/null +++ b/src/MaaFramework.Binding/IMaaAgentServer.cs @@ -0,0 +1,43 @@ +using MaaFramework.Binding.Custom; + +namespace MaaFramework.Binding; + +/// +/// An interface defining wrapped members for MaaAgentServer. +/// +public interface IMaaAgentServer +{ + /// + /// Registers a or in the . + /// + /// The or . + /// The new name that will be used to reference the custom resource. + /// The custom resource instance to register. + /// if the registration was successful; otherwise, . + bool Register(string name, T custom) where T : IMaaCustomResource; + + /// + bool Register(T custom) where T : IMaaCustomResource; + + /// + /// Starts up the agent server to prepare for receiving client messages from the specified connection. + /// + /// The connection identifier. + /// if the server started successfully; otherwise, . + bool StartUp(string identifier); + + /// + /// Shuts down the agent server. + /// + void ShutDown(); + + /// + /// Blocks the calling thread until the thread for receiving client messages finishes its execution. + /// + void Join(); + + /// + /// Separates the thread for receiving client messages, allowing execution to continue independently. + /// + void Detach(); +} diff --git a/src/MaaFramework.Binding/IMaaResource.cs b/src/MaaFramework.Binding/IMaaResource.cs index 3e21b3e..b784619 100644 --- a/src/MaaFramework.Binding/IMaaResource.cs +++ b/src/MaaFramework.Binding/IMaaResource.cs @@ -18,9 +18,9 @@ public interface IMaaResource : IMaaCommon, IMaaOption, IMaaPost /// Registers a or in the . /// /// The or . - /// The new name that will be used to reference it. - /// The or . - /// if the custom action or recognition was registered successfully; otherwise, . + /// The new name that will be used to reference the custom resource. + /// The custom resource instance to register. + /// if the registration was successful; otherwise, . bool Register(string name, T custom) where T : IMaaCustomResource; /// @@ -30,24 +30,25 @@ public interface IMaaResource : IMaaCommon, IMaaOption, IMaaPost /// Unregisters a or in the . /// /// The or . - /// The name of or when it was registered. - /// if the custom action or recognition was unregistered successfully; otherwise, . + /// The name of the instance when it was registered. + /// if the unregistration was successful; otherwise, . bool Unregister(string name) where T : IMaaCustomResource; /// - /// The or . + /// The custom resource instance to unregister. bool Unregister(T custom) where T : IMaaCustomResource; /// /// Clears all or registered in the . /// /// The or . - /// if custom actions or recognitions were cleared successfully; otherwise, . + /// if custom resource instances were cleared successfully; otherwise, . bool Clear() where T : IMaaCustomResource; /// - /// Clear the loaded resource. + /// Clears the loaded resource. /// + /// Whether to include custom resources. /// if the is cleared successfully; otherwise, . bool Clear(bool includeCustomResource = false); From dcafc4e45796b44437fe7fe80bbf6007ec465c6d Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Tue, 25 Mar 2025 00:22:59 +0800 Subject: [PATCH 03/19] refactor: load native libraries using `ModuleInitializer` --- .../Interop/NativeLibrary.cs | 108 +++++++++++------- 1 file changed, 67 insertions(+), 41 deletions(-) diff --git a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs index b2fd190..fa62127 100644 --- a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs +++ b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace MaaFramework.Binding.Interop.Native; @@ -7,34 +8,55 @@ internal static partial class NativeLibrary { private static readonly Assembly s_assembly = typeof(NativeLibrary).Assembly; - public static void Init() - => SetDllImportResolver(s_assembly, NativeAssemblyResolver); + private static bool s_isAgentServer; + private static readonly List s_searchPath = []; + private static readonly Dictionary s_libraryHandles = []; - public static IntPtr NativeAssemblyResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) +#pragma warning disable CA2255 // 不应在库中使用 “ModuleInitializer” 属性 + [ModuleInitializer] + internal static void SetNativeAssemblyResolver() => SetDllImportResolver(s_assembly, NativeAssemblyResolver); +#pragma warning restore CA2255 // 不应在库中使用 “ModuleInitializer” 属性 + + public static void Init(bool isAgentServer, params string[] paths) { - var libHandle = IntPtr.Zero; - if (!libraryName.Equals("MaaFramework", StringComparison.Ordinal) - && !libraryName.Equals("MaaToolkit", StringComparison.Ordinal)) - { - return libHandle; - } + if (s_libraryHandles.Count > 0) + throw new InvalidOperationException("NativeLibrary is already loaded."); - if (TryGetRuntimesPath(libraryName, out var dllPath)) - { - _ = TryLoad(dllPath, assembly, searchPath, out libHandle); - } - return libHandle; + s_isAgentServer = isAgentServer; + s_searchPath.AddRange(paths.Where(path => !string.IsNullOrWhiteSpace(path))); + } + + public static IntPtr NativeAssemblyResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) => libraryName switch + { + "MaaFramework" or "MaaToolkit" + or "MaaAgentServer" or "MaaAgentClient" => GetLibraryHandle(libraryName), + + _ => IntPtr.Zero, + }; + + private static nint GetLibraryHandle(string libraryName) + { + if (s_libraryHandles.TryGetValue(libraryName, out var libraryHandle)) + return libraryHandle; + + if (TryGetRuntimesPath(libraryName, out var dllPath) + && TryLoad(dllPath, out libraryHandle) + && s_libraryHandles.TryAdd(libraryName, libraryHandle)) + return libraryHandle; + + _ = s_libraryHandles.TryAdd(libraryName, nint.Zero); + return nint.Zero; } private static bool TryGetRuntimesPath(string libraryName, out string dllPath) { + libraryName = GetFullLibraryName(libraryName); dllPath = GetRuntimesPaths(libraryName).FirstOrDefault(File.Exists, string.Empty); return !string.IsNullOrEmpty(dllPath); } - private static IEnumerable GetRuntimesPaths(string libraryName) + private static IEnumerable GetRuntimesPaths(string libraryFullName) { - GetArchitectureNameAndExtensionName(out var arch, out var ext); var args1 = new string[] { Path.GetDirectoryName(s_assembly.Location) ?? "./", @@ -42,44 +64,48 @@ private static IEnumerable GetRuntimesPaths(string libraryName) }; var args2 = new string[] { - $"/runtimes/{arch}/native/", + $"/runtimes/{GetArchitectureName()}/native/", "/" }; - var args3 = new string[] - { - $"{libraryName}.{ext}", - $"lib{libraryName}.{ext}" - }; return from arg1 in args1 from arg2 in args2 - from arg3 in args3 select Path.GetFullPath( - string.Concat(arg1, arg2, arg3)); + string.Concat(arg1, arg2, libraryFullName)); } - private static void GetArchitectureNameAndExtensionName(out string arch, out string ext) +#pragma warning disable IDE0072 // 添加缺失的事例 + private static string GetArchitectureName() => RuntimeInformation.OSArchitecture switch { - if (IsWindows) arch = "win"; - else if (IsLinux) arch = "linux"; - else if (IsOSX) arch = "osx"; - else if (IsAndroid) arch = "android"; - else throw new PlatformNotSupportedException(); - - if (IsX64) arch += "-x64"; - else if (IsArm64) arch += "-arm64"; - else throw new PlatformNotSupportedException(); - - if (IsWindows) ext = "dll"; - else if (IsLinux || IsAndroid) ext = "so"; - else if (IsOSX) ext = "dylib"; - else throw new PlatformNotSupportedException(); + Architecture.X64 when IsWindows => "win-x64", + // Architecture.Arm64 when IsWindows => "win-arm64", + Architecture.X64 when IsLinux => "linux-x64", + Architecture.Arm64 when IsLinux => "linux-arm64", + Architecture.X64 when IsOSX => "osx-x64", + Architecture.Arm64 when IsOSX => "osx-arm64", + Architecture.X64 when IsAndroid => "android-x64", + Architecture.Arm64 when IsAndroid => "android-arm64", + _ => throw new PlatformNotSupportedException(), + }; +#pragma warning restore IDE0072 // 添加缺失的事例 + + private static string GetFullLibraryName(string libraryName) + { + if (s_isAgentServer && libraryName == "MaaFramework") + libraryName = "MaaAgentServer"; + + if (IsWindows) + return $"{libraryName}.dll"; + if (IsLinux || IsAndroid) + return $"lib{libraryName}.so"; + if (IsOSX) + return $"lib{libraryName}.dylib"; + + throw new PlatformNotSupportedException(); } private static bool IsWindows => OperatingSystem.IsWindows(); private static bool IsLinux => OperatingSystem.IsLinux(); private static bool IsOSX => OperatingSystem.IsMacOS(); private static bool IsAndroid => OperatingSystem.IsAndroid(); - private static bool IsX64 => RuntimeInformation.OSArchitecture == Architecture.X64; - private static bool IsArm64 => RuntimeInformation.OSArchitecture == Architecture.Arm64; } From c34eb44b5f98daae58a1796041a08f9f8a988c24 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Wed, 26 Mar 2025 19:30:30 +0800 Subject: [PATCH 04/19] fix: fix errors of wrapper of MaaAgent --- .../Interop/AgentClient/MaaAgentClientDef.cs | 2 + .../Interop/NativeLibrary.cs | 36 +++++++++++++----- .../MaaAgentClient.cs | 38 +++++++++++-------- .../MaaAgentServer.cs | 15 +++++++- src/MaaFramework.Binding.Native/MaaContext.cs | 12 +++++- .../MaaController.cs | 7 +++- .../MaaResource.cs | 9 ++++- src/MaaFramework.Binding.Native/MaaTasker.cs | 11 ++++++ .../NativeBindingInfo.cs | 35 +++++++++++++++++ src/MaaFramework.Binding/IMaaAgentClient.cs | 2 +- 10 files changed, 135 insertions(+), 32 deletions(-) create mode 100644 src/MaaFramework.Binding.Native/NativeBindingInfo.cs diff --git a/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs index 1823216..ac2ae0c 100644 --- a/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs +++ b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs @@ -12,3 +12,5 @@ global using MaaAgentClientHandle = nint; +namespace MaaFramework.Binding.Interop.Native; + diff --git a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs index fa62127..491dca5 100644 --- a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs +++ b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs @@ -19,7 +19,7 @@ internal static partial class NativeLibrary public static void Init(bool isAgentServer, params string[] paths) { - if (s_libraryHandles.Count > 0) + if (s_libraryHandles.Count != 0) throw new InvalidOperationException("NativeLibrary is already loaded."); s_isAgentServer = isAgentServer; @@ -39,13 +39,29 @@ private static nint GetLibraryHandle(string libraryName) if (s_libraryHandles.TryGetValue(libraryName, out var libraryHandle)) return libraryHandle; - if (TryGetRuntimesPath(libraryName, out var dllPath) - && TryLoad(dllPath, out libraryHandle) - && s_libraryHandles.TryAdd(libraryName, libraryHandle)) - return libraryHandle; + if (!TryGetRuntimesPath(libraryName, out var dllPath) || !TryLoad(dllPath, out libraryHandle)) + { + s_libraryHandles.Add(libraryName, nint.Zero); + NativeBindingInfo.NativeAssemblyDirectory = null; + NativeBindingInfo.IsStatelessMode = false; + NativeBindingInfo.ApiInfo = "Using default dll resolver."; + return nint.Zero; + } + + s_libraryHandles.Add(libraryName, libraryHandle); + + var dllDir = Path.GetDirectoryName(dllPath); + if (s_libraryHandles.Count == 1) + { + NativeBindingInfo.NativeAssemblyDirectory ??= dllDir; + NativeBindingInfo.IsStatelessMode = s_isAgentServer; + NativeBindingInfo.ApiInfo = s_isAgentServer ? "In MaaAgentServer context." : "In MaaFramework context."; + } + + if (NativeBindingInfo.NativeAssemblyDirectory != dllDir) + throw new InvalidOperationException($"The native assembly directory '{NativeBindingInfo.NativeAssemblyDirectory}' was switched to '{dllDir}'."); - _ = s_libraryHandles.TryAdd(libraryName, nint.Zero); - return nint.Zero; + return libraryHandle; } private static bool TryGetRuntimesPath(string libraryName, out string dllPath) @@ -57,11 +73,11 @@ private static bool TryGetRuntimesPath(string libraryName, out string dllPath) private static IEnumerable GetRuntimesPaths(string libraryFullName) { - var args1 = new string[] - { + var args1 = s_searchPath.Concat( + [ Path.GetDirectoryName(s_assembly.Location) ?? "./", Environment.CurrentDirectory, - }; + ]); var args2 = new string[] { $"/runtimes/{GetArchitectureName()}/native/", diff --git a/src/MaaFramework.Binding.Native/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/MaaAgentClient.cs index 7f72f1b..234482d 100644 --- a/src/MaaFramework.Binding.Native/MaaAgentClient.cs +++ b/src/MaaFramework.Binding.Native/MaaAgentClient.cs @@ -1,9 +1,9 @@ using MaaFramework.Binding.Abstractions; +using MaaFramework.Binding.Buffers; using MaaFramework.Binding.Interop.Native; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using static MaaFramework.Binding.Interop.Native.MaaAgentClient; -using static MaaFramework.Binding.Interop.Native.MaaBuffer; namespace MaaFramework.Binding; @@ -13,7 +13,13 @@ namespace MaaFramework.Binding; [DebuggerDisplay("{DebuggerDisplay,nq}")] public class MaaAgentClient : MaaDisposableHandle, IMaaAgentClient { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => $"{{{GetType().Name} {{ Disposed = {IsInvalid} }}}}"; + private string? _debugSocketId; + + [ExcludeFromCodeCoverage(Justification = "Debugger display.")] + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => IsInvalid + ? $"Invalid {GetType().Name}" + : $"{GetType().Name} {{ SocketId = {_debugSocketId}, {nameof(DisposeOptions)} = {DisposeOptions} }}"; /// /// Creates a instance. @@ -43,10 +49,7 @@ public MaaAgentClient(MaaResource resource, DisposeOptions disposeOptions) public required DisposeOptions DisposeOptions { get; set; } /// - /// - /// Wrapper of . - /// - protected override void ReleaseHandle() + protected override void Dispose(bool disposing) { // Cannot destroy Instance before disposing Resource. @@ -55,9 +58,16 @@ protected override void ReleaseHandle() Resource.Dispose(); } - MaaAgentClientDestroy(Handle); + base.Dispose(disposing); } + /// + /// + /// Wrapper of . + /// + protected override void ReleaseHandle() + => MaaAgentClientDestroy(Handle); + /// IMaaResource IMaaAgentClient.Resource { @@ -84,13 +94,12 @@ public required MaaResource Resource /// /// Wrapper of . /// - public bool CreateSocket(string identifier) - { - var handle = MaaStringBufferCreate(); - var ret = MaaStringBufferSetEx(Handle, identifier) && MaaAgentClientCreateSocket(Handle, handle); - MaaStringBufferDestroy(handle); - return ret; - } + public string? CreateSocket(string identifier = "") + => MaaStringBuffer.TryGetValue(out _debugSocketId, handle + => MaaStringBuffer.TrySetValue(handle, identifier) + && MaaAgentClientCreateSocket(Handle, handle)) + ? _debugSocketId + : null; /// /// @@ -106,4 +115,3 @@ public bool LinkStart() public bool LinkStop() => MaaAgentClientDisconnect(Handle); } - diff --git a/src/MaaFramework.Binding.Native/MaaAgentServer.cs b/src/MaaFramework.Binding.Native/MaaAgentServer.cs index 846ce74..989f1d4 100644 --- a/src/MaaFramework.Binding.Native/MaaAgentServer.cs +++ b/src/MaaFramework.Binding.Native/MaaAgentServer.cs @@ -1,16 +1,24 @@ using MaaFramework.Binding.Custom; using MaaFramework.Binding.Interop.Native; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using static MaaFramework.Binding.Interop.Native.MaaAgentServer; -namespace MaaFramework.Binding.Native; +namespace MaaFramework.Binding; /// /// A wrapper class providing a reference implementation for . /// +[DebuggerDisplay("{DebuggerDisplay,nq}")] public class MaaAgentServer : IMaaAgentServer { private readonly MaaMarshaledApiRegistry _actions = new(); private readonly MaaMarshaledApiRegistry _recognitions = new(); + private string? _debugSocketId; + + [ExcludeFromCodeCoverage(Justification = "Debugger display.")] + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay => $"{GetType().Name} {{ SocketId = {_debugSocketId}, CustomActions = [{string.Join(", ", _actions.Names)}] , CustomRecognitions = [{string.Join(" & ", _recognitions.Names)}] }}"; /// public bool Register(string name, T custom) where T : IMaaCustomResource @@ -37,7 +45,10 @@ IMaaCustomRecognition res /// public bool StartUp(string identifier) - => MaaAgentServerStartUp(identifier); + { + _debugSocketId = identifier; + return MaaAgentServerStartUp(_debugSocketId); + } /// public void ShutDown() diff --git a/src/MaaFramework.Binding.Native/MaaContext.cs b/src/MaaFramework.Binding.Native/MaaContext.cs index ff99598..faa922e 100644 --- a/src/MaaFramework.Binding.Native/MaaContext.cs +++ b/src/MaaFramework.Binding.Native/MaaContext.cs @@ -97,7 +97,17 @@ public bool OverrideNext(string nodeName, IEnumerable nextList) /// /// Wrapper of . /// - public MaaTasker Tasker => MaaTasker.Instances[MaaContextGetTasker(Handle)]; + public MaaTasker Tasker + { + get + { + var taskerHandle = MaaContextGetTasker(Handle); + if (NativeBindingInfo.IsStatelessMode) + return new MaaTasker(taskerHandle); + else + return MaaTasker.Instances[taskerHandle]; + } + } object ICloneable.Clone() => Clone(); diff --git a/src/MaaFramework.Binding.Native/MaaController.cs b/src/MaaFramework.Binding.Native/MaaController.cs index e0d2317..8dfdca4 100644 --- a/src/MaaFramework.Binding.Native/MaaController.cs +++ b/src/MaaFramework.Binding.Native/MaaController.cs @@ -11,7 +11,7 @@ namespace MaaFramework.Binding; /// A wrapper class providing a reference implementation for . /// [DebuggerDisplay("{DebuggerDisplay,nq}")] -public abstract class MaaController : MaaCommon, IMaaController +public class MaaController : MaaCommon, IMaaController { [ExcludeFromCodeCoverage(Justification = "Debugger display.")] [DebuggerBrowsable(DebuggerBrowsableState.Never)] @@ -19,6 +19,11 @@ public abstract class MaaController : MaaCommon, IMaaController ? $"Invalid {GetType().Name}" : $"{GetType().Name} {{ }}"; + internal MaaController(MaaControllerHandle handle) + { + SetHandle(handle, needReleased: false); + } + /// /// Creates a instance. /// diff --git a/src/MaaFramework.Binding.Native/MaaResource.cs b/src/MaaFramework.Binding.Native/MaaResource.cs index d2074b6..3c15f53 100644 --- a/src/MaaFramework.Binding.Native/MaaResource.cs +++ b/src/MaaFramework.Binding.Native/MaaResource.cs @@ -9,14 +9,19 @@ namespace MaaFramework.Binding; /// /// A wrapper class providing a reference implementation for . /// -public class MaaResource : MaaCommon, IMaaResource +public class MaaResource : MaaCommon, IMaaResource { private readonly HashSet _postedPaths = []; /// public override string ToString() => IsInvalid ? $"Invalid {GetType().Name}" - : $"{GetType().Name} {{ Paths = {string.Join(" & ", _postedPaths)}, CustomActions = {string.Join(" & ", _actions.Names)}, CustomRecognitions = {string.Join(" & ", _recognitions.Names)} }}"; + : $"{GetType().Name} {{ Paths = [{string.Join(", ", _postedPaths)}], CustomActions = [{string.Join(", ", _actions.Names)}] , CustomRecognitions = [{string.Join(" & ", _recognitions.Names)}] }}"; + + internal MaaResource(MaaResourceHandle handle) + { + SetHandle(handle, needReleased: false); + } /// /// Creates a instance. diff --git a/src/MaaFramework.Binding.Native/MaaTasker.cs b/src/MaaFramework.Binding.Native/MaaTasker.cs index 2e1f403..2991cef 100644 --- a/src/MaaFramework.Binding.Native/MaaTasker.cs +++ b/src/MaaFramework.Binding.Native/MaaTasker.cs @@ -28,6 +28,17 @@ public class MaaTasker : MaaCommon, IMaaTasker /// protected internal static ConcurrentDictionary Instances { get; } = []; + [SetsRequiredMembers] + internal MaaTasker(MaaTaskerHandle handle) + { + SetHandle(handle, needReleased: false); + Resource = new MaaResource(MaaTaskerGetResource(handle)); + Controller = new MaaController(MaaTaskerGetController(handle)); + DisposeOptions = DisposeOptions.None; + Toolkit = new MaaToolkit(); + Utility = new MaaUtility(); + } + /// /// Creates a instance. /// diff --git a/src/MaaFramework.Binding.Native/NativeBindingInfo.cs b/src/MaaFramework.Binding.Native/NativeBindingInfo.cs new file mode 100644 index 0000000..59646c8 --- /dev/null +++ b/src/MaaFramework.Binding.Native/NativeBindingInfo.cs @@ -0,0 +1,35 @@ +using MaaFramework.Binding.Interop.Native; + +namespace MaaFramework.Binding; + +/// +/// Provides information and configuration for native bindings in the MaaFramework. +/// +public static class NativeBindingInfo +{ + /// + /// Gets the directory path where native assemblies are located. + /// + public static string? NativeAssemblyDirectory { get; internal set; } + + /// + /// Gets a value indicating whether the framework is operating in stateless mode. + /// Stateless mode is typically used in server contexts. + /// + public static bool IsStatelessMode { get; internal set; } + + /// + /// Gets or sets the API information string, which provides details about the current API context. + /// + public static string ApiInfo { get; internal set; } = string.Empty; + + /// + /// Initializes the native library with the specified configuration. + /// This method sets up the native library resolver and prepares the environment for native interop. + /// + /// Indicates whether the application is running in an agent server context. + /// Directory paths to search for native libraries. + /// Thrown if the native library is already loaded. + public static void Set(bool isAgentServer, params string[] dllSearchPaths) + => NativeLibrary.Init(isAgentServer, dllSearchPaths); +} diff --git a/src/MaaFramework.Binding/IMaaAgentClient.cs b/src/MaaFramework.Binding/IMaaAgentClient.cs index 4cedc2f..7c0b919 100644 --- a/src/MaaFramework.Binding/IMaaAgentClient.cs +++ b/src/MaaFramework.Binding/IMaaAgentClient.cs @@ -30,7 +30,7 @@ public interface IMaaAgentClient : IMaaDisposable /// /// The connection identifier. /// if the socket was created successfully; otherwise, . - bool CreateSocket(string identifier); + string? CreateSocket(string identifier = ""); /// /// Starts the connection. From 407c40e4966e4ccd301b4bb1f21ddf7db419adbf Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Tue, 29 Apr 2025 20:43:19 +0800 Subject: [PATCH 05/19] feat: support env variable "MAAFW_BINARY_PATH" --- .../Interop/NativeLibrary.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs index 491dca5..5137ef4 100644 --- a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs +++ b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs @@ -23,7 +23,7 @@ public static void Init(bool isAgentServer, params string[] paths) throw new InvalidOperationException("NativeLibrary is already loaded."); s_isAgentServer = isAgentServer; - s_searchPath.AddRange(paths.Where(path => !string.IsNullOrWhiteSpace(path))); + s_searchPath.AddRange(paths); } public static IntPtr NativeAssemblyResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) => libraryName switch @@ -73,21 +73,23 @@ private static bool TryGetRuntimesPath(string libraryName, out string dllPath) private static IEnumerable GetRuntimesPaths(string libraryFullName) { - var args1 = s_searchPath.Concat( + var searchPaths = s_searchPath.Concat( [ - Path.GetDirectoryName(s_assembly.Location) ?? "./", + Environment.GetEnvironmentVariable("MAAFW_BINARY_PATH"), + Path.GetDirectoryName(s_assembly.Location), Environment.CurrentDirectory, - ]); - var args2 = new string[] + ]).Where(path => !string.IsNullOrWhiteSpace(path)); + + var runtimePaths = new[] { - $"/runtimes/{GetArchitectureName()}/native/", - "/" + $"./runtimes/{GetArchitectureName()}/native/", + "./" }; - return from arg1 in args1 - from arg2 in args2 + return from searchPath in searchPaths + from runtimePath in runtimePaths select Path.GetFullPath( - string.Concat(arg1, arg2, libraryFullName)); + Path.Combine(searchPath, runtimePath, libraryFullName)); } #pragma warning disable IDE0072 // 添加缺失的事例 From 599ff15b17cd307ce8c9efc53be36ee270818ea1 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:36:21 +0800 Subject: [PATCH 06/19] feat: add agent sample --- sample/Agent/.config/dotnet-tools.json | 13 +++++ sample/Agent/AgentChild.cs | 58 ++++++++++++++++++++++ sample/Agent/AgentMain.cs | 68 ++++++++++++++++++++++++++ sample/Agent/QuickStart.ps1 | 3 ++ 4 files changed, 142 insertions(+) create mode 100644 sample/Agent/.config/dotnet-tools.json create mode 100644 sample/Agent/AgentChild.cs create mode 100644 sample/Agent/AgentMain.cs create mode 100644 sample/Agent/QuickStart.ps1 diff --git a/sample/Agent/.config/dotnet-tools.json b/sample/Agent/.config/dotnet-tools.json new file mode 100644 index 0000000..fb26112 --- /dev/null +++ b/sample/Agent/.config/dotnet-tools.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-script": { + "version": "1.6.0", + "commands": [ + "dotnet-script" + ], + "rollForward": false + } + } +} \ No newline at end of file diff --git a/sample/Agent/AgentChild.cs b/sample/Agent/AgentChild.cs new file mode 100644 index 0000000..1586026 --- /dev/null +++ b/sample/Agent/AgentChild.cs @@ -0,0 +1,58 @@ +#!/usr/bin/env dotnet-script +#r "nuget: Maa.Framework.Binding.Native, 4.0.0-preview.25163.6" + +using MaaFramework.Binding; +using MaaFramework.Binding.Buffers; +using MaaFramework.Binding.Custom; + +var commandLineArgs = Environment.GetCommandLineArgs(); +if (commandLineArgs.Length < 4) +{ + Console.WriteLine("Call AgentMain.cs instead of this file."); + return 1; +} + +var socketId = commandLineArgs[^1]; +var userPath = commandLineArgs[^2]; +var dllPath = commandLineArgs[^3]; + +NativeBindingInfo.Set(isAgentServer: true, dllPath); // First step +_ = new MaaToolkit(true, userPath); +var agentServer = new MaaAgentServer(); +agentServer.Register(new MyRec()); +agentServer.Register(new MyAct()); +agentServer.StartUp(socketId); +agentServer.Join(); +agentServer.ShutDown(); + +Console.Write("Press any key to exit:"); +Console.ReadKey(); +return 0; + +internal sealed class MyRec : IMaaCustomRecognition +{ + public string Name { get; set; } = nameof(MyRec); + + public bool Analyze(in IMaaContext context, in AnalyzeArgs args, in AnalyzeResults results) + { + Console.WriteLine("{0} Called", Name); + + results.Box.SetValues(0, 0, 100, 100); + results.Detail.SetValue("Hello Client!"); + return true; + } +} + +internal sealed class MyAct : IMaaCustomAction +{ + public string Name { get; set; } = nameof(MyAct); + + public bool Run(in IMaaContext context, in RunArgs args) + { + Console.WriteLine("{0} Called", Name); + Console.WriteLine("recognition detail: {0}", args.RecognitionDetail); + Console.WriteLine("custom action param: {0}", args.ActionParam); + + return true; + } +} diff --git a/sample/Agent/AgentMain.cs b/sample/Agent/AgentMain.cs new file mode 100644 index 0000000..fce0db6 --- /dev/null +++ b/sample/Agent/AgentMain.cs @@ -0,0 +1,68 @@ +#!/usr/bin/env dotnet-script +#r "nuget: Maa.Framework.Native, 4.0.0-preview.25163.6" +#r "nuget: Maa.Framework.Runtime.win-x64, 4.0.0" + +using System.Diagnostics; +using MaaFramework.Binding; + +var toolkit = new MaaToolkit(true); +var resource = new MaaResource(); +var maa = new MaaTasker +{ + Controller = toolkit.AdbDevice.Find().First().ToAdbController(), + Resource = resource, + DisposeOptions = DisposeOptions.All, + Toolkit = toolkit, +}; + +if (!maa.Initialized) + throw new InvalidOperationException("Failed to init tasker."); + +var agent = new MaaAgentClient +{ + Resource = resource, + DisposeOptions = DisposeOptions.All, +}; + +var socketId = agent.CreateSocket(string.Empty) + ?? throw new InvalidOperationException("Failed to create socket."); + +var p = Process.Start(new ProcessStartInfo( + "dotnet",["script", + "AgentChild.cs", + NativeBindingInfo.NativeAssemblyDirectory + ?? throw new ArgumentNullException("Native.BindingInfo.NativeAssemblyDirectory"), + Environment.CurrentDirectory, + socketId]) { UseShellExecute = true }); + +if (!agent.LinkStart()) + throw new InvalidOperationException("Failed to connect."); + +var ppover = """ +{ + "Entry": {"next": "Rec"}, + "Rec": { + "recognition": "Custom", + "custom_recognition": "MyRec", + "action": "Custom", + "custom_action": "MyAct", + "custom_action_param": { + "param": "Hello Server!" + } + } +} +"""; +Console.WriteLine(ppover); + +var detail = maa + .AppendTask("Entry", ppover) + .WaitFor(MaaJobStatus.Succeeded) + .QueryTaskDetail() + ?? throw new InvalidOperationException("Failed to pipeline."); +Console.WriteLine($"pipeline detail: {detail}"); +Console.WriteLine($"MyRec detail: {detail.QueryRecognitionDetail(maa, 1)?.Detail}"); + +agent.LinkStop(); + +Console.Write("Press any key to exit:"); +Console.ReadKey(); diff --git a/sample/Agent/QuickStart.ps1 b/sample/Agent/QuickStart.ps1 new file mode 100644 index 0000000..f5988bd --- /dev/null +++ b/sample/Agent/QuickStart.ps1 @@ -0,0 +1,3 @@ +$ENV:DOTNET_SCRIPT_CACHE_LOCATION="$PSScriptRoot\.cache" +dotnet tool restore +dotnet script AgentMain.cs -s https://api.nuget.org/v3/index.json -s https://maaxyz.github.io/pkg/nuget/index.json \ No newline at end of file From 491bdd713c0d7c941790c563cf1d188b473ca13d Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Thu, 1 May 2025 14:39:32 +0800 Subject: [PATCH 07/19] chore: update Maa.AgentBinary to 1.1.0 --- Directory.Packages.props | 2 +- .../packages.lock.json | 16 ++++++++-------- src/MaaFramework.Native/packages.lock.json | 12 ++++++------ src/MaaFramework/packages.lock.json | 16 ++++++++-------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index cd287b8..1c088bd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,7 @@ true - + diff --git a/src/MaaFramework.Binding.UnitTests/packages.lock.json b/src/MaaFramework.Binding.UnitTests/packages.lock.json index c6089ff..5e33f5c 100644 --- a/src/MaaFramework.Binding.UnitTests/packages.lock.json +++ b/src/MaaFramework.Binding.UnitTests/packages.lock.json @@ -182,15 +182,15 @@ "Maa.Framework.Native": { "type": "Project", "dependencies": { - "Maa.AgentBinary": "[1.0.0, )", + "Maa.AgentBinary": "[1.1.0, )", "Maa.Framework.Binding.Native": "[1.0.0-dev, )" } }, "Maa.AgentBinary": { "type": "CentralTransitive", - "requested": "[1.0.0, )", - "resolved": "1.0.0", - "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA==" + "requested": "[1.1.0, )", + "resolved": "1.1.0", + "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A==" }, "Maa.Framework.Runtimes": { "type": "CentralTransitive", @@ -388,15 +388,15 @@ "Maa.Framework.Native": { "type": "Project", "dependencies": { - "Maa.AgentBinary": "[1.0.0, )", + "Maa.AgentBinary": "[1.1.0, )", "Maa.Framework.Binding.Native": "[1.0.0-dev, )" } }, "Maa.AgentBinary": { "type": "CentralTransitive", - "requested": "[1.0.0, )", - "resolved": "1.0.0", - "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA==" + "requested": "[1.1.0, )", + "resolved": "1.1.0", + "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A==" }, "Maa.Framework.Runtimes": { "type": "CentralTransitive", diff --git a/src/MaaFramework.Native/packages.lock.json b/src/MaaFramework.Native/packages.lock.json index 40fc620..1932700 100644 --- a/src/MaaFramework.Native/packages.lock.json +++ b/src/MaaFramework.Native/packages.lock.json @@ -4,9 +4,9 @@ "net7.0": { "Maa.AgentBinary": { "type": "Direct", - "requested": "[1.0.0, )", - "resolved": "1.0.0", - "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA==" + "requested": "[1.1.0, )", + "resolved": "1.1.0", + "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A==" }, "SonarAnalyzer.CSharp": { "type": "Direct", @@ -27,9 +27,9 @@ "net9.0": { "Maa.AgentBinary": { "type": "Direct", - "requested": "[1.0.0, )", - "resolved": "1.0.0", - "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA==" + "requested": "[1.1.0, )", + "resolved": "1.1.0", + "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A==" }, "SonarAnalyzer.CSharp": { "type": "Direct", diff --git a/src/MaaFramework/packages.lock.json b/src/MaaFramework/packages.lock.json index ce93f8b..fb372b1 100644 --- a/src/MaaFramework/packages.lock.json +++ b/src/MaaFramework/packages.lock.json @@ -64,15 +64,15 @@ "Maa.Framework.Native": { "type": "Project", "dependencies": { - "Maa.AgentBinary": "[1.0.0, )", + "Maa.AgentBinary": "[1.1.0, )", "Maa.Framework.Binding.Native": "[1.0.0-dev, )" } }, "Maa.AgentBinary": { "type": "CentralTransitive", - "requested": "[1.0.0, )", - "resolved": "1.0.0", - "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA==" + "requested": "[1.1.0, )", + "resolved": "1.1.0", + "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A==" } }, "net9.0": { @@ -138,15 +138,15 @@ "Maa.Framework.Native": { "type": "Project", "dependencies": { - "Maa.AgentBinary": "[1.0.0, )", + "Maa.AgentBinary": "[1.1.0, )", "Maa.Framework.Binding.Native": "[1.0.0-dev, )" } }, "Maa.AgentBinary": { "type": "CentralTransitive", - "requested": "[1.0.0, )", - "resolved": "1.0.0", - "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA==" + "requested": "[1.1.0, )", + "resolved": "1.1.0", + "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A==" } } } From 66e2460fffde578e02a16de4244b9ab56a5317c6 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Sat, 3 May 2025 23:27:01 +0800 Subject: [PATCH 08/19] refactor: MaaAgentClient --- .../MaaAgentClient.cs | 106 ++++++++++++++---- .../MaaFramework.Binding.UnitTests.csproj | 13 +-- .../Test_IMaaAgentClient.cs | 97 ++++++++++++++++ .../Test_IMaaAgentServer.cs | 28 +++++ src/MaaFramework.Binding/IMaaAgentClient.cs | 41 +++++-- 5 files changed, 246 insertions(+), 39 deletions(-) create mode 100644 src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs create mode 100644 src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs diff --git a/src/MaaFramework.Binding.Native/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/MaaAgentClient.cs index 234482d..5ddde70 100644 --- a/src/MaaFramework.Binding.Native/MaaAgentClient.cs +++ b/src/MaaFramework.Binding.Native/MaaAgentClient.cs @@ -13,52 +13,71 @@ namespace MaaFramework.Binding; [DebuggerDisplay("{DebuggerDisplay,nq}")] public class MaaAgentClient : MaaDisposableHandle, IMaaAgentClient { - private string? _debugSocketId; + private bool _isConnected; + private Process? _agentServerProcess; [ExcludeFromCodeCoverage(Justification = "Debugger display.")] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => IsInvalid ? $"Invalid {GetType().Name}" - : $"{GetType().Name} {{ SocketId = {_debugSocketId}, {nameof(DisposeOptions)} = {DisposeOptions} }}"; + : $"{GetType().Name} {{ Id = {Id}, IsConnected = {_isConnected} }}"; /// /// Creates a instance. /// + /// The unique identifier used to communicate with the agent server. /// /// Wrapper of . /// - public MaaAgentClient() + public MaaAgentClient(string identifier = "") : base(invalidHandleValue: MaaAgentClientHandle.Zero) { var handle = MaaAgentClientCreate(); SetHandle(handle, needReleased: true); + Id = CreateSocket(identifier).ThrowIfNull(); + + if (!string.IsNullOrEmpty(identifier)) + _ = Id.ThrowIfNotEquals(identifier); } + /// The unique identifier used to communicate with the agent server. /// The resource. - /// The dispose options. - /// + /// [SetsRequiredMembers] - public MaaAgentClient(MaaResource resource, DisposeOptions disposeOptions) - : this() + public MaaAgentClient(string identifier, MaaResource resource) + : this(identifier) { Resource = resource; - DisposeOptions = disposeOptions; } + /// + [SetsRequiredMembers] + public MaaAgentClient(MaaResource resource) + : this("", resource) + { + } + + /// + /// Creates a instance. + /// + /// The unique identifier used to communicate with the agent server. + /// The resource. + /// The instance. + public static MaaAgentClient Create(string identifier, MaaResource resource) + => new(identifier, resource); + + /// + public static MaaAgentClient Create(MaaResource resource) + => new(resource); + /// - public required DisposeOptions DisposeOptions { get; set; } + public string Id { get; } /// protected override void Dispose(bool disposing) { - // Cannot destroy Instance before disposing Resource. - - if (DisposeOptions.HasFlag(DisposeOptions.Resource)) - { - Resource.Dispose(); - } - base.Dispose(disposing); + _agentServerProcess?.Dispose(); } /// @@ -90,15 +109,19 @@ public required MaaResource Resource } } - /// + /// + /// Creates a socket connection with the specified identifier. + /// + /// The specified identifier. + /// if the socket was created successfully; otherwise, . /// /// Wrapper of . /// - public string? CreateSocket(string identifier = "") - => MaaStringBuffer.TryGetValue(out _debugSocketId, handle + protected string? CreateSocket(string identifier = "") + => MaaStringBuffer.TryGetValue(out var socketId, handle => MaaStringBuffer.TrySetValue(handle, identifier) && MaaAgentClientCreateSocket(Handle, handle)) - ? _debugSocketId + ? socketId : null; /// @@ -106,12 +129,51 @@ public required MaaResource Resource /// Wrapper of . /// public bool LinkStart() - => MaaAgentClientConnect(Handle); + => _isConnected = MaaAgentClientConnect(Handle); + + /// + /// + /// Wrapper of . + /// + public bool LinkStart(ProcessStartInfo info) + { + _agentServerProcess = Process.Start(info); + return LinkStart(); + } + + /// + /// + /// Wrapper of . + /// + public bool LinkStart(IMaaAgentClient.AgentServerStartupMethod method) + { + ArgumentException.ThrowIfNullOrEmpty(Id); + ArgumentException.ThrowIfNullOrEmpty(NativeBindingInfo.NativeAssemblyDirectory); + + _agentServerProcess = method.Invoke(Id, NativeBindingInfo.NativeAssemblyDirectory); + return LinkStart(); + } /// /// /// Wrapper of . /// public bool LinkStop() - => MaaAgentClientDisconnect(Handle); + { + if (_isConnected) + { + _isConnected = false; + if (!MaaAgentClientDisconnect(Handle)) + { + _isConnected = true; + return false; + } + } + + return true; + } + + /// + public Process AgentServerProcess => _agentServerProcess + ?? throw new InvalidOperationException($"The agent server process is unavailable or not managed by {nameof(MaaAgentClient)}."); } diff --git a/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj b/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj index 1078b2b..e137071 100644 --- a/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj +++ b/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj @@ -5,25 +5,20 @@ - - + + + PreserveNewest - - - - - + false $(NoWarn);S1121,S1656,S1199,IDE0022,IDE0060,IDE0072 - - $(DefineConstants);MAA_NATIVE; diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs new file mode 100644 index 0000000..ac82346 --- /dev/null +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs @@ -0,0 +1,97 @@ +using MaaFramework.Binding.Abstractions; +using System.Diagnostics; + +namespace MaaFramework.Binding.UnitTests; + +/// +/// Test and . +/// +[TestClass] +// ReSharper disable InconsistentNaming +public class Test_IMaaAgentClient +{ + public static Dictionary NewData => new() + { +#if MAA_NATIVE + { MaaTypes.Native, new MaaAgentClient(new MaaResource()) }, +#endif + }; + public static Dictionary Data { get; private set; } = default!; + + [ClassInitialize] + public static void InitializeClass(TestContext context) + { + Data = NewData; + foreach (var data in Data.Values.Cast()) + { + Assert.IsFalse(data.IsInvalid); + } + } + + [ClassCleanup] + public static void CleanUpClass() + { + Common.DisposeData(Data.Values.Cast()); + } + + [TestMethod] + public void CreateInstances() + { +#if MAA_NATIVE + var newId = Guid.NewGuid().ToString(); + using var native1 = new MaaAgentClient(/*identifier = string.Empty*/) { Resource = new MaaResource() }; + using var native2 = new MaaAgentClient(/*identifier = string.Empty*/ new MaaResource()); + // var native3 = new MaaAgentClient(newId, new MaaResource()); + using var native3 = MaaAgentClient.Create(newId, new MaaResource()); + using var native4 = MaaAgentClient.Create(new MaaResource()); + + Assert.AreNotEqual(native1.Id, native2.Id); + Assert.AreEqual(newId, native3.Id); +#endif + } + + [TestMethod] + [MaaData(MaaTypes.All, nameof(Data))] + public void Interface_Id_Resource(MaaTypes type, IMaaAgentClient maaAgentClient) + { + Assert.AreNotEqual(string.Empty, maaAgentClient.Id); + Assert.IsNotNull(maaAgentClient.Resource); + } + + [TestMethod] + [MaaData(MaaTypes.All, nameof(Data))] + public void Interface_LinkStart_LinkStop_AgentServerProcess(MaaTypes type, IMaaAgentClient maaAgentClient) + { + _ = Assert.ThrowsException(() => + maaAgentClient.AgentServerProcess); + var ret = maaAgentClient.LinkStart((socketId, nativeAssemblyDirectory) => + { + string[] arguments = + [ + typeof(Test_IMaaAgentServer).Assembly.Location, + nativeAssemblyDirectory, + Environment.CurrentDirectory, + socketId + ]; + + return Process.Start(new ProcessStartInfo("dotnet", string.Join(' ', arguments)) + { + UseShellExecute = false, + }); + }); + Assert.IsTrue( + ret); + Assert.IsTrue( // double start + maaAgentClient.LinkStart()); + Assert.IsFalse( + maaAgentClient.AgentServerProcess.HasExited); + + Assert.IsTrue( + maaAgentClient.LinkStop()); + Assert.IsTrue( // double stop + maaAgentClient.LinkStop()); + Task.Delay(100).Wait(); // wait for process exit + Assert.IsTrue( + maaAgentClient.AgentServerProcess.HasExited); + } +} diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs new file mode 100644 index 0000000..a70323c --- /dev/null +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs @@ -0,0 +1,28 @@ +namespace MaaFramework.Binding.UnitTests; + +internal static class Test_IMaaAgentServer +{ + public static int Main() + { + var commandLineArgs = Environment.GetCommandLineArgs(); + if (commandLineArgs.Length < 4) + { + Console.WriteLine("Call AgentMain.cs instead of this file."); + return 1; + } + + var socketId = commandLineArgs[^1]; + var userPath = commandLineArgs[^2]; + var dllPath = commandLineArgs[^3]; + + NativeBindingInfo.Set(isAgentServer: true, dllPath); // First step + _ = new MaaToolkit(true, userPath); + var agentServer = new MaaAgentServer(); + _ = agentServer.Register(Custom.Recognition); + _ = agentServer.Register(Custom.Action); + _ = agentServer.StartUp(socketId); + agentServer.Join(); + agentServer.ShutDown(); + return 0; + } +} diff --git a/src/MaaFramework.Binding/IMaaAgentClient.cs b/src/MaaFramework.Binding/IMaaAgentClient.cs index 7c0b919..83575ea 100644 --- a/src/MaaFramework.Binding/IMaaAgentClient.cs +++ b/src/MaaFramework.Binding/IMaaAgentClient.cs @@ -1,4 +1,5 @@ using MaaFramework.Binding.Abstractions; +using System.Diagnostics; namespace MaaFramework.Binding; @@ -14,9 +15,9 @@ public interface IMaaAgentClient : IMaaAgentClient, IMaaDisposableHandle< public interface IMaaAgentClient : IMaaDisposable { /// - /// Gets or sets whether disposes the when was invoked. + /// Gets the unique identifier used to communicate with the agent server. /// - DisposeOptions DisposeOptions { get; set; } + string Id { get; } /// /// Gets or sets a resource that binds to the . @@ -26,21 +27,45 @@ public interface IMaaAgentClient : IMaaDisposable IMaaResource Resource { get; set; } /// - /// Creates a socket connection with the specified identifier. + /// Starts the connection. /// - /// The connection identifier. - /// if the socket was created successfully; otherwise, . - string? CreateSocket(string identifier = ""); + /// if the connection was started successfully; otherwise, . + bool LinkStart(); /// - /// Starts the connection. + /// Starts the agent server process using the specified and connects to the agent server. /// + /// The process start info. /// if the connection was started successfully; otherwise, . - bool LinkStart(); + bool LinkStart(ProcessStartInfo info); + + /// + /// Starts the agent server process using the specified method and connects to the agent server. + /// + /// The delegate method that defines how to start the agent server process. + /// if the connection was started successfully; otherwise, . + bool LinkStart(AgentServerStartupMethod method); /// /// Stops the connection. /// /// if the connection was stopped successfully; otherwise, . bool LinkStop(); + + /// + /// Represents a method that starts the agent server process. + /// + /// The unique identifier used to communicate with the agent server. + /// The directory path where the native assemblies are located. + /// + /// A instance representing the started agent server process, + /// or if the method is used to synchronize with unmanaged processes. + /// + delegate Process? AgentServerStartupMethod(string identifier, string nativeAssemblyDirectory); + + /// + /// Gets the agent server process managed by from method LinkStart. + /// + /// The process is unavailable or not managed by . + Process AgentServerProcess { get; } } From ca4abb659d96c086ee65867d517344d5903380d8 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Mon, 5 May 2025 22:37:24 +0800 Subject: [PATCH 09/19] refactor: MaaAgentClient.LinkStart() & Dispose() --- .../MaaAgentClient.cs | 86 ++++++++++++++++--- src/MaaFramework.Binding/IMaaAgentClient.cs | 26 +++++- 2 files changed, 98 insertions(+), 14 deletions(-) diff --git a/src/MaaFramework.Binding.Native/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/MaaAgentClient.cs index 5ddde70..84c296f 100644 --- a/src/MaaFramework.Binding.Native/MaaAgentClient.cs +++ b/src/MaaFramework.Binding.Native/MaaAgentClient.cs @@ -77,7 +77,7 @@ public static MaaAgentClient Create(MaaResource resource) protected override void Dispose(bool disposing) { base.Dispose(disposing); - _agentServerProcess?.Dispose(); + KillAndDisposeAgentServerProcess(); } /// @@ -135,23 +135,74 @@ public bool LinkStart() /// /// Wrapper of . /// - public bool LinkStart(ProcessStartInfo info) + public bool LinkStart(ProcessStartInfo info, CancellationToken cancellationToken = default) { - _agentServerProcess = Process.Start(info); - return LinkStart(); + if (_agentServerProcess is null or { HasExited: true }) + { + _agentServerProcess?.Dispose(); + _agentServerProcess = Process.Start(info); + + if (_agentServerProcess is null or { HasExited: true }) + return false; + } + + return LinkStartUnlessProcessExit(_agentServerProcess, cancellationToken).GetAwaiter().GetResult(); } /// /// /// Wrapper of . /// - public bool LinkStart(IMaaAgentClient.AgentServerStartupMethod method) + public bool LinkStart(IMaaAgentClient.AgentServerStartupMethod method, CancellationToken cancellationToken = default) { - ArgumentException.ThrowIfNullOrEmpty(Id); - ArgumentException.ThrowIfNullOrEmpty(NativeBindingInfo.NativeAssemblyDirectory); + if (_agentServerProcess is null or { HasExited: true }) + { + if (string.IsNullOrEmpty(Id) || string.IsNullOrEmpty(NativeBindingInfo.NativeAssemblyDirectory)) + { + throw new InvalidOperationException( + $"The {nameof(Id)}({Id ?? ""})" + + $" or {nameof(NativeBindingInfo.NativeAssemblyDirectory)}({NativeBindingInfo.NativeAssemblyDirectory ?? ""})" + + $" is invalid."); + } - _agentServerProcess = method.Invoke(Id, NativeBindingInfo.NativeAssemblyDirectory); - return LinkStart(); + _agentServerProcess?.Dispose(); + _agentServerProcess = method.Invoke(Id, NativeBindingInfo.NativeAssemblyDirectory); + + if (_agentServerProcess is null or { HasExited: true }) + return false; + } + + return LinkStartUnlessProcessExit(_agentServerProcess, cancellationToken).GetAwaiter().GetResult(); + } + + /// + public async Task LinkStartUnlessProcessExit(Process process, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(process); + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + if (process.HasExited) + return false; + + var serverExitTask = process.WaitForExitAsync(cts.Token); + var linkStartTask = Task.Run(LinkStart, cts.Token); + var completedTask = await Task.WhenAny(linkStartTask, serverExitTask).ConfigureAwait(false); + + try + { + cts.Token.ThrowIfCancellationRequested(); + if (completedTask == serverExitTask) + return false; + + return linkStartTask.Result; + } + finally + { +#if NET8_0_OR_GREATER + await cts.CancelAsync(); +#else + cts.Cancel(); +#endif + } } /// @@ -175,5 +226,20 @@ public bool LinkStop() /// public Process AgentServerProcess => _agentServerProcess - ?? throw new InvalidOperationException($"The agent server process is unavailable or not managed by {nameof(MaaAgentClient)}."); + ?? throw new InvalidOperationException($"The agent server process is unavailable or not managed by {nameof(MaaAgentClient)}."); + + private void KillAndDisposeAgentServerProcess() + { + if (_agentServerProcess is null) + return; + + if (!_agentServerProcess.HasExited) + { + _agentServerProcess.Kill(entireProcessTree: true); + _agentServerProcess.WaitForExit(); + } + + _agentServerProcess.Dispose(); + _agentServerProcess = null; + } } diff --git a/src/MaaFramework.Binding/IMaaAgentClient.cs b/src/MaaFramework.Binding/IMaaAgentClient.cs index 83575ea..3a62c93 100644 --- a/src/MaaFramework.Binding/IMaaAgentClient.cs +++ b/src/MaaFramework.Binding/IMaaAgentClient.cs @@ -34,17 +34,36 @@ public interface IMaaAgentClient : IMaaDisposable /// /// Starts the agent server process using the specified and connects to the agent server. + /// To start a new process, the current must have exited first. /// /// The process start info. + /// An optional token to cancel the asynchronous operation waiting for the connection. /// if the connection was started successfully; otherwise, . - bool LinkStart(ProcessStartInfo info); + /// The has had cancellation requested. + bool LinkStart(ProcessStartInfo info, CancellationToken cancellationToken = default); /// /// Starts the agent server process using the specified method and connects to the agent server. + /// To start a new process, the current must have exited first. /// /// The delegate method that defines how to start the agent server process. + /// An optional token to cancel the asynchronous operation waiting for the connection. /// if the connection was started successfully; otherwise, . - bool LinkStart(AgentServerStartupMethod method); + /// One or more parameters required by the are invalid. + /// The has had cancellation requested. + bool LinkStart(AgentServerStartupMethod method, CancellationToken cancellationToken = default); + + /// + /// Starts the connection asynchronously unless the process has exited. + /// + /// The process to monitor for exit status. + /// An optional token to cancel the asynchronous operation waiting for the connection. + /// + /// A task that represents the asynchronous operation. The task result contains + /// if the connection was started successfully; otherwise, . + /// + /// The has had cancellation requested. + Task LinkStartUnlessProcessExit(Process process, CancellationToken cancellationToken); /// /// Stops the connection. @@ -58,8 +77,7 @@ public interface IMaaAgentClient : IMaaDisposable /// The unique identifier used to communicate with the agent server. /// The directory path where the native assemblies are located. /// - /// A instance representing the started agent server process, - /// or if the method is used to synchronize with unmanaged processes. + /// A new that is associated with the process resource, or if no process resource is started. /// delegate Process? AgentServerStartupMethod(string identifier, string nativeAssemblyDirectory); From 55ab55e4277c6dfe13472203a2d3f69a7170b8f1 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Tue, 6 May 2025 21:20:05 +0800 Subject: [PATCH 10/19] feat: add `Shared` instance for toolkit & utility --- src/MaaFramework.Binding.Native/MaaTasker.cs | 10 ++++++---- src/MaaFramework.Binding.Native/MaaToolkit.cs | 5 +++++ src/MaaFramework.Binding.Native/MaaUtility.cs | 5 +++++ src/MaaFramework.Binding.UnitTests/Test_Buffers.cs | 4 ++-- src/MaaFramework.Binding.UnitTests/Test_Common.cs | 6 +++--- .../Test_IMaaController.cs | 3 +-- src/MaaFramework.Binding.UnitTests/Test_IMaaToolkit.cs | 2 +- src/MaaFramework.Binding.UnitTests/Test_IMaaUtility.cs | 2 +- 8 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/MaaFramework.Binding.Native/MaaTasker.cs b/src/MaaFramework.Binding.Native/MaaTasker.cs index 2991cef..884ddf8 100644 --- a/src/MaaFramework.Binding.Native/MaaTasker.cs +++ b/src/MaaFramework.Binding.Native/MaaTasker.cs @@ -35,8 +35,8 @@ internal MaaTasker(MaaTaskerHandle handle) Resource = new MaaResource(MaaTaskerGetResource(handle)); Controller = new MaaController(MaaTaskerGetController(handle)); DisposeOptions = DisposeOptions.None; - Toolkit = new MaaToolkit(); - Utility = new MaaUtility(); + Toolkit = MaaToolkit.Shared; + Utility = MaaUtility.Shared; } /// @@ -54,8 +54,10 @@ public MaaTasker(bool toolkitInit = false) throw new InvalidOperationException($"This {nameof(MaaTasker)} already added to {nameof(Instances)}."); SetHandle(handle, needReleased: true); - Toolkit = new MaaToolkit(toolkitInit); - Utility = new MaaUtility(); + Toolkit = MaaToolkit.Shared; + Utility = MaaUtility.Shared; + if (toolkitInit) + _ = Toolkit.Config.InitOption().ThrowIfFalse(); } /// The controller. diff --git a/src/MaaFramework.Binding.Native/MaaToolkit.cs b/src/MaaFramework.Binding.Native/MaaToolkit.cs index 357ce6e..7c514e1 100644 --- a/src/MaaFramework.Binding.Native/MaaToolkit.cs +++ b/src/MaaFramework.Binding.Native/MaaToolkit.cs @@ -12,6 +12,11 @@ namespace MaaFramework.Binding; /// public class MaaToolkit : IMaaToolkit { + /// + /// Gets the shared instance. + /// + public static MaaToolkit Shared { get; } = new(); + /// /// Creates a instance. /// diff --git a/src/MaaFramework.Binding.Native/MaaUtility.cs b/src/MaaFramework.Binding.Native/MaaUtility.cs index af10545..2d8cbec 100644 --- a/src/MaaFramework.Binding.Native/MaaUtility.cs +++ b/src/MaaFramework.Binding.Native/MaaUtility.cs @@ -8,6 +8,11 @@ namespace MaaFramework.Binding; /// public class MaaUtility : IMaaUtility { + /// + /// Gets the shared instance. + /// + public static MaaUtility Shared { get; } = new(); + /// /// /// Wrapper of . diff --git a/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs b/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs index 6fa19f1..5fcf574 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs @@ -22,9 +22,9 @@ public class Test_Buffers new MaaStringBuffer(), new MaaImageListBuffer(), new MaaStringListBuffer(), - (AdbDeviceListBuffer)new MaaToolkit().AdbDevice.Find(), + (AdbDeviceListBuffer)MaaToolkit.Shared.AdbDevice.Find(), #if MAA_WIN32 - (DesktopWindowListBuffer)new MaaToolkit().Desktop.Window.Find(), + (DesktopWindowListBuffer)MaaToolkit.Shared.Desktop.Window.Find(), #endif ] }, diff --git a/src/MaaFramework.Binding.UnitTests/Test_Common.cs b/src/MaaFramework.Binding.UnitTests/Test_Common.cs index 8594752..bfaddca 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Common.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Common.cs @@ -27,7 +27,7 @@ private static void InitializeInfo(TestContext testContext) #if GITHUB_ACTIONS // use environment "AdbPath" AdbDeviceInfo[] devices = []; #else - var devices = new MaaToolkit().AdbDevice.Find(); + var devices = MaaToolkit.Shared.AdbDevice.Find(); #endif // 请修改 TestParam.runsettings,并在测试资源管理器——设置——配置运行设置 @@ -52,8 +52,8 @@ public static void InitializeAssembly(TestContext testContext) { ArgumentNullException.ThrowIfNull(testContext); - _ = new MaaUtility().SetOption(GlobalOption.LogDir, DebugPath); - _ = new MaaUtility().SetOption(GlobalOption.StdoutLevel, LoggingLevel.Off); + _ = MaaUtility.Shared.SetOptionLogDir(DebugPath); + _ = MaaUtility.Shared.SetOptionStdoutLevel(LoggingLevel.Off); InitializeInfo(testContext); } diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs index 72b2ea1..1e099e0 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs @@ -112,8 +112,7 @@ public void CreateInstances() #if !GITHUB_ACTIONS #region MaaWin32Controller - var toolkit = new MaaToolkit(); - var windowInfo = toolkit.Desktop.Window.Find().First(static x + var windowInfo = MaaToolkit.Shared.Desktop.Window.Find().First(static x => x.Name.Contains("Visual Studio", StringComparison.OrdinalIgnoreCase) || x.Name.Contains("Maa", StringComparison.OrdinalIgnoreCase)); diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaToolkit.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaToolkit.cs index a6de2da..33bbb88 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaToolkit.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaToolkit.cs @@ -12,7 +12,7 @@ public class Test_IMaaToolkit public static Dictionary NewData => new() { #if MAA_NATIVE - { MaaTypes.Native, new MaaToolkit() }, + { MaaTypes.Native, MaaToolkit.Shared }, #endif }; public static Dictionary Data { get; private set; } = default!; diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaUtility.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaUtility.cs index d50a234..3cadf96 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaUtility.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaUtility.cs @@ -10,7 +10,7 @@ public class Test_IMaaMaaUtility public static Dictionary NewData => new() { #if MAA_NATIVE - { MaaTypes.Native, new MaaUtility() }, + { MaaTypes.Native, MaaUtility.Shared }, #endif }; public static Dictionary Data { get; private set; } = default!; From 4fa224bf5f87c9b9121a5c4c6f1cd54601569928 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Wed, 7 May 2025 21:32:11 +0800 Subject: [PATCH 11/19] [Breaking change] chore: improve readability for methods of MaaOptionExtensions --- .../Test_Common.cs | 4 +-- .../Test_IMaaController.cs | 2 +- .../Test_IMaaResource.cs | 2 +- .../Test_IMaaTasker.cs | 10 +++---- .../Extensions/MaaOptionExtensions.cs | 26 +++++++++---------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/MaaFramework.Binding.UnitTests/Test_Common.cs b/src/MaaFramework.Binding.UnitTests/Test_Common.cs index bfaddca..4305396 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Common.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Common.cs @@ -52,8 +52,8 @@ public static void InitializeAssembly(TestContext testContext) { ArgumentNullException.ThrowIfNull(testContext); - _ = MaaUtility.Shared.SetOptionLogDir(DebugPath); - _ = MaaUtility.Shared.SetOptionStdoutLevel(LoggingLevel.Off); + _ = MaaUtility.Shared.SetOption_LogDir(DebugPath); + _ = MaaUtility.Shared.SetOption_StdoutLevel(LoggingLevel.Off); InitializeInfo(testContext); } diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs index 1e099e0..901a9db 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs @@ -63,7 +63,7 @@ static void InitializeData(AdbInputMethods inputPreset) .Wait() .ThrowIfNot(MaaJobStatus.Succeeded); Assert.IsTrue( - data.SetOption(ControllerOption.ScreenshotTargetShortSide, 720)); + data.SetOption_ScreenshotTargetShortSide(720)); } } } diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaResource.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaResource.cs index 0036dcc..c371e46 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaResource.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaResource.cs @@ -32,7 +32,7 @@ public static void InitializeClass(TestContext context) data.Callback += Common.NotificationHandlerRegistry.OnCallback; data.Callback += Common.OnResourceLoading.ToCallback(); - _ = data.SetOption(ResourceOption.InferenceDevice, InferenceDevice.CPU); + _ = data.SetOption_InferenceDevice(InferenceDevice.CPU); } } diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaTasker.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaTasker.cs index 1e76282..84d66ab 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaTasker.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaTasker.cs @@ -52,15 +52,15 @@ public static void InitializeClass(TestContext context) .Wait() .ThrowIfNot(MaaJobStatus.Succeeded); _ = data.Resource - .SetOption(ResourceOption.InferenceDevice, InferenceDevice.CPU); + .SetOption_InferenceDevice(InferenceDevice.CPU); _ = data.Resource - .SetOption(ResourceOption.InferenceExecutionProvider, InferenceExecutionProvider.CPU); + .SetOption_InferenceExecutionProvider(InferenceExecutionProvider.CPU); _ = data.Controller .LinkStart() .Wait() .ThrowIfNot(MaaJobStatus.Succeeded); _ = data.Controller - .SetOption(ControllerOption.ScreenshotTargetShortSide, 720); + .SetOption_ScreenshotTargetShortSide(720); Assert.IsTrue(data.IsInitialized); } } @@ -328,7 +328,7 @@ public void MaaTaskJob_Query(MaaTypes type, IMaaTasker maaTasker, string taskEnt { Assert.IsNotNull(maaTasker); Assert.IsTrue( - maaTasker.Utility.SetOption(GlobalOption.DebugMode, debugMode)); + maaTasker.Utility.SetOption_DebugMode(debugMode)); var job = maaTasker.AppendTask(taskEntryName, diff); @@ -358,7 +358,7 @@ public void MaaTaskJob_Query(MaaTypes type, IMaaTasker maaTasker, string taskEnt } Assert.IsTrue( - maaTasker.Utility.SetOption(GlobalOption.DebugMode, false)); + maaTasker.Utility.SetOption_DebugMode(false)); } [TestMethod] diff --git a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs index e448004..f370ede 100644 --- a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs +++ b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs @@ -10,54 +10,54 @@ namespace MaaFramework.Binding; public static class MaaOptionExtensions { /// - public static bool SetOptionScreenshotTargetLongSide(this IMaaOption? opt, int value) + public static bool SetOption_ScreenshotTargetLongSide(this IMaaOption? opt, int value) => opt?.SetOption(ControllerOption.ScreenshotTargetLongSide, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionScreenshotTargetShortSide(this IMaaOption? opt, int value) + public static bool SetOption_ScreenshotTargetShortSide(this IMaaOption? opt, int value) => opt?.SetOption(ControllerOption.ScreenshotTargetShortSide, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionScreenshotUseRawSize(this IMaaOption? opt, bool value) + public static bool SetOption_ScreenshotUseRawSize(this IMaaOption? opt, bool value) => opt?.SetOption(ControllerOption.ScreenshotUseRawSize, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionRecording(this IMaaOption? opt, bool value) + public static bool SetOption_Recording(this IMaaOption? opt, bool value) => opt?.SetOption(ControllerOption.Recording, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionLogDir(this IMaaOption? opt, string value) + public static bool SetOption_LogDir(this IMaaOption? opt, string value) => opt?.SetOption(GlobalOption.LogDir, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionSaveDraw(this IMaaOption? opt, bool value) + public static bool SetOption_SaveDraw(this IMaaOption? opt, bool value) => opt?.SetOption(GlobalOption.SaveDraw, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionRecording(this IMaaOption? opt, bool value) + public static bool SetOption_Recording(this IMaaOption? opt, bool value) => opt?.SetOption(GlobalOption.Recording, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionStdoutLevel(this IMaaOption? opt, LoggingLevel value) + public static bool SetOption_StdoutLevel(this IMaaOption? opt, LoggingLevel value) => opt?.SetOption(GlobalOption.StdoutLevel, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionShowHitDraw(this IMaaOption? opt, bool value) + public static bool SetOption_ShowHitDraw(this IMaaOption? opt, bool value) => opt?.SetOption(GlobalOption.ShowHitDraw, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionDebugMode(this IMaaOption? opt, bool value) + public static bool SetOption_DebugMode(this IMaaOption? opt, bool value) => opt?.SetOption(GlobalOption.DebugMode, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionInferenceDevice(this IMaaOption? opt, InferenceDevice value) + public static bool SetOption_InferenceDevice(this IMaaOption? opt, InferenceDevice value) => opt?.SetOption(ResourceOption.InferenceDevice, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionInferenceDevice(this IMaaOption? opt, int value) + public static bool SetOption_InferenceDevice(this IMaaOption? opt, int value) => opt?.SetOption(ResourceOption.InferenceDevice, value) ?? throw new ArgumentNullException(nameof(opt)); /// - public static bool SetOptionInferenceDevice(this IMaaOption? opt, InferenceExecutionProvider value) + public static bool SetOption_InferenceExecutionProvider(this IMaaOption? opt, InferenceExecutionProvider value) => opt?.SetOption(ResourceOption.InferenceExecutionProvider, value) ?? throw new ArgumentNullException(nameof(opt)); } From 25efb4bc9223c2cf41baddcdb9ce770881626984 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Thu, 8 May 2025 16:45:15 +0800 Subject: [PATCH 12/19] refactor: MaaAgentServer --- .../MaaAgentServer.cs | 148 ++++++++++++++---- .../Test_IMaaAgentServer.cs | 22 ++- src/MaaFramework.Binding/IMaaAgentServer.cs | 23 +-- 3 files changed, 146 insertions(+), 47 deletions(-) diff --git a/src/MaaFramework.Binding.Native/MaaAgentServer.cs b/src/MaaFramework.Binding.Native/MaaAgentServer.cs index 989f1d4..83d282c 100644 --- a/src/MaaFramework.Binding.Native/MaaAgentServer.cs +++ b/src/MaaFramework.Binding.Native/MaaAgentServer.cs @@ -10,55 +10,143 @@ namespace MaaFramework.Binding; /// A wrapper class providing a reference implementation for . /// [DebuggerDisplay("{DebuggerDisplay,nq}")] -public class MaaAgentServer : IMaaAgentServer +public sealed class MaaAgentServer : IMaaAgentServer { + /// + /// Gets the unique identifier used to communicate with the agent client. + /// + public static string CurrentId { get; private set; } + + /// + /// Gets the current instance. + /// + public static MaaAgentServer Current { get; } + + /// + /// Creates a instance. + /// + private MaaAgentServer() { } + static MaaAgentServer() + { + NativeLibrary.Init(isAgentServer: true); + CurrentId = string.Empty; + Current = new(); + } + private readonly MaaMarshaledApiRegistry _actions = new(); private readonly MaaMarshaledApiRegistry _recognitions = new(); - private string? _debugSocketId; [ExcludeFromCodeCoverage(Justification = "Debugger display.")] [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay => $"{GetType().Name} {{ SocketId = {_debugSocketId}, CustomActions = [{string.Join(", ", _actions.Names)}] , CustomRecognitions = [{string.Join(" & ", _recognitions.Names)}] }}"; + private string DebuggerDisplay => $"{GetType().Name} {{ {nameof(CurrentId)} = {CurrentId}, CustomActions = [{string.Join(", ", _actions.Names)}] , CustomRecognitions = [{string.Join(" & ", _recognitions.Names)}] }}"; + + IMaaAgentServer IMaaAgentServer.WithIdentifier(string identifier) => WithIdentifier(identifier); + IMaaAgentServer IMaaAgentServer.Register(string name, T custom) => Register(name, custom); + IMaaAgentServer IMaaAgentServer.Register(T custom) => Register(custom); + IMaaAgentServer IMaaAgentServer.StartUp() => StartUp(); + IMaaAgentServer IMaaAgentServer.ShutDown() => ShutDown(); + IMaaAgentServer IMaaAgentServer.Join() => Join(); + IMaaAgentServer IMaaAgentServer.Detach() => Detach(); + + /// + public MaaAgentServer WithIdentifier(string identifier) + { + CurrentId = identifier; + return this; + } - /// - public bool Register(string name, T custom) where T : IMaaCustomResource + /// + public MaaAgentServer Register(string name, T custom) where T : IMaaCustomResource { custom.Name = name; return Register(custom); } - /// + /// /// /// Wrapper of and . /// - public bool Register(T custom) where T : IMaaCustomResource => custom switch + public MaaAgentServer Register(T custom) where T : IMaaCustomResource { - IMaaCustomAction res - => MaaAgentServerRegisterCustomAction(res.Name, res.Convert(out var callback), nint.Zero) - && _actions.Register(res.Name, callback), - IMaaCustomRecognition res - => MaaAgentServerRegisterCustomRecognition(res.Name, res.Convert(out var callback), nint.Zero) - && _recognitions.Register(res.Name, callback), - _ - => throw new NotImplementedException($"Type '{typeof(T)}' is not implemented."), - }; - - /// - public bool StartUp(string identifier) + var ret = custom switch + { + IMaaCustomAction res + => MaaAgentServerRegisterCustomAction(res.Name, res.Convert(out var callback), nint.Zero) + && _actions.Register(res.Name, callback), + IMaaCustomRecognition res + => MaaAgentServerRegisterCustomRecognition(res.Name, res.Convert(out var callback), nint.Zero) + && _recognitions.Register(res.Name, callback), + _ + => throw new NotImplementedException($"Type '{typeof(T)}' is not implemented."), + }; + _ = ret.ThrowIfFalse(); + return this; + } + + /// + /// + /// Wrapper of . + /// + public MaaAgentServer StartUp() { - _debugSocketId = identifier; - return MaaAgentServerStartUp(_debugSocketId); + if (string.IsNullOrEmpty(CurrentId)) + throw new InvalidOperationException("Identifier is not set. Use 'WithIdentifier' method to set it."); + _ = MaaAgentServerStartUp(CurrentId).ThrowIfFalse(); + return this; } - /// - public void ShutDown() - => MaaAgentServerShutDown(); + /// + /// + /// Wrapper of . + /// + public MaaAgentServer ShutDown() + { + MaaAgentServerShutDown(); + return this; + } - /// - public void Join() - => MaaAgentServerJoin(); + /// + /// + /// Wrapper of . + /// + public MaaAgentServer Join() + { + MaaAgentServerJoin(); + return this; + } + + /// + /// + /// Wrapper of . + /// + public MaaAgentServer Detach() + { + MaaAgentServerDetach(); + return this; + } +} - /// - public void Detach() - => MaaAgentServerDetach(); +/// +/// A static class providing extension methods for . +/// +public static class MaaAgentServerExtensions +{ + /// + /// + public static MaaAgentServer WithToolkitConfig_InitOption(this MaaAgentServer server, string userPath = nameof(Environment.CurrentDirectory), [StringSyntax("Json")] string defaultJson = "{}") + { + _ = MaaToolkit.Shared.Config.InitOption(userPath, defaultJson).ThrowIfFalse(); + return server; + } + + /// + /// Configures the MaaAgentServer to use the specified native libraries. + /// + /// The server. + /// The directory paths to search for native libraries. + public static MaaAgentServer WithNativeLibrary(this MaaAgentServer server, params string[] paths) + { + NativeLibrary.Init(true, paths); + return server; + } } diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs index a70323c..de61aa6 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs @@ -14,15 +14,21 @@ public static int Main() var socketId = commandLineArgs[^1]; var userPath = commandLineArgs[^2]; var dllPath = commandLineArgs[^3]; + Test(socketId, userPath, dllPath); - NativeBindingInfo.Set(isAgentServer: true, dllPath); // First step - _ = new MaaToolkit(true, userPath); - var agentServer = new MaaAgentServer(); - _ = agentServer.Register(Custom.Recognition); - _ = agentServer.Register(Custom.Action); - _ = agentServer.StartUp(socketId); - agentServer.Join(); - agentServer.ShutDown(); return 0; } + + public static void Test(string id, string userPath, string dllPath) + { + _ = MaaAgentServer.Current // test double call + .WithIdentifier(id).WithIdentifier(id) + .WithNativeLibrary(dllPath).WithNativeLibrary(dllPath) + .WithToolkitConfig_InitOption(userPath).WithToolkitConfig_InitOption(userPath) + .Register(Custom.Recognition) // cat not double call from here + .Register(Custom.Action) + .StartUp() + .Join().Join() // test double call + .ShutDown().ShutDown(); + } } diff --git a/src/MaaFramework.Binding/IMaaAgentServer.cs b/src/MaaFramework.Binding/IMaaAgentServer.cs index 53e6661..5f73052 100644 --- a/src/MaaFramework.Binding/IMaaAgentServer.cs +++ b/src/MaaFramework.Binding/IMaaAgentServer.cs @@ -7,37 +7,42 @@ namespace MaaFramework.Binding; /// public interface IMaaAgentServer { + /// + /// Configures the unique identifier used to communicate with the agent client. + /// + /// The unique identifier used to communicate with the agent client. + IMaaAgentServer WithIdentifier(string identifier); + /// /// Registers a or in the . /// /// The or . /// The new name that will be used to reference the custom resource. /// The custom resource instance to register. - /// if the registration was successful; otherwise, . - bool Register(string name, T custom) where T : IMaaCustomResource; + /// Thrown if the registration fails. + IMaaAgentServer Register(string name, T custom) where T : IMaaCustomResource; /// - bool Register(T custom) where T : IMaaCustomResource; + IMaaAgentServer Register(T custom) where T : IMaaCustomResource; /// /// Starts up the agent server to prepare for receiving client messages from the specified connection. /// - /// The connection identifier. - /// if the server started successfully; otherwise, . - bool StartUp(string identifier); + /// Thrown if the registration fails. + IMaaAgentServer StartUp(); /// /// Shuts down the agent server. /// - void ShutDown(); + IMaaAgentServer ShutDown(); /// /// Blocks the calling thread until the thread for receiving client messages finishes its execution. /// - void Join(); + IMaaAgentServer Join(); /// /// Separates the thread for receiving client messages, allowing execution to continue independently. /// - void Detach(); + IMaaAgentServer Detach(); } From 4b833eaefa33ed9ebc3a4c51435a2ef0e112ab05 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Fri, 9 May 2025 18:45:32 +0800 Subject: [PATCH 13/19] refactor: NativeBindingContext NativeBindingInfo -> NativeBindingContext --- .../Interop/NativeLibrary.cs | 78 ++++++++--------- .../MaaAgentClient.cs | 10 +-- .../MaaAgentServer.cs | 4 +- src/MaaFramework.Binding.Native/MaaContext.cs | 2 +- .../NativeBindingContext.cs | 85 +++++++++++++++++++ .../NativeBindingInfo.cs | 35 -------- .../MaaFramework.Binding.UnitTests.csproj | 2 +- .../Enums/Binding/ApiInfoFlags.cs | 44 ++++++++++ src/MaaFramework.Binding/IMaaAgentClient.cs | 5 +- .../MaaFramework.Binding.csproj | 4 + 10 files changed, 182 insertions(+), 87 deletions(-) create mode 100644 src/MaaFramework.Binding.Native/NativeBindingContext.cs delete mode 100644 src/MaaFramework.Binding.Native/NativeBindingInfo.cs create mode 100644 src/MaaFramework.Binding/Enums/Binding/ApiInfoFlags.cs diff --git a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs index 5137ef4..2cfd936 100644 --- a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs +++ b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs @@ -8,25 +8,23 @@ internal static partial class NativeLibrary { private static readonly Assembly s_assembly = typeof(NativeLibrary).Assembly; - private static bool s_isAgentServer; - private static readonly List s_searchPath = []; - private static readonly Dictionary s_libraryHandles = []; - #pragma warning disable CA2255 // 不应在库中使用 “ModuleInitializer” 属性 - [ModuleInitializer] - internal static void SetNativeAssemblyResolver() => SetDllImportResolver(s_assembly, NativeAssemblyResolver); -#pragma warning restore CA2255 // 不应在库中使用 “ModuleInitializer” 属性 +#pragma warning disable S2223 // Non-constant static fields should not be visible - public static void Init(bool isAgentServer, params string[] paths) - { - if (s_libraryHandles.Count != 0) - throw new InvalidOperationException("NativeLibrary is already loaded."); + internal static bool IsLoaded; + internal static string LoadedDirectory = string.Empty; + internal static readonly Dictionary LoadedLibraryHandles = []; - s_isAgentServer = isAgentServer; - s_searchPath.AddRange(paths); - } + internal static ApiInfoFlags ApiInfo; + internal static readonly List SearchPath = []; + + [ModuleInitializer] + internal static void SetNativeLibraryResolver() => SetDllImportResolver(s_assembly, NativeLibraryResolver); + +#pragma warning restore S2223 // Non-constant static fields should not be visible +#pragma warning restore CA2255 // 不应在库中使用 “ModuleInitializer” 属性 - public static IntPtr NativeAssemblyResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) => libraryName switch + public static nint NativeLibraryResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) => libraryName switch { "MaaFramework" or "MaaToolkit" or "MaaAgentServer" or "MaaAgentClient" => GetLibraryHandle(libraryName), @@ -36,32 +34,36 @@ public static void Init(bool isAgentServer, params string[] paths) private static nint GetLibraryHandle(string libraryName) { - if (s_libraryHandles.TryGetValue(libraryName, out var libraryHandle)) + if (LoadedLibraryHandles.TryGetValue(libraryName, out var libraryHandle)) return libraryHandle; - if (!TryGetRuntimesPath(libraryName, out var dllPath) || !TryLoad(dllPath, out libraryHandle)) - { - s_libraryHandles.Add(libraryName, nint.Zero); - NativeBindingInfo.NativeAssemblyDirectory = null; - NativeBindingInfo.IsStatelessMode = false; - NativeBindingInfo.ApiInfo = "Using default dll resolver."; - return nint.Zero; - } + IsLoaded = true; + var resolver = TryGetRuntimesPath(libraryName, out var dllPath) && TryLoad(dllPath, out libraryHandle) + ? ApiInfoFlags.UseBindingResolver + : ApiInfoFlags.UseDefaultResolver; + SetBindingContext( + resolver, + dllDir: Path.GetDirectoryName(dllPath) ?? string.Empty, + isFirst: LoadedLibraryHandles.Count == 0); + + LoadedLibraryHandles.Add(libraryName, libraryHandle); + return libraryHandle; + } - s_libraryHandles.Add(libraryName, libraryHandle); + private static void SetBindingContext(ApiInfoFlags resolver, string dllDir, bool isFirst) + { + if (ApiInfo.HasFlag_ResolverExcept(resolver)) + throw new InvalidOperationException($"The resolver '{ApiInfo}' was attempted to switch to '{resolver}'."); - var dllDir = Path.GetDirectoryName(dllPath); - if (s_libraryHandles.Count == 1) - { - NativeBindingInfo.NativeAssemblyDirectory ??= dllDir; - NativeBindingInfo.IsStatelessMode = s_isAgentServer; - NativeBindingInfo.ApiInfo = s_isAgentServer ? "In MaaAgentServer context." : "In MaaFramework context."; - } + if (isFirst) + LoadedDirectory = dllDir; - if (NativeBindingInfo.NativeAssemblyDirectory != dllDir) - throw new InvalidOperationException($"The native assembly directory '{NativeBindingInfo.NativeAssemblyDirectory}' was switched to '{dllDir}'."); + if (LoadedDirectory != dllDir) + throw new InvalidOperationException($"The native library directory '{LoadedDirectory}' was attempted to switch to '{dllDir}'."); - return libraryHandle; + ApiInfo |= resolver; + if (!ApiInfo.HasFlag_Context()) + ApiInfo |= ApiInfoFlags.InFrameworkContext; } private static bool TryGetRuntimesPath(string libraryName, out string dllPath) @@ -73,7 +75,7 @@ private static bool TryGetRuntimesPath(string libraryName, out string dllPath) private static IEnumerable GetRuntimesPaths(string libraryFullName) { - var searchPaths = s_searchPath.Concat( + var searchPaths = SearchPath.Concat( [ Environment.GetEnvironmentVariable("MAAFW_BINARY_PATH"), Path.GetDirectoryName(s_assembly.Location), @@ -82,8 +84,8 @@ private static IEnumerable GetRuntimesPaths(string libraryFullName) var runtimePaths = new[] { + "./", $"./runtimes/{GetArchitectureName()}/native/", - "./" }; return from searchPath in searchPaths @@ -109,7 +111,7 @@ select Path.GetFullPath( private static string GetFullLibraryName(string libraryName) { - if (s_isAgentServer && libraryName == "MaaFramework") + if (libraryName == "MaaFramework" && ApiInfo.HasFlag(ApiInfoFlags.InAgentServerContext)) libraryName = "MaaAgentServer"; if (IsWindows) diff --git a/src/MaaFramework.Binding.Native/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/MaaAgentClient.cs index 84c296f..c26b3bb 100644 --- a/src/MaaFramework.Binding.Native/MaaAgentClient.cs +++ b/src/MaaFramework.Binding.Native/MaaAgentClient.cs @@ -157,16 +157,8 @@ public bool LinkStart(IMaaAgentClient.AgentServerStartupMethod method, Cancellat { if (_agentServerProcess is null or { HasExited: true }) { - if (string.IsNullOrEmpty(Id) || string.IsNullOrEmpty(NativeBindingInfo.NativeAssemblyDirectory)) - { - throw new InvalidOperationException( - $"The {nameof(Id)}({Id ?? ""})" + - $" or {nameof(NativeBindingInfo.NativeAssemblyDirectory)}({NativeBindingInfo.NativeAssemblyDirectory ?? ""})" + - $" is invalid."); - } - _agentServerProcess?.Dispose(); - _agentServerProcess = method.Invoke(Id, NativeBindingInfo.NativeAssemblyDirectory); + _agentServerProcess = method.Invoke(Id, NativeBindingContext.LoadedNativeLibraryDirectory); if (_agentServerProcess is null or { HasExited: true }) return false; diff --git a/src/MaaFramework.Binding.Native/MaaAgentServer.cs b/src/MaaFramework.Binding.Native/MaaAgentServer.cs index 83d282c..7bfa192 100644 --- a/src/MaaFramework.Binding.Native/MaaAgentServer.cs +++ b/src/MaaFramework.Binding.Native/MaaAgentServer.cs @@ -28,7 +28,7 @@ public sealed class MaaAgentServer : IMaaAgentServer private MaaAgentServer() { } static MaaAgentServer() { - NativeLibrary.Init(isAgentServer: true); + NativeBindingContext.SwitchToAgentServerContext(); CurrentId = string.Empty; Current = new(); } @@ -146,7 +146,7 @@ public static MaaAgentServer WithToolkitConfig_InitOption(this MaaAgentServer se /// The directory paths to search for native libraries. public static MaaAgentServer WithNativeLibrary(this MaaAgentServer server, params string[] paths) { - NativeLibrary.Init(true, paths); + NativeBindingContext.AppendNativeLibrarySearchPaths(paths); return server; } } diff --git a/src/MaaFramework.Binding.Native/MaaContext.cs b/src/MaaFramework.Binding.Native/MaaContext.cs index faa922e..ace24d5 100644 --- a/src/MaaFramework.Binding.Native/MaaContext.cs +++ b/src/MaaFramework.Binding.Native/MaaContext.cs @@ -102,7 +102,7 @@ public MaaTasker Tasker get { var taskerHandle = MaaContextGetTasker(Handle); - if (NativeBindingInfo.IsStatelessMode) + if (NativeBindingContext.IsStatelessMode) return new MaaTasker(taskerHandle); else return MaaTasker.Instances[taskerHandle]; diff --git a/src/MaaFramework.Binding.Native/NativeBindingContext.cs b/src/MaaFramework.Binding.Native/NativeBindingContext.cs new file mode 100644 index 0000000..1201dae --- /dev/null +++ b/src/MaaFramework.Binding.Native/NativeBindingContext.cs @@ -0,0 +1,85 @@ +using MaaFramework.Binding.Interop.Native; +using System.Diagnostics; + +namespace MaaFramework.Binding; + +/// +/// Provides information and configuration for native bindings in the MaaFramework. +/// +public static class NativeBindingContext +{ + /// + /// Gets a value indicating whether NativeLibrary is already loaded. + /// + public static bool IsLoaded => NativeLibrary.IsLoaded; + + /// + /// Gets the loaded directory path where native libraries are located. + /// + public static string LoadedNativeLibraryDirectory + { + get + { + if (!NativeLibrary.ApiInfo.HasFlag(ApiInfoFlags.UseDefaultResolver)) + return NativeLibrary.LoadedDirectory; + + var name = NativeLibrary.LoadedLibraryHandles.First().Key; + foreach (var obj in Process.GetCurrentProcess().Modules) + { + if (obj is not ProcessModule module || Path.GetFileNameWithoutExtension(module.ModuleName) != name) + continue; + return Path.GetDirectoryName(module.FileName) ?? string.Empty; + } + return string.Empty; + } + } + + /// + /// Gets the API information, which provides details about the current API context. + /// + public static ApiInfoFlags ApiInfo => NativeLibrary.ApiInfo; + + /// + /// Gets a value indicating whether the current API is interoperating in the stateless mode. + /// Stateless mode is typically used in server contexts. + /// + public static bool IsStatelessMode => ApiInfo.HasFlag(ApiInfoFlags.InAgentServerContext); + + /// + /// Switches the context to the MaaAgentServer context. + /// + /// NativeLibrary is already loaded. + public static void SwitchToAgentServerContext() + { + ThrowIfLoaded(); + NativeLibrary.ApiInfo = ApiInfoFlags.InAgentServerContext; + } + + /// + /// Switches the context to the MaaFramework context. + /// + /// NativeLibrary is already loaded. + public static void SwitchToFrameworkContext() + { + ThrowIfLoaded(); + NativeLibrary.ApiInfo = ApiInfoFlags.InFrameworkContext; + } + + /// + /// Appends the specified paths to the native library search paths. + /// + /// + /// NativeLibrary is already loaded. + public static void AppendNativeLibrarySearchPaths(params IEnumerable paths) + { + ThrowIfLoaded(); + NativeLibrary.SearchPath.AddRange(paths); + } + + /// NativeLibrary is already loaded. + internal static void ThrowIfLoaded() + { + if (NativeLibrary.IsLoaded) + throw new InvalidOperationException("NativeLibrary is already loaded."); + } +} diff --git a/src/MaaFramework.Binding.Native/NativeBindingInfo.cs b/src/MaaFramework.Binding.Native/NativeBindingInfo.cs deleted file mode 100644 index 59646c8..0000000 --- a/src/MaaFramework.Binding.Native/NativeBindingInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using MaaFramework.Binding.Interop.Native; - -namespace MaaFramework.Binding; - -/// -/// Provides information and configuration for native bindings in the MaaFramework. -/// -public static class NativeBindingInfo -{ - /// - /// Gets the directory path where native assemblies are located. - /// - public static string? NativeAssemblyDirectory { get; internal set; } - - /// - /// Gets a value indicating whether the framework is operating in stateless mode. - /// Stateless mode is typically used in server contexts. - /// - public static bool IsStatelessMode { get; internal set; } - - /// - /// Gets or sets the API information string, which provides details about the current API context. - /// - public static string ApiInfo { get; internal set; } = string.Empty; - - /// - /// Initializes the native library with the specified configuration. - /// This method sets up the native library resolver and prepares the environment for native interop. - /// - /// Indicates whether the application is running in an agent server context. - /// Directory paths to search for native libraries. - /// Thrown if the native library is already loaded. - public static void Set(bool isAgentServer, params string[] dllSearchPaths) - => NativeLibrary.Init(isAgentServer, dllSearchPaths); -} diff --git a/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj b/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj index e137071..e116e79 100644 --- a/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj +++ b/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/MaaFramework.Binding/Enums/Binding/ApiInfoFlags.cs b/src/MaaFramework.Binding/Enums/Binding/ApiInfoFlags.cs new file mode 100644 index 0000000..aed55a0 --- /dev/null +++ b/src/MaaFramework.Binding/Enums/Binding/ApiInfoFlags.cs @@ -0,0 +1,44 @@ +namespace MaaFramework.Binding; + +#pragma warning disable S2344 // Enumeration type names should not have "Flags" or "Enum" suffixes + +/// +/// Represents information about binding interoperable API. +/// +[Flags] +public enum ApiInfoFlags +{ + /// + /// No flags. + /// + None = 0, + + /// + /// Indicates that the API is in MaaFramework context. + /// + InFrameworkContext = 1, + + /// + /// Indicates that the API is in MaaAgentServer context. + /// + InAgentServerContext = 2, + + /// + /// Indicates that the API uses the default resolver. + /// + UseDefaultResolver = 1 << 8, + + /// + /// Indicates that the API uses the resolver from binding. + /// + UseBindingResolver = 2 << 8, +} + +internal static class ApiInfoFlagsExtensions +{ + internal const ApiInfoFlags ContextMask = ApiInfoFlags.InFrameworkContext | ApiInfoFlags.InAgentServerContext; + internal const ApiInfoFlags ResolverMask = ApiInfoFlags.UseDefaultResolver | ApiInfoFlags.UseBindingResolver; + + internal static bool HasFlag_Context(this ApiInfoFlags flags) => (flags & ContextMask) != ApiInfoFlags.None; + internal static bool HasFlag_ResolverExcept(this ApiInfoFlags flags, ApiInfoFlags other) => (flags & ~other & ResolverMask) != ApiInfoFlags.None; +} diff --git a/src/MaaFramework.Binding/IMaaAgentClient.cs b/src/MaaFramework.Binding/IMaaAgentClient.cs index 3a62c93..9f9595d 100644 --- a/src/MaaFramework.Binding/IMaaAgentClient.cs +++ b/src/MaaFramework.Binding/IMaaAgentClient.cs @@ -49,7 +49,6 @@ public interface IMaaAgentClient : IMaaDisposable /// The delegate method that defines how to start the agent server process. /// An optional token to cancel the asynchronous operation waiting for the connection. /// if the connection was started successfully; otherwise, . - /// One or more parameters required by the are invalid. /// The has had cancellation requested. bool LinkStart(AgentServerStartupMethod method, CancellationToken cancellationToken = default); @@ -79,6 +78,10 @@ public interface IMaaAgentClient : IMaaDisposable /// /// A new that is associated with the process resource, or if no process resource is started. /// + /// + /// The implementation of this delegate is responsible for validating the provided parameters. + /// Ensure that and are valid and meet the requirements of the agent server process. + /// delegate Process? AgentServerStartupMethod(string identifier, string nativeAssemblyDirectory); /// diff --git a/src/MaaFramework.Binding/MaaFramework.Binding.csproj b/src/MaaFramework.Binding/MaaFramework.Binding.csproj index 8a40f6b..a32697b 100644 --- a/src/MaaFramework.Binding/MaaFramework.Binding.csproj +++ b/src/MaaFramework.Binding/MaaFramework.Binding.csproj @@ -12,4 +12,8 @@ snupkg + + + + From 13b0abbf7ed764ae75d4bf1414f65c09d969f88d Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Sat, 10 May 2025 12:54:40 +0800 Subject: [PATCH 14/19] feat: add IMaaDisposable.IsStateless --- src/MaaFramework.Binding.UnitTests/Test_Buffers.cs | 3 +++ src/MaaFramework.Binding.UnitTests/Test_Custom.cs | 11 +++++++++-- .../Abstractions/IMaaDisposable.cs | 10 +++++++++- .../Abstractions/MaaDisposableHandle.cs | 3 +++ src/MaaFramework.Binding/MaaImage.cs | 3 +++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs b/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs index 5fcf574..29b7f4c 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs @@ -1101,6 +1101,7 @@ public bool ThrowOnInvalid get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool IsStateless => throw new NotImplementedException(); public bool IsEmpty => throw new NotImplementedException(); public bool TryClear() => throw new NotImplementedException(); public ImageInfo GetInfo() => throw new NotImplementedException(); @@ -1126,6 +1127,7 @@ public bool ThrowOnInvalid get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool IsStateless => throw new NotImplementedException(); public bool IsEmpty => throw new NotImplementedException(); public ulong Size => throw new NotImplementedException(); public bool TryClear() => throw new NotImplementedException(); @@ -1143,6 +1145,7 @@ public bool ThrowOnInvalid get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public bool IsStateless => throw new NotImplementedException(); public int X => throw new NotImplementedException(); public int Y => throw new NotImplementedException(); public int Width => throw new NotImplementedException(); diff --git a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs index f5822bb..f7b04e6 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs @@ -35,7 +35,10 @@ public bool Analyze(in IMaaContext context, in AnalyzeArgs args, in AnalyzeResul Assert.AreEqual(NodeName, args.NodeName); Assert.AreEqual(RecognitionParam, args.RecognitionParam); - _ = Assert.ThrowsException(() => new MaaContext(IntPtr.Zero)); + _ = Assert.ThrowsException(() => +#if MAA_NATIVE + new MaaContext(IntPtr.Zero)); +#endif var cloneContext = (context as ICloneable).Clone() as IMaaContext; cloneContext = cloneContext?.Clone(); #if MAA_NATIVE @@ -105,7 +108,7 @@ public bool Run(in IMaaContext context, in RunArgs args) } } - internal sealed class TestController(MaaController c) : IMaaCustomController, IMaaDisposable + internal sealed class TestController(IMaaController c) : IMaaCustomController, IMaaDisposable { #region Test_IMaaDisposable @@ -117,6 +120,8 @@ public bool ThrowOnInvalid set => c.ThrowOnInvalid = value; } + public bool IsStateless => c.IsStateless; + public void Dispose() => c.Dispose(); #endregion @@ -137,7 +142,9 @@ public bool PressKey(int keycode) public bool RequestResolution(out int width, out int height) { +#if MAA_NATIVE using var image = new MaaImageBuffer(); +#endif if (Screencap(image)) { width = image.Width; diff --git a/src/MaaFramework.Binding/Abstractions/IMaaDisposable.cs b/src/MaaFramework.Binding/Abstractions/IMaaDisposable.cs index 3f3720f..abde39f 100644 --- a/src/MaaFramework.Binding/Abstractions/IMaaDisposable.cs +++ b/src/MaaFramework.Binding/Abstractions/IMaaDisposable.cs @@ -11,9 +11,17 @@ public interface IMaaDisposable : IDisposable bool IsInvalid { get; } /// - /// Gets a value indicating whether an is thrown when current instance is invalid but still called. + /// Gets a value indicating whether an is thrown when the current instance is invalid but still called. /// bool ThrowOnInvalid { get; set; } + + /// + /// Gets a value indicating whether the current instance is stateless. + /// + /// + /// The lifetime of unmanaged resources is controlled by . + /// + bool IsStateless { get; } } // 设计思路: diff --git a/src/MaaFramework.Binding/Abstractions/MaaDisposableHandle.cs b/src/MaaFramework.Binding/Abstractions/MaaDisposableHandle.cs index 82e5bf8..71a96b2 100644 --- a/src/MaaFramework.Binding/Abstractions/MaaDisposableHandle.cs +++ b/src/MaaFramework.Binding/Abstractions/MaaDisposableHandle.cs @@ -45,6 +45,9 @@ public void SetHandleAsInvalid() /// public bool ThrowOnInvalid { get; set; } + /// + public bool IsStateless => !_needReleased; + /// /// Creates a instance. /// diff --git a/src/MaaFramework.Binding/MaaImage.cs b/src/MaaFramework.Binding/MaaImage.cs index 17e287a..d3ea0fd 100644 --- a/src/MaaFramework.Binding/MaaImage.cs +++ b/src/MaaFramework.Binding/MaaImage.cs @@ -25,6 +25,9 @@ public bool ThrowOnInvalid set => buffer.ThrowOnInvalid = value; } + /// + public bool IsStateless => buffer.IsStateless; + /// /// Caches the image data from the . /// From 14b52b8abd4e3d178318fa9fdecc6610715343f5 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Mon, 12 May 2025 18:15:24 +0800 Subject: [PATCH 15/19] chore: modify MaaAgentClient.ctor() accessibility --- .../MaaAgentClient.cs | 23 +++---------------- .../Test_IMaaAgentClient.cs | 8 +++---- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/MaaFramework.Binding.Native/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/MaaAgentClient.cs index c26b3bb..8ac875a 100644 --- a/src/MaaFramework.Binding.Native/MaaAgentClient.cs +++ b/src/MaaFramework.Binding.Native/MaaAgentClient.cs @@ -29,7 +29,7 @@ public class MaaAgentClient : MaaDisposableHandle, IMaaAge /// /// Wrapper of . /// - public MaaAgentClient(string identifier = "") + protected MaaAgentClient(string identifier = "") : base(invalidHandleValue: MaaAgentClientHandle.Zero) { var handle = MaaAgentClientCreate(); @@ -40,23 +40,6 @@ public MaaAgentClient(string identifier = "") _ = Id.ThrowIfNotEquals(identifier); } - /// The unique identifier used to communicate with the agent server. - /// The resource. - /// - [SetsRequiredMembers] - public MaaAgentClient(string identifier, MaaResource resource) - : this(identifier) - { - Resource = resource; - } - - /// - [SetsRequiredMembers] - public MaaAgentClient(MaaResource resource) - : this("", resource) - { - } - /// /// Creates a instance. /// @@ -64,11 +47,11 @@ public MaaAgentClient(MaaResource resource) /// The resource. /// The instance. public static MaaAgentClient Create(string identifier, MaaResource resource) - => new(identifier, resource); + => new(identifier) { Resource = resource, }; /// public static MaaAgentClient Create(MaaResource resource) - => new(resource); + => new() { Resource = resource, }; /// public string Id { get; } diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs index ac82346..aa43665 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs @@ -13,7 +13,7 @@ public class Test_IMaaAgentClient public static Dictionary NewData => new() { #if MAA_NATIVE - { MaaTypes.Native, new MaaAgentClient(new MaaResource()) }, + { MaaTypes.Native, MaaAgentClient.Create(new MaaResource()) }, #endif }; public static Dictionary Data { get; private set; } = default!; @@ -39,11 +39,9 @@ public void CreateInstances() { #if MAA_NATIVE var newId = Guid.NewGuid().ToString(); - using var native1 = new MaaAgentClient(/*identifier = string.Empty*/) { Resource = new MaaResource() }; - using var native2 = new MaaAgentClient(/*identifier = string.Empty*/ new MaaResource()); - // var native3 = new MaaAgentClient(newId, new MaaResource()); + using var native1 = MaaAgentClient.Create(new MaaResource()); + using var native2 = MaaAgentClient.Create(new MaaResource()); using var native3 = MaaAgentClient.Create(newId, new MaaResource()); - using var native4 = MaaAgentClient.Create(new MaaResource()); Assert.AreNotEqual(native1.Id, native2.Id); Assert.AreEqual(newId, native3.Id); From 79cb6ac25426a819fcdbc30a57de7fb1fce766aa Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Tue, 13 May 2025 00:19:23 +0800 Subject: [PATCH 16/19] chore: move files Directory.*.props to src/ --- MaaFramework.Binding.sln | 4 ++-- Directory.Build.props => src/Directory.Build.props | 0 Directory.Packages.props => src/Directory.Packages.props | 0 tools/Builder/Build.csx | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename Directory.Build.props => src/Directory.Build.props (100%) rename Directory.Packages.props => src/Directory.Packages.props (100%) diff --git a/MaaFramework.Binding.sln b/MaaFramework.Binding.sln index 3a50aff..3c4b511 100644 --- a/MaaFramework.Binding.sln +++ b/MaaFramework.Binding.sln @@ -8,8 +8,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore - Directory.Build.props = Directory.Build.props - Directory.Packages.props = Directory.Packages.props + src\Directory.Build.props = src\Directory.Build.props + src\Directory.Packages.props = src\Directory.Packages.props LICENSE.md = LICENSE.md README.md = README.md README.zh_cn.md = README.zh_cn.md diff --git a/Directory.Build.props b/src/Directory.Build.props similarity index 100% rename from Directory.Build.props rename to src/Directory.Build.props diff --git a/Directory.Packages.props b/src/Directory.Packages.props similarity index 100% rename from Directory.Packages.props rename to src/Directory.Packages.props diff --git a/tools/Builder/Build.csx b/tools/Builder/Build.csx index 163158b..cf035f3 100644 --- a/tools/Builder/Build.csx +++ b/tools/Builder/Build.csx @@ -31,7 +31,7 @@ var version = tags.Count switch }; var runtimes = NuGetVersion.Parse( - XDocument.Load("Directory.Packages.props") + XDocument.Load("./src/Directory.Packages.props") .Descendants("PackageVersion") .FirstOrDefault(e => e.Attribute("Include")?.Value == "Maa.Framework.Runtimes") !.Attribute("Version") From 1b4ea5a96490b2efd50760e67b138fe3a1f1465c Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Wed, 14 May 2025 18:51:56 +0800 Subject: [PATCH 17/19] fix: tasker errors in stateless mode --- src/MaaFramework.Binding.Native/MaaContext.cs | 15 ++--- src/MaaFramework.Binding.Native/MaaTasker.cs | 28 +++++----- .../Properties/launchSettings.json | 8 +++ .../Test_Custom.cs | 10 +++- .../Test_IMaaAgentClient.cs | 56 ++++++++++++++----- 5 files changed, 74 insertions(+), 43 deletions(-) create mode 100644 src/MaaFramework.Binding.UnitTests/Properties/launchSettings.json diff --git a/src/MaaFramework.Binding.Native/MaaContext.cs b/src/MaaFramework.Binding.Native/MaaContext.cs index ace24d5..677d070 100644 --- a/src/MaaFramework.Binding.Native/MaaContext.cs +++ b/src/MaaFramework.Binding.Native/MaaContext.cs @@ -22,6 +22,9 @@ public MaaContext(MaaContextHandle contextHandle) if (contextHandle == MaaContextHandle.Zero) throw new ArgumentException($"Value cannot be {MaaContextHandle.Zero}.", nameof(contextHandle)); Handle = contextHandle; + + var taskerHandle = MaaContextGetTasker(Handle); + Tasker = NativeBindingContext.IsStatelessMode ? new MaaTasker(taskerHandle) : MaaTasker.Instances[taskerHandle]; } /// @@ -97,17 +100,7 @@ public bool OverrideNext(string nodeName, IEnumerable nextList) /// /// Wrapper of . /// - public MaaTasker Tasker - { - get - { - var taskerHandle = MaaContextGetTasker(Handle); - if (NativeBindingContext.IsStatelessMode) - return new MaaTasker(taskerHandle); - else - return MaaTasker.Instances[taskerHandle]; - } - } + public MaaTasker Tasker { get; } object ICloneable.Clone() => Clone(); diff --git a/src/MaaFramework.Binding.Native/MaaTasker.cs b/src/MaaFramework.Binding.Native/MaaTasker.cs index 884ddf8..1839f20 100644 --- a/src/MaaFramework.Binding.Native/MaaTasker.cs +++ b/src/MaaFramework.Binding.Native/MaaTasker.cs @@ -28,16 +28,18 @@ public class MaaTasker : MaaCommon, IMaaTasker /// protected internal static ConcurrentDictionary Instances { get; } = []; +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 [SetsRequiredMembers] internal MaaTasker(MaaTaskerHandle handle) { SetHandle(handle, needReleased: false); - Resource = new MaaResource(MaaTaskerGetResource(handle)); - Controller = new MaaController(MaaTaskerGetController(handle)); + _resource = new MaaResource(MaaTaskerGetResource(handle)); + _controller = new MaaController(MaaTaskerGetController(handle)); DisposeOptions = DisposeOptions.None; Toolkit = MaaToolkit.Shared; Utility = MaaUtility.Shared; } +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 /// /// Creates a instance. @@ -80,18 +82,13 @@ public MaaTasker(MaaController controller, MaaResource resource, DisposeOptions /// protected override void Dispose(bool disposing) { - // Cannot destroy Instance before disposing Controller and Resource. - if (DisposeOptions.HasFlag(DisposeOptions.Controller)) - { Controller.Dispose(); - } if (DisposeOptions.HasFlag(DisposeOptions.Resource)) - { Resource.Dispose(); - } + _ = Instances.TryRemove(new KeyValuePair(Handle, this)); base.Dispose(disposing); } @@ -122,6 +119,9 @@ public bool SetOption(TaskerOption opt, T value) #pragma warning restore } + private MaaResource _resource = null!; + private MaaController _controller = null!; + IMaaResource IMaaTasker.Resource { get => Resource; @@ -143,14 +143,14 @@ public required MaaResource Resource get { if (!IsInvalid) - _ = MaaTaskerGetResource(Handle).ThrowIfNotEquals(field.Handle, MaaInteroperationException.ResourceModifiedMessage); - return field; + _ = MaaTaskerGetResource(Handle).ThrowIfNotEquals(_resource.Handle, MaaInteroperationException.ResourceModifiedMessage); + return _resource; } set { ArgumentNullException.ThrowIfNull(value); _ = MaaTaskerBindResource(Handle, value.Handle).ThrowIfFalse(MaaInteroperationException.ResourceBindingFailedMessage); - field = value; + _resource = value; } } @@ -163,14 +163,14 @@ public required MaaController Controller get { if (!IsInvalid) - _ = MaaTaskerGetController(Handle).ThrowIfNotEquals(field.Handle, MaaInteroperationException.ControllerModifiedMessage); - return field; + _ = MaaTaskerGetController(Handle).ThrowIfNotEquals(_controller.Handle, MaaInteroperationException.ControllerModifiedMessage); + return _controller; } set { ArgumentNullException.ThrowIfNull(value); _ = MaaTaskerBindController(Handle, value.Handle).ThrowIfFalse(MaaInteroperationException.ControllerBindingFailedMessage); - field = value; + _controller = value; } } diff --git a/src/MaaFramework.Binding.UnitTests/Properties/launchSettings.json b/src/MaaFramework.Binding.UnitTests/Properties/launchSettings.json new file mode 100644 index 0000000..6b50930 --- /dev/null +++ b/src/MaaFramework.Binding.UnitTests/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "MaaFramework.Binding.UnitTests": { + "commandName": "Project", + "commandLineArgs": "CurrentDirectory CurrentDirectory 6CDC213A-085C-40C8-8665-635820D10425" + } + } +} \ No newline at end of file diff --git a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs index f7b04e6..1ddb9ae 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs @@ -47,8 +47,12 @@ public bool Analyze(in IMaaContext context, in AnalyzeArgs args, in AnalyzeResul Assert.IsNotNull(cloneContext); Assert.IsNull( cloneContext.RunRecognition(DiffEntry, "{}", (IMaaImageBuffer)args.Image)); - Assert.AreSame( - context.Tasker, cloneContext.Tasker); + if (!context.Tasker.IsStateless) + { + Assert.AreSame( + context.Tasker, cloneContext.Tasker); + } + Assert.AreEqual( context.TaskJob.Id, cloneContext.TaskJob.Id); @@ -142,7 +146,7 @@ public bool PressKey(int keycode) public bool RequestResolution(out int width, out int height) { -#if MAA_NATIVE +#if MAA_NATIVE using var image = new MaaImageBuffer(); #endif if (Screencap(image)) diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs index aa43665..240a311 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs @@ -62,21 +62,7 @@ public void Interface_LinkStart_LinkStop_AgentServerProcess(MaaTypes type, IMaaA { _ = Assert.ThrowsException(() => maaAgentClient.AgentServerProcess); - var ret = maaAgentClient.LinkStart((socketId, nativeAssemblyDirectory) => - { - string[] arguments = - [ - typeof(Test_IMaaAgentServer).Assembly.Location, - nativeAssemblyDirectory, - Environment.CurrentDirectory, - socketId - ]; - - return Process.Start(new ProcessStartInfo("dotnet", string.Join(' ', arguments)) - { - UseShellExecute = false, - }); - }); + var ret = maaAgentClient.LinkStart(StartupAgentServer); Assert.IsTrue( ret); Assert.IsTrue( // double start @@ -92,4 +78,44 @@ public void Interface_LinkStart_LinkStop_AgentServerProcess(MaaTypes type, IMaaA Assert.IsTrue( maaAgentClient.AgentServerProcess.HasExited); } + + [TestMethod] + [MaaData(MaaTypes.All, nameof(Data))] + public void RunTask(MaaTypes type, IMaaAgentClient maaAgentClient) + { + using var maa = new MaaTasker + { + Controller = new MaaAdbController(Common.AdbPath, Common.Address, AdbScreencapMethods.Encode, AdbInputMethods.AdbShell, Common.AdbConfig, Common.AgentPath), + Resource = new MaaResource(), + DisposeOptions = DisposeOptions.All, + }; + Assert.IsTrue( + maa.IsInitialized); + + using var agent = MaaAgentClient.Create("6CDC213A-085C-40C8-8665-635820D10425", maa.Resource); + using (var cts = new CancellationTokenSource(10000)) + { + Assert.IsTrue( + // agent.LinkStart()); + agent.LinkStart(StartupAgentServer, cts.Token)); + } + var status = maa + .AppendTask(Custom.NodeName, Custom.Param) + .Wait(); + + Assert.AreEqual(MaaJobStatus.Succeeded, + status); + Assert.IsTrue( + agent.LinkStop()); + + } + + private static Process? StartupAgentServer(string identifier, string nativeAssemblyDirectory) + { + return Process.Start(new ProcessStartInfo( + "dotnet", $"{typeof(Test_IMaaAgentServer).Assembly.Location} {nativeAssemblyDirectory} {Environment.CurrentDirectory} {identifier}") + { + UseShellExecute = false, + }); + } } From b99639afa165fb1e3043931bd7f493a6cce0d2aa Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Fri, 16 May 2025 14:33:56 +0800 Subject: [PATCH 18/19] [Breaking change] refactor(IMaaContext): make pipelineOverride an optional trailing parameter --- src/MaaFramework.Binding.Native/MaaContext.cs | 14 +++++++------- src/MaaFramework.Binding.UnitTests/Test_Custom.cs | 6 +++--- src/MaaFramework.Binding/IMaaContext.cs | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/MaaFramework.Binding.Native/MaaContext.cs b/src/MaaFramework.Binding.Native/MaaContext.cs index 677d070..a52a75d 100644 --- a/src/MaaFramework.Binding.Native/MaaContext.cs +++ b/src/MaaFramework.Binding.Native/MaaContext.cs @@ -31,7 +31,7 @@ public MaaContext(MaaContextHandle contextHandle) /// /// Wrapper of . /// - public TaskDetail? RunTask(string entry, [StringSyntax("Json")] string pipelineOverride) + public TaskDetail? RunTask(string entry, [StringSyntax("Json")] string pipelineOverride = "{}") { var taskId = MaaContextRunTask(Handle, entry, pipelineOverride); return taskId == Interop.Native.MaaDef.MaaInvalidId @@ -40,14 +40,14 @@ public MaaContext(MaaContextHandle contextHandle) } /// - public RecognitionDetail? RunRecognition(string entry, [StringSyntax("Json")] string pipelineOverride, IMaaImageBuffer image) - => RunRecognition(entry, pipelineOverride, (MaaImageBuffer)image); + public RecognitionDetail? RunRecognition(string entry, IMaaImageBuffer image, [StringSyntax("Json")] string pipelineOverride = "{}") + => RunRecognition(entry, (MaaImageBuffer)image, pipelineOverride); /// /// /// Wrapper of . /// - public RecognitionDetail? RunRecognition(string entry, [StringSyntax("Json")] string pipelineOverride, MaaImageBuffer image) + public RecognitionDetail? RunRecognition(string entry, MaaImageBuffer image, [StringSyntax("Json")] string pipelineOverride = "{}") { ArgumentNullException.ThrowIfNull(image); var recognitionId = MaaContextRunRecognition(Handle, entry, pipelineOverride, image.Handle); @@ -57,14 +57,14 @@ public MaaContext(MaaContextHandle contextHandle) } /// - public NodeDetail? RunAction(string entry, [StringSyntax("Json")] string pipelineOverride, IMaaRectBuffer recognitionBox, string recognitionDetail) - => RunAction(entry, pipelineOverride, (MaaRectBuffer)recognitionBox, recognitionDetail); + public NodeDetail? RunAction(string entry, IMaaRectBuffer recognitionBox, string recognitionDetail, [StringSyntax("Json")] string pipelineOverride = "{}") + => RunAction(entry, (MaaRectBuffer)recognitionBox, recognitionDetail, pipelineOverride); /// /// /// Wrapper of . /// - public NodeDetail? RunAction(string entry, [StringSyntax("Json")] string pipelineOverride, MaaRectBuffer recognitionBox, string recognitionDetail) + public NodeDetail? RunAction(string entry, MaaRectBuffer recognitionBox, string recognitionDetail, [StringSyntax("Json")] string pipelineOverride = "{}") { ArgumentNullException.ThrowIfNull(recognitionBox); var nodeId = MaaContextRunAction(Handle, entry, pipelineOverride, recognitionBox.Handle, recognitionDetail); diff --git a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs index 1ddb9ae..5a89ea9 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs @@ -46,7 +46,7 @@ public bool Analyze(in IMaaContext context, in AnalyzeArgs args, in AnalyzeResul #endif Assert.IsNotNull(cloneContext); Assert.IsNull( - cloneContext.RunRecognition(DiffEntry, "{}", (IMaaImageBuffer)args.Image)); + cloneContext.RunRecognition(DiffEntry, args.Image)); if (!context.Tasker.IsStateless) { Assert.AreSame( @@ -57,7 +57,7 @@ public bool Analyze(in IMaaContext context, in AnalyzeArgs args, in AnalyzeResul context.TaskJob.Id, cloneContext.TaskJob.Id); var recognitionDetail = - context.RunRecognition(DiffEntry, DiffParam, args.Image); + context.RunRecognition(DiffEntry, args.Image, DiffParam); Assert.IsNotNull( recognitionDetail?.HitBox); @@ -106,7 +106,7 @@ public bool Run(in IMaaContext context, in RunArgs args) Assert.AreNotEqual(Detail, args.RecognitionDetail.Detail); Assert.AreEqual(Box, $"{args.RecognitionBox.X}{args.RecognitionBox.Y}{args.RecognitionBox.Width}{args.RecognitionBox.Height}"); - var nodeDetail = context.RunAction(DiffEntry, DiffParam, args.RecognitionBox, args.RecognitionDetail.Detail); + var nodeDetail = context.RunAction(DiffEntry, args.RecognitionBox, args.RecognitionDetail.Detail, DiffParam); Assert.IsNotNull(nodeDetail); return true; } diff --git a/src/MaaFramework.Binding/IMaaContext.cs b/src/MaaFramework.Binding/IMaaContext.cs index 4f0779c..a413f9d 100644 --- a/src/MaaFramework.Binding/IMaaContext.cs +++ b/src/MaaFramework.Binding/IMaaContext.cs @@ -26,28 +26,28 @@ public interface IMaaContext : ICloneable /// The entry name of the task. /// The json used to override the pipeline. /// if the operation was executed successfully; otherwise, . - TaskDetail? RunTask(string entry, [StringSyntax("Json")] string pipelineOverride); + TaskDetail? RunTask(string entry, [StringSyntax("Json")] string pipelineOverride = "{}"); /// /// Run a recognition. /// /// The recognition entry name. - /// The json used to override the pipeline. /// The image to be recognized. + /// The json used to override the pipeline. /// if the operation was executed successfully; otherwise, . /// - RecognitionDetail? RunRecognition(string entry, [StringSyntax("Json")] string pipelineOverride, IMaaImageBuffer image); + RecognitionDetail? RunRecognition(string entry, IMaaImageBuffer image, [StringSyntax("Json")] string pipelineOverride = "{}"); /// /// Run an action. /// /// The action entry name. - /// The json used to override the pipeline. /// The rect buffer containing current rect in the recognition result. /// The rect detail in the recognition result. + /// The json used to override the pipeline. /// if the operation was executed successfully; otherwise, . /// - NodeDetail? RunAction(string entry, [StringSyntax("Json")] string pipelineOverride, IMaaRectBuffer recognitionBox, string recognitionDetail); + NodeDetail? RunAction(string entry, IMaaRectBuffer recognitionBox, [StringSyntax("Json")] string recognitionDetail, [StringSyntax("Json")] string pipelineOverride = "{}"); /// /// Override a pipeline. From 7b83f07abf33c9fbed5ca611465db6b3543e8c59 Mon Sep 17 00:00:00 2001 From: moomiji <35213527+moomiji@users.noreply.github.com> Date: Fri, 16 May 2025 14:44:12 +0800 Subject: [PATCH 19/19] [Breaking change] feat(IMaaCustomAction): add results parameter to Run method --- .../Interop/MaaCustomExtensions.cs | 3 ++- src/MaaFramework.Binding.UnitTests/Test_Custom.cs | 2 +- src/MaaFramework.Binding/Custom/IMaaCustomAction.cs | 10 +++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/MaaFramework.Binding.Native/Interop/MaaCustomExtensions.cs b/src/MaaFramework.Binding.Native/Interop/MaaCustomExtensions.cs index 1363caf..2e52c80 100644 --- a/src/MaaFramework.Binding.Native/Interop/MaaCustomExtensions.cs +++ b/src/MaaFramework.Binding.Native/Interop/MaaCustomExtensions.cs @@ -44,7 +44,8 @@ nint transArg ActionParam: customActionParam, RecognitionDetail: recognitionDetail, RecognitionBox: new MaaRectBuffer(boxHandle) - ) + ), + new RunResults() ); }; return callback; diff --git a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs index 5a89ea9..9354d3a 100644 --- a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs +++ b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs @@ -98,7 +98,7 @@ internal sealed class TestAction : IMaaCustomAction { public string Name { get; set; } = nameof(TestAction); - public bool Run(in IMaaContext context, in RunArgs args) + public bool Run(in IMaaContext context, in RunArgs args, in RunResults results) { Assert.AreEqual(NodeName, args.NodeName); Assert.AreEqual(ActionParam, args.ActionParam); diff --git a/src/MaaFramework.Binding/Custom/IMaaCustomAction.cs b/src/MaaFramework.Binding/Custom/IMaaCustomAction.cs index 38942e5..cc8cf72 100644 --- a/src/MaaFramework.Binding/Custom/IMaaCustomAction.cs +++ b/src/MaaFramework.Binding/Custom/IMaaCustomAction.cs @@ -13,8 +13,9 @@ public interface IMaaCustomAction : IMaaCustomResource /// /// The context. /// The run args. + /// The run results. /// if the operation was executed successfully; otherwise, . - bool Run(in IMaaContext context, in RunArgs args); + bool Run(in IMaaContext context, in RunArgs args, in RunResults results); } /// @@ -27,3 +28,10 @@ public interface IMaaCustomAction : IMaaCustomResource /// Gets the recognition detail. /// Gets the recognition box. public sealed record RunArgs(string NodeName, TaskDetail TaskDetail, string ActionName, [StringSyntax("Json")] string ActionParam, RecognitionDetail RecognitionDetail, IMaaRectBuffer RecognitionBox); + +#pragma warning disable S2094 // Classes should not be empty +/// +/// The action run results. +/// +public sealed record RunResults; +#pragma warning restore S2094 // Classes should not be empty