diff --git a/PCL.Core/Utils/Exts/EnumerableExtensions.cs b/PCL.Core/Utils/Exts/EnumerableExtensions.cs
new file mode 100644
index 000000000..e20ddb3d6
--- /dev/null
+++ b/PCL.Core/Utils/Exts/EnumerableExtensions.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+
+namespace PCL.Core.Utils.Exts;
+
+public static class EnumerableExtensions
+{
+ ///
+ /// 对目标集合进行枚举,并传递该元素在集合中的位置
+ ///
+ /// 要枚举的集合
+ /// 处理函数
+ /// 类型
+ public static void ForEachIndexed(this IEnumerable collection, Action handle)
+ {
+ var i = 0;
+ foreach (var element in collection)
+ {
+ handle.Invoke(element ,i);
+ i++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgBoxCommand.cs b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgBoxCommand.cs
new file mode 100644
index 000000000..feaab219d
--- /dev/null
+++ b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgBoxCommand.cs
@@ -0,0 +1,39 @@
+using System.Windows.Input;
+using PCL.Controls.MyMsg.Models;
+using PCL.Core.Utils.Exts;
+
+namespace PCL.Controls.MyMsg.Commands;
+
+public class MyMsgBoxCommand: ICommand
+{
+ public MyMsgBoxCommand(ButtonContext[] contexts)
+ {
+ contexts.ForEachIndexed((value, index) => OperateMap[index] = value);
+ }
+
+ public event Action? Exited;
+
+ public event Action? Clicked;
+
+ public event EventHandler? CanExecuteChanged;
+
+ private Dictionary OperateMap { get; set; } = [];
+
+ public string GetButtonTextById(int id) =>
+ OperateMap.FirstOrDefault(key => key.Key == id).Value.ButtonName;
+
+ public bool CanExecute(object? parameter) =>
+ parameter is not null && OperateMap.ContainsKey((int)parameter);
+
+ public void Execute(object? parameter)
+ {
+
+ if(parameter is null
+ || !OperateMap.TryGetValue((int)parameter, out var op)) return;
+
+ Clicked?.Invoke((int)parameter);
+
+ op.Operation.Invoke(parameter);
+ if(op.ExitWhenClick) Exited?.Invoke();
+ }
+}
\ No newline at end of file
diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputButtonCommand.cs b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputButtonCommand.cs
new file mode 100644
index 000000000..494731b17
--- /dev/null
+++ b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputButtonCommand.cs
@@ -0,0 +1,14 @@
+using System.Windows.Input;
+
+namespace PCL.Controls.MyMsg.Commands;
+
+public class MyMsgInputButtonCommand: ICommand
+{
+ public event Action? Exited;
+
+ public event EventHandler? CanExecuteChanged;
+
+ public bool CanExecute(object? parameter) => true;
+
+ public void Execute(object? parameter) => Exited?.Invoke();
+}
\ No newline at end of file
diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputCommand.cs b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputCommand.cs
new file mode 100644
index 000000000..c5fb4803c
--- /dev/null
+++ b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputCommand.cs
@@ -0,0 +1,30 @@
+using System.Windows.Input;
+using FluentValidation;
+
+namespace PCL.Controls.MyMsg.Commands;
+
+public class MyMsgInputCommand: ICommand
+{
+ public List> ValidateRules { get; } = [];
+ public event Action? Error;
+
+ public event Action? Success;
+
+ public event EventHandler? CanExecuteChanged;
+
+ public void Execute(object? parameter)
+ {
+ var userInput = parameter?.ToString()!;
+ foreach (var rule in ValidateRules)
+ {
+ var result = rule.Validate(userInput);
+ if(result.IsValid) continue;
+ Error?.Invoke(string.Join(Environment.NewLine, result.Errors.Select(e => e.ErrorMessage)));
+ return;
+ }
+ Success?.Invoke();
+ }
+
+ public bool CanExecute(object? parameter) => true;
+
+}
\ No newline at end of file
diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Models/ButtonContext.cs b/Plain Craft Launcher 2/Controls/MyMsg/Models/ButtonContext.cs
new file mode 100644
index 000000000..741120bd1
--- /dev/null
+++ b/Plain Craft Launcher 2/Controls/MyMsg/Models/ButtonContext.cs
@@ -0,0 +1,8 @@
+namespace PCL.Controls.MyMsg.Models;
+
+public class ButtonContext
+{
+ public required string ButtonName { get; set; }
+ public bool ExitWhenClick { get; set; }
+ public Action