1.macad-properties
设置类型可见,以及语言
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]//设置此程序集中的类型对COM组件不可见
//In order to begin building localizable applications, set
//CultureYouAreCodingWith in your .csproj file
//inside a . For example, if you are using US english
//in your source files, set the to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
[assembly: NeutralResourcesLanguage("en", UltimateResourceFallbackLocation.MainAssembly)]
//语言设置
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
1.macad-application-appcontext
using System;
using System.IO;
using System.Text;
using System.Windows.Input;
using Macad.Core; // 引入Macad核心命名空间
using Macad.Common.Serialization; // 引入Macad通用序列化命名空间
using Macad.Interaction; // 引入Macad交互命名空间
using Macad.Common; // 引入Macad通用命名空间
using Macad.Exchange; // 引入Macad交换命名空间
namespace Macad.Window
{
// 定义应用程序上下文类,继承自交互上下文类
public class AppContext : InteractiveContext
{
#region Properties
// 获取当前应用程序上下文的静态属性
public new static AppContext Current { get; private set; }
//--------------------------------------------------------------------------------------------------
// 是否在沙盒模式中的静态属性
public static bool IsInSandbox
{
get { return CommandLine.EnableSandbox; }
}
//--------------------------------------------------------------------------------------------------
// 命令行对象的静态属性
public static CommandLine CommandLine { get; private set; }
#endregion
#region Initialization
// 构造函数,初始化应用程序上下文
AppContext()
{
LoadShortcuts(); // 加载快捷键
}
//--------------------------------------------------------------------------------------------------
// 初始化应用程序上下文
internal static void Initialize(CommandLine cmdLine)
{
CommandLine = cmdLine; // 设置命令行对象
if (IsInSandbox) // 如果处于沙盒模式
{
DebugPipeServer.Open(); // 开启调试管道服务器
}
Current = new AppContext(); // 创建当前应用程序上下文对象
ExchangeModule.Initialize(); // 初始化交换模块
}
//--------------------------------------------------------------------------------------------------
// 加载快捷键
void LoadShortcuts()
{
ShortcutHandler.AddShortcut(ShortcutScope.Application, new(Key.F1, ApplicationCommands.Help)); // 添加帮助命令的快捷键
ShortcutHandler.AddShortcut(ShortcutScope.Application, new(Key.S, ModifierKeys.Control, DocumentCommands.SaveAll)); // 添加保存所有文档命令的快捷键
}
//--------------------------------------------------------------------------------------------------
#endregion
#region Local App Data
// 本地应用数据目录
public readonly string LocalAppDataDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create), "Macad");
//--------------------------------------------------------------------------------------------------
// 保存本地设置
public override void SaveLocalSettings(string name, object obj)
{
if (IsInSandbox) // 如果处于沙盒模式,则返回
return;
try
{
var data = Serializer.Serialize(obj, new SerializationContext(SerializationScope.Storage)); // 序列化对象
if (data.IsNullOrEmpty()) // 如果数据为空,则返回
return;
var dir = Path.Combine(LocalAppDataDirectory, "Settings"); // 设置保存目录
Directory.CreateDirectory(dir); // 创建目录
var path = Path.Combine(dir, name); // 设置保存路径
File.WriteAllText(path, Serializer.Format(data), Encoding.UTF8); // 写入文件
}
catch (Exception e)
{
Messages.Exception("Exception while saving local settings for " + name, e); // 异常处理
}
}
//--------------------------------------------------------------------------------------------------
// 加载本地设置
public override T LoadLocalSettings(string name)
{
if (IsInSandbox) // 如果处于沙盒模式,则返回null
return null;
try
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create), "Macad", "Settings", name); // 设置加载路径
if (!File.Exists(path)) // 如果文件不存在,则返回null
return null;
var data = File.ReadAllText(path, Encoding.UTF8); // 读取文件内容
if (data.IsNullOrEmpty()) // 如果数据为空,则返回null
return null;
return Serializer.Deserialize(data, new SerializationContext(SerializationScope.Storage)); // 反序列化对象
}
catch (Exception e)
{
Messages.Exception("Exception while saving local settings for " + name, e); // 异常处理
return null;
}
}
//--------------------------------------------------------------------------------------------------
#endregion
}
}
这段代码是一个C#应用程序的主要入口点,定义了一个名为AppContext
的类,用于管理应用程序的上下文环境。主要功能包括:
定义了AppContext
类,继承自InteractiveContext
,用于管理应用程序的交互环境。
提供了静态属性Current
,用于获取当前应用程序上下文的实例。
提供了静态属性IsInSandbox
,用于判断应用程序是否处于沙盒模式。
提供了静态属性CommandLine
,用于获取命令行对象。
提供了静态方法Initialize
,用于初始化应用程序上下文。
在构造函数中加载了快捷键。
提供了方法SaveLocalSettings
和LoadLocalSettings
,用于保存和加载本地设置。
总的来说,这段代码的作用是初始化应用程序的上下文环境,包括加载快捷键和管理本地设置等功能。
1.macad-application-crashhandler
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using Macad.Common.Interop;
using Macad.Core.Topology;
using Macad.Presentation;
namespace Macad.Window
{
public static class CrashHandler
{
// 初始化崩溃处理器
public static void Init(string crashDumpDirectory)
{
// 设置崩溃转储目录
_CrashDumpDirectory = crashDumpDirectory;
// 注册未捕获异常事件处理程序
AppDomain.CurrentDomain.UnhandledException += _CurrentDomain_UnhandledException;
Application.Current.DispatcherUnhandledException += _Current_DispatcherUnhandledException;
// 删除之前的崩溃转储文件
foreach (var dumpFile in Directory.EnumerateFiles(_CrashDumpDirectory, "Macad_*.dmp"))
{
try
{
File.Delete(dumpFile);
}
catch (Exception)
{
}
}
}
//--------------------------------------------------------------------------------------------------
static string _CrashDumpDirectory;
static bool _InCrashState;
//--------------------------------------------------------------------------------------------------
// 处理调度程序未捕获的异常
static void _Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs args)
{
if (_UnhandledException())
{
if (args != null)
args.Handled = true;
Application.Current?.Shutdown();
}
}
//--------------------------------------------------------------------------------------------------
// 处理应用程序域未捕获的异常
static void _CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs args)
{
_UnhandledException();
Application.Current?.Shutdown();
}
//--------------------------------------------------------------------------------------------------
// 处理未捕获的异常
static bool _UnhandledException()
{
if (_InCrashState || Debugger.IsAttached)
return false;
_InCrashState = true;
MessageBoxResult mbres = MessageBoxResult.OK;
Task.Run(() =>
{
mbres = MessageBox.Show(
"An unhandled exception occurred, and the application is terminating.\n\n"
+ "If you click OK, it is tried to save a copy of the open documents.\n"
+ "Also two crashdump files will be saved to the application directory. "
+ "These can be used by the developer to trace the crash.\n\n"
+ "The crashdump files will be deleted on the next start of the application.",
"Ooops...", MessageBoxButton.OKCancel, MessageBoxImage.Error);
}).Wait();
if(mbres != MessageBoxResult.OK)
return false;
using (new WaitCursor())
{
_Dump(false);
_Dump(true);
_SaveDocuments();
}
return true;
}
//--------------------------------------------------------------------------------------------------
// 保存文档
static void _SaveDocuments()
{
try
{
string fileName = AppContext.Current?.Document?.FilePath;
if (!string.IsNullOrEmpty(fileName))
{
string dirName = Path.GetDirectoryName(fileName) ?? _CrashDumpDirectory;
fileName = Path.Combine(dirName, $"{Path.GetFileNameWithoutExtension(fileName)}_{DateTime.Now:yyyy-MM-yy_HHmmss}.{Model.FileExtension}");
AppContext.Current.Document.SaveToFile(fileName);
}
}
catch (Exception)
{
}
}
//--------------------------------------------------------------------------------------------------
// 生成转储文件
static void _Dump(bool full)
{
var fileName = Path.Combine(_CrashDumpDirectory, $"Macad_{ (full ? "Full" : "Small")}_{DateTime.Now:yyyy-MM-yy_HHmmss}.dmp");
var fileStream = File.Create(fileName);
if (fileStream.SafeFileHandle == null)
return;
var info = new Win32Api.MINIDUMP_EXCEPTION_INFORMATION
{
ClientPointers = 1,
ExceptionPointers = Marshal.GetExceptionPointers(),
ThreadId = Win32Api.GetCurrentThreadId()
};
Win32Api.MiniDumpWriteDump(
Win32Api.GetCurrentProcess(), Win32Api.GetCurrentProcessId(),
fileStream.SafeFileHandle.DangerousGetHandle(),
full ? Win32Api.MINIDUMP_TYPE.MiniDumpWithFullMemory : Win32Api.MINIDUMP_TYPE.MiniDumpNormal,
ref info, IntPtr.Zero, IntPtr.Zero);
fileStream.Close();
}
//--------------------------------------------------------------------------------------------------
}
}
整段代码的作用是实现了一个崩溃处理器,用于捕获应用程序中的未处理异常,并进行相应的处理:
初始化方法Init
用于设置崩溃转储目录,并注册应用程序域和调度程序未捕获异常的处理事件。
处理未捕获异常的方法_CurrentDomain_UnhandledException
和_Current_DispatcherUnhandledException
。
_UnhandledException
方法用于处理未捕获的异常,当发生未捕获的异常时,弹出提示框询问用户是否希望保存当前打开的文档,并生成两个崩溃转储文件。
_SaveDocuments
方法用于保存当前打开文档。
_Dump
方法用于生成崩溃转储文件。
1.macad-application-windowscliboard
using System.IO;
using Macad.Core;
namespace Macad.Window
{
// WindowsClipboard 类继承自 Clipboard 抽象类,用于处理 Windows 平台的剪贴板操作
public sealed class WindowsClipboard : Clipboard
{
// 构造函数
WindowsClipboard()
{
}
//--------------------------------------------------------------------------------------------------
// 检查剪贴板中是否包含指定格式的数据
public override bool ContainsData(in string format)
{
return System.Windows.Clipboard.ContainsData(format);
}
//--------------------------------------------------------------------------------------------------
// 从剪贴板中获取指定格式的数据流
public override MemoryStream GetDataStream(in string format)
{
return System.Windows.Clipboard.GetData(format) as MemoryStream;
}
//--------------------------------------------------------------------------------------------------
// 将数据设置到剪贴板中指定的格式
public override void SetData(in string format, in MemoryStream data)
{
System.Windows.Clipboard.SetData(format, data);
}
//--------------------------------------------------------------------------------------------------
// 初始化 WindowsClipboard 实例
public static void Init()
{
if(Current == null)
Current = new WindowsClipboard();
}
//--------------------------------------------------------------------------------------------------
}
}
1.macad-command-appcommands
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Threading;
using Macad.Common;
using Macad.Common.Interop;
using Macad.Core;
using Macad.Core.Topology;
using Macad.Interaction;
using Macad.Interaction.Dialogs;
using Macad.Presentation;
namespace Macad.Window
{
// 定义了一组应用程序命令
public static class AppCommands
{
// 退出应用程序命令,关闭主窗口
public static ActionCommand ExitApplication { get; } = new(
() =>
{
Application.Current.MainWindow.Close();
})
{
Header = () => "Exit Program" // 设置菜单显示文本
};
//--------------------------------------------------------------------------------------------------
// 初始化应用程序,包括显示欢迎信息、创建新模型、检查更新等操作
internal static RelayCommand InitApplication { get; } = new(
() =>
{
Messages.Info("Welcome to Macad|3D."); // 显示欢迎信息
DocumentCommands.CreateNewModel.Execute(); // 创建新模型
// 检查更新
if (!AppContext.IsInSandbox && VersionCheck.IsAutoCheckEnabled)
{
Dispatcher.CurrentDispatcher.BeginInvoke(VersionCheck.BeginCheckForUpdate, DispatcherPriority.ApplicationIdle);
}
});
//--------------------------------------------------------------------------------------------------
// 运行启动命令,包括打开文件、运行脚本等操作
internal static RelayCommand RunStartupCommands { get; } = new(
() =>
{
var cmdArgs = AppContext.CommandLine;
// 检查命令行选项以加载项目
if (cmdArgs.HasPathToOpen
&& DocumentCommands.OpenFile.CanExecute(cmdArgs.PathToOpen)
&& PathUtils.GetExtensionWithoutPoint(cmdArgs.PathToOpen).Equals(Model.FileExtension))
{
DocumentCommands.OpenFile.Execute(cmdArgs.PathToOpen);
}
// 如果没有加载文档,则创建一个新文档
if (AppContext.Current.Document == null)
{
DocumentCommands.CreateNewModel.Execute();
}
// 加载除模型以外的其他文件
if (cmdArgs.HasPathToOpen
&& DocumentCommands.OpenFile.CanExecute(cmdArgs.PathToOpen)
&& !PathUtils.GetExtensionWithoutPoint(cmdArgs.PathToOpen).Equals(Model.FileExtension))
{
Dispatcher.CurrentDispatcher.InvokeAsync(() => DocumentCommands.OpenFile.Execute(cmdArgs.PathToOpen), DispatcherPriority.Loaded);
}
// 检查命令行选项以运行脚本
if (cmdArgs.HasScriptToRun)
{
ToolboxCommands.RunScriptCommand.Execute(cmdArgs.ScriptToRun);
}
});
//--------------------------------------------------------------------------------------------------
// 准备关闭窗口,包括提示保存修改等操作
public static RelayCommand PrepareWindowClose { get; } = new(
(e) =>
{
if (DocumentCommands.SaveAll.CanExecute())
{
var result = Dialogs.AskForSavingChanges(); // 提示用户保存修改
switch (result)
{
case TaskDialogResults.Cancel:
e.Cancel = true;
return;
case TaskDialogResults.Yes:
DocumentCommands.SaveAll.Execute(); // 执行保存操作
e.Cancel = DocumentCommands.SaveAll.CanExecute();
break;
case TaskDialogResults.No:
break;
}
}
});
//--------------------------------------------------------------------------------------------------
// 显示关于对话框
public static ActionCommand ShowAboutDialog { get; } = new(
() =>
{
new AboutDialog
{
Owner = MainWindow.Current
}.ShowDialog(); // 显示关于对话框
})
{
Header = () => "About Macad|3D...", // 设置菜单显示文本
Description = () => "Shows version and license information.", // 设置菜单项描述
};
//--------------------------------------------------------------------------------------------------
// 显示图层编辑器
public static ActionCommand ShowLayerEditor { get; } = new(
() => MainWindow.Current?.Docking.ActivateToolAnchorable("Layers") ) // 激活图层编辑器
{
Header = () => "Layer Editor", // 设置菜单显示文本
Description = () => "Opens the layer editor.", // 设置菜单项描述
Icon = () => "Layer-Editor" // 设置菜单项图标
};
//--------------------------------------------------------------------------------------------------
// 显示形状检查器
public static ActionCommand ShowShapeInspector { get; } = new(
() =>
{
MainWindow.Current?.Docking.ActivateToolAnchorable("ShapeInspector");
}) // 激活形状检查器
{
Header = () => "Shape Inspector", // 设置菜单显示文本
Description = () => "Opens the shape inspector.", // 设置菜单项描述
Icon = () => "Tool-ShapeInspect" // 设置菜单项图标
};
//--------------------------------------------------------------------------------------------------
// 重置窗口布局
public static ActionCommand ResetWindowLayout { get; } = new(
() =>
{
MainWindow.Current?.Docking.LoadWindowLayout("Default");
}) // 加载默认窗口布局
{
Header = () => "Reset Window Layout", // 设置菜单显示文本
Description = () => "Resets the window layout to the default layout.", // 设置菜单项描述
Icon = () => "App-RestoreLayout" // 设置菜单项图标
};
//--------------------------------------------------------------------------------------------------
// 显示帮助主题
public static ActionCommand ShowHelpTopic { get; } = new(
(topicId) =>
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
var url = $"https://macad3d.net/userguide/go/?version={version.Major}.{version.Minor}&guid={topicId}";
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
}) // 打开帮助主题链接
{
Header = (topicId) => "Show User Guide", // 设置菜单显示文本
Description = (topicId) => "Open and browse the Macad|3D User Guide.", // 设置菜单项描述
Icon = (topicId) => "App-UserGuide" // 设置菜单项图标
};
//--------------------------------------------------------------------------------------------------
}
}
这段代码定义了一系列应用程序命令,用于处理用户界面中的各种操作,例如退出程序、打开文件、显示帮助等
1.macad-utils-commandline
using System;
using System.Reflection;
using Macad.Common;
namespace Macad.Window
{
// 命令行参数处理类
public class CommandLine
{
// 要打开的文件路径
public string PathToOpen { get; }
// 是否存在要打开的文件路径
public bool HasPathToOpen => !PathToOpen.IsNullOrEmpty();
// 是否启用沙箱模式
public bool EnableSandbox { get; }
// 是否禁用欢迎对话框
public bool NoWelcomeDialog { get; }
// 要运行的脚本路径
public string ScriptToRun { get; }
// 是否存在要运行的脚本路径
public bool HasScriptToRun => !ScriptToRun.IsNullOrEmpty();
//--------------------------------------------------------------------------------------------------
// 构造函数,解析命令行参数
public CommandLine(string[] cmdArgs)
{
foreach (var cmdarg in cmdArgs)
{
if (cmdarg.StartsWith("-") | cmdarg.StartsWith("/"))
{
string option, parameter;
int splitPos = Math.Min(cmdarg.IndexOf('='), cmdarg.IndexOf(':'));
if (splitPos > 1)
{
option = cmdarg.Substring(1, splitPos-1).ToLower(); // 提取参数名称
parameter = cmdarg.Substring(splitPos + 1); // 提取参数值
}
else
{
option = cmdarg.Substring(1).ToLower(); // 提取参数名称
parameter = null; // 参数值为空
}
// 根据参数名称进行处理
switch (option)
{
case "sandbox":
EnableSandbox = true; // 启用沙箱模式
break;
case "nowelcome":
NoWelcomeDialog = true; // 禁用欢迎对话框
break;
case "runscript":
ScriptToRun = parameter; // 设置要运行的脚本路径
break;
}
}
else
{
PathToOpen = cmdarg; // 设置要打开的文件路径
}
}
}
//--------------------------------------------------------------------------------------------------
}
}
这段代码定义了一个命令行参数处理类 CommandLine
,用于解析传递给应用程序的命令行参数。它包含了处理命令行参数的方法,并提供了访问这些参数的属性
1.macad-utils-debugpipeserver
using System;
using System.Globalization;
using System.IO.Pipes;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using Macad.Common;
using Macad.Common.Interop;
using Macad.Common.Serialization;
using Macad.Core;
using Macad.Interaction.Editors.Shapes;
using Macad.Presentation;
namespace Macad.Window
{
// 调试管道服务器类,用于与外部调试器进行通信
internal static class DebugPipeServer
{
#region Pipe Handling
static Thread _Thread;
//--------------------------------------------------------------------------------------------------
// 打开调试管道服务器
internal static void Open()
{
if (_Thread != null)
return;
_Thread = new Thread(_ThreadProc);
_Thread.Start();
// 订阅应用程序退出事件
App.Current.Exit += (sender, args) => _Close();
}
//--------------------------------------------------------------------------------------------------
// 关闭调试管道服务器
static void _Close()
{
if (_Thread == null)
return;
_Thread.Interrupt();
_Thread = null;
}
//--------------------------------------------------------------------------------------------------
// 线程处理函数
static void _ThreadProc(object data)
{
try
{
// 创建命名管道服务器
using (var pipeServer = new NamedPipeServerStream("MacadDebug", PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous))
{
pipeServer.WaitForConnection();
var buffer = new byte[64];
var sb = new StringBuilder();
while(true)
{
var readBytes = pipeServer.Read(buffer, 0, 64);
if (readBytes == 0)
break;
// 读取并处理消息
sb.Append(Encoding.UTF8.GetString(buffer, 0, readBytes));
if (pipeServer.IsMessageComplete)
{
var answer = _OnMessageReceived(sb.ToString());
if(answer != null)
pipeServer.Write(answer, 0, answer.Length);
sb.Clear();
}
};
}
}
catch (ThreadInterruptedException)
{
// 优雅地关闭线程
}
catch (Exception e)
{
// 在UI线程中显示异常信息
Application.Current.Dispatcher.Invoke(() => Messages.Exception("An exception occured in the debug pipe stream thread.", e));
}
}
//--------------------------------------------------------------------------------------------------
// 处理接收到的消息
static byte[] _OnMessageReceived(string message)
{
var code = message.Substring(0, 4);
switch (code)
{
case "GETV":
var signature = message.Substring(4, 8);
string value = Application.Current.Dispatcher.Invoke(
() => _GetValue(message.Substring(12), out var obj ) ? Serializer.Serialize(obj) : "INVALID");
return $"VALU{signature}{value}".ToUtf8Bytes();
case "KEYD":
var keyd = int.Parse(message.Substring(4), NumberStyles.HexNumber);
return (_SendKeyEvent(keyd, false) ? $"DYEK{message.Substring(4)}" : "DYEK$Failed").ToUtf8Bytes();
case "KEYU":
var keyu = int.Parse(message.Substring(4), NumberStyles.HexNumber);
return (_SendKeyEvent(keyu, true) ? $"UYEK{message.Substring(4)}" : "UYEK$Failed").ToUtf8Bytes();
}
return null;
}
//--------------------------------------------------------------------------------------------------
#endregion
#region Value Query
// 获取对象的值
static bool _GetValue(string path, out object obj)
{
// 根据路径查找对象的值
obj = null;
string[] parts;
// 是否为方法调用?
var firstPar = path.IndexOf('(');
if (firstPar > 0)
{
parts = path.Substring(0, firstPar).Split('.');
parts[^1] = parts[^1] + path.Substring(firstPar);
}
else
{
parts = path.Split('.');
if (parts.Length == 0)
return false;
}
if (!_FindRootObject(ref obj, parts[0]))
return false;
foreach (var part in parts.Skip(1))
{
if (!_FindValue(ref obj, part))
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------
// 查找根对象
static bool _FindRootObject(ref object obj, string memberName)
{
if (memberName.StartsWith("!"))
{
if (!Guid.TryParse(memberName.Substring(1), out var guid))
return false;
obj = AppContext.Current?.Document?.FindInstance(guid);
return true;
}
switch (memberName)
{
case "$Context":
obj = AppContext.Current;
return true;
case "$Selected":
obj = AppContext.Current?.WorkspaceController?.Selection?.SelectedEntities?.FirstOrDefault();
return true;
case "$Tool":
obj = AppContext.Current?.WorkspaceController?.CurrentTool;
return true;
case "$Sketch":
obj = (AppContext.Current?.WorkspaceController?.CurrentTool as SketchEditorTool)?.Sketch;
return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------
// 查找对象的属性值或方法返回值
static bool _FindValue(ref object obj, string memberName)
{
if (obj == null)
return false;
if (memberName.StartsWith('[') && memberName.EndsWith(']'))
{
return _FindIndexedPropertyValue(ref obj, memberName);
}
if (memberName.EndsWith(')'))
{
return _FindMethodValue(ref obj, memberName);
}
// 获取属性值
var propInfo = obj.GetType().GetProperty(memberName);
if (propInfo == null)
return false;
obj = propInfo.GetValue(obj);
return true;
}
//--------------------------------------------------------------------------------------------------
// 查找方法的返回值
static bool _FindMethodValue(ref object obj, string memberName)
{
int first = memberName.IndexOf('(');
string paramStr = memberName.Substring(first + 1, memberName.Length - first - 2).Trim();
int paramCount = paramStr.Length > 0 ? 1 : 0;
string methodName = memberName.Substring(0, first);
var methodInfo = obj.GetType().GetMethods().FirstOrDefault(mi => mi.Name == methodName && mi.GetParameters().Length == paramCount);
if (methodInfo == null)
return false;
object param1 = null;
if (paramCount == 1 && !_GetValue(paramStr, out param1))
return false;
obj = methodInfo.Invoke(obj, new[] {param1});
return true;
}
//--------------------------------------------------------------------------------------------------
// 查找对象的索引器属性值
static bool _FindIndexedPropertyValue(ref object obj, string memberName)
{
var indexerPropInfo = obj.GetType().GetProperties().FirstOrDefault(pi => pi.GetIndexParameters().Length > 0);
if (indexerPropInfo == null)
return false;
var indexStr = memberName.Substring(1, memberName.Length - 2);
obj = int.TryParse(indexStr, out var index)
? indexerPropInfo.GetValue(obj, new object[] {index})
: indexerPropInfo.GetValue(obj, new object[] {indexStr});
return true;
}
//--------------------------------------------------------------------------------------------------
#endregion
#region Keyboard Input
// 发送键盘事件
static bool _SendKeyEvent(int key, bool isUp)
{
bool success = false;
int vkey = key & 0xff;
Application.Current.Dispatcher.Invoke(() =>
{
var sourceElement = Keyboard.PrimaryDevice.ActiveSource;
var focusElement = Keyboard.PrimaryDevice.FocusedElement;
if (focusElement == null || sourceElement == null)
return;
ModifierKeys modifiers = ModifierKeys.None;
if ((key & 0x0100) > 0) modifiers = modifiers.Added(ModifierKeys.Shift);
if ((key & 0x0200) > 0) modifiers = modifiers.Added(ModifierKeys.Control);
if ((key & 0x0400) > 0) modifiers = modifiers.Added(ModifierKeys.Alt);
InputHelper.SimulatedModifiers = modifiers;
var tunnelArgs = new KeyEventArgs(Keyboard.PrimaryDevice, sourceElement, 0, KeyInterop.KeyFromVirtualKey(vkey));
tunnelArgs.RoutedEvent = isUp ? Keyboard.PreviewKeyUpEvent : Keyboard.PreviewKeyDownEvent;
focusElement.RaiseEvent(tunnelArgs);
if (!tunnelArgs.Handled)
{
var bubbleArgs = new KeyEventArgs(Keyboard.PrimaryDevice, sourceElement, 0, KeyInterop.KeyFromVirtualKey(vkey));
bubbleArgs.RoutedEvent = isUp ? Keyboard.KeyUpEvent : Keyboard.KeyDownEvent;
focusElement.RaiseEvent(bubbleArgs);
if (!bubbleArgs.Handled && !isUp)
{
var sb = new StringBuilder();
byte[] bKeyState = new byte[255];
if ((key & 0x0100) > 0) bKeyState[0x10] = 0x80;
if ((key & 0x0200) > 0) bKeyState[0x11] = 0x80;
if ((key & 0x0400) > 0) bKeyState[0x12] = 0x80;
uint lScanCode = Win32Api.MapVirtualKey((uint)(vkey), Win32Api.MapVirtualKeyMapTypes.MAPVK_VK_TO_VSC);
Win32Api.ToUnicode((uint)(vkey), lScanCode, bKeyState, sb, 5, 0);
TextCompositionManager.StartComposition(new TextComposition(InputManager.Current, Keyboard.FocusedElement, sb.ToString()));
}
}
InputHelper.SimulatedModifiers = ModifierKeys.None;
success = true;
});
return success;
}
//--------------------------------------------------------------------------------------------------
#endregion
}
}
这段代码实现了调试管道服务器,用于与外部调试器进行通信。它包括了以下功能:
1.macad-utils-vertioncheck
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Windows;
using Macad.Common;
using Macad.Common.Serialization;
using Macad.Core;
namespace Macad.Window
{
#region Model
// 存储版本检查信息的类
[SerializeType]
internal class VersionCheckInfo
{
[SerializeMember]
public DateTime OnlineCheck { get; set; } = DateTime.MinValue;
[SerializeMember]
public string LocalVersion { get; set; } = "0.0.0.0";
[SerializeMember]
public bool UpdateAvailable { get; set; } = false;
[SerializeMember]
public string UpdateVersion { get; set; } = "0.0.0.0";
[SerializeMember]
public string UpdateUrl { get; set; } = "";
}
//--------------------------------------------------------------------------------------------------
// 版本检查参数设置类
public sealed class VersionCheckParameterSet : OverridableParameterSet
{
public bool CheckForUpdate { get => GetValue(); set => SetValue(value); }
public uint CheckIntervalDays { get => GetValue(); set => SetValue(value); }
//--------------------------------------------------------------------------------------------------
// 构造函数,设置默认值
public VersionCheckParameterSet()
{
SetDefaultValue(nameof(CheckForUpdate), true);
SetDefaultValue(nameof(CheckIntervalDays), (uint)7);
}
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
#endregion
#region VersionCheckRequest
// 版本检查请求接口
internal interface IVersionCheckRequest
{
void BeginRequest(Action> callback);
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// 版本检查Web请求类
internal class VersionCheckWebRequest : IVersionCheckRequest
{
readonly string _Url;
readonly Version _UserAgentVersion;
Action> _Callback;
//--------------------------------------------------------------------------------------------------
// 构造函数
public VersionCheckWebRequest(string url, Version userAgentVersion)
{
_Url = url;
_UserAgentVersion = userAgentVersion;
}
//--------------------------------------------------------------------------------------------------
// 开始请求
public void BeginRequest(Action> callback)
{
_Callback = callback;
// 在后台线程中执行请求
Thread t = new Thread(_ThreadEntry)
{
IsBackground = true,
};
t.Start();
}
//--------------------------------------------------------------------------------------------------
// 线程入口函数
void _ThreadEntry(object obj)
{
using HttpClient httpClient = new();
httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd($"Macad3D/{_UserAgentVersion.Major}.{_UserAgentVersion.Minor}");
try
{
var task = httpClient.GetStringAsync(_Url);
if (!task.Wait(new TimeSpan(0, 0, 30)))
{
Debug.Print("--- Version check failed: Timeout on server connection.");
}
string responseText = task.Result;
if (responseText.Length >= 1)
{
var serializer = Serializer.GetSerializer(typeof(Dictionary));
var reader = new Reader(responseText);
if (serializer.Read(reader, null, null) is Dictionary dict)
{
_Callback.Invoke(dict);
}
}
}
catch (Exception)
{
Debug.Print("--- Version check failed: Retrieving version information from server failed.");
}
}
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
#endregion
#region VersionCheck
// 版本检查类
internal class VersionCheck
{
public const string GetVersionUrl = "https://macad3d.net/versioninfo";
//--------------------------------------------------------------------------------------------------
// 获取是否启用自动检查更新
public static bool IsAutoCheckEnabled
{
get
{
var parameterSet = CoreContext.Current.Parameters.Get();
return parameterSet.CheckForUpdate;
}
set
{
var parameterSet = CoreContext.Current.Parameters.Get();
parameterSet.CheckForUpdate = value;
}
}
//--------------------------------------------------------------------------------------------------
// 开始检查更新
public static void BeginCheckForUpdate()
{
var vc = new VersionCheck();
if (vc._Info.UpdateAvailable)
{
Application.Current.Dispatcher.Invoke(() =>
{
vc.RaiseUpdateAvailable();
});
return;
}
vc.BeginCheckForUpdate(false, new VersionCheckWebRequest(GetVersionUrl, vc._LocalVersion));
}
//--------------------------------------------------------------------------------------------------
// 重置更新状态
public static void ResetAvailability()
{
var info = CoreContext.Current.LoadLocalSettings("LastVersionCheck") ?? new VersionCheckInfo();
info.UpdateAvailable = false;
CoreContext.Current.SaveLocalSettings("LastVersionCheck", info);
}
//--------------------------------------------------------------------------------------------------
// 更新可用事件
public static event EventHandler UpdateAvailable;
void RaiseUpdateAvailable()
{
UpdateAvailable?.Invoke(this, _Info);
}
//--------------------------------------------------------------------------------------------------
VersionCheckInfo _Info;
readonly Version _LocalVersion;
//--------------------------------------------------------------------------------------------------
// 构造函数
public VersionCheck()
{
_LocalVersion = Assembly.GetExecutingAssembly().GetName().Version;
_Info = CoreContext.Current.LoadLocalSettings("LastVersionCheck") ?? new VersionCheckInfo();
}
//--------------------------------------------------------------------------------------------------
// 开始检查更新
public bool BeginCheckForUpdate(bool force, IVersionCheckRequest request)
{
if (!force && !_IsUpdateCheckNeeded())
{
return false;
}
_Info.OnlineCheck = DateTime.Now;
_Info.LocalVersion = _LocalVersion.ToString();
CoreContext.Current.SaveLocalSettings("LastVersionCheck", _Info);
request?.BeginRequest(_OnRequested);
return true;
}
//--------------------------------------------------------------------------------------------------
// 处理请求后的回调
void _OnRequested(Dictionary dictionary)
{
if (dictionary.TryGetValue("Version", out string version)
&& dictionary.TryGetValue("Url", out string url))
{
// 检查是否已经发布新版本
if (_Compare(new Version(version), new Version(_Info.UpdateVersion)) != 0)
{
_Info.UpdateAvailable = _Compare(new Version(version), new Version(_Info.LocalVersion)) == 1;
_Info.UpdateVersion = version;
_Info.UpdateUrl = url;
CoreContext.Current.SaveLocalSettings("LastVersionCheck", _Info);
}
// 通知用户更新可用
if (_Info.UpdateAvailable)
{
Application.Current?.Dispatcher.Invoke(() =>
{
RaiseUpdateAvailable();
});
}
}
}
//--------------------------------------------------------------------------------------------------
// 检查是否需要进行更新检查
bool _IsUpdateCheckNeeded()
{
// 如果有新版本,强制进行一次更新
var lastLocalVersion = new Version(_Info.LocalVersion);
if (_Compare(lastLocalVersion, _LocalVersion) != 0)
{
return true;
}
var parameterSet = CoreContext.Current.Parameters.Get();
// 如果距离上次检查超过 n 天,则需要更新检查
TimeSpan span = DateTime.Now - _Info.OnlineCheck;
if (span.Days > (int)parameterSet.CheckIntervalDays)
{
return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------
// 版本比较函数
int _Compare(Version v1, Version v2)
{
if (v1.Major != v2.Major)
if (v1.Major > v2.Major)
return 1;
else
return -1;
if (v1.Minor != v2.Minor)
if (v1.Minor > v2.Minor)
return 1;
else
return -1;
if (v1.Build != v2.Build)
if (v1.Build > v2.Build)
return 1;
else
return -1;
if (v1.Revision != v2.Revision)
if (v1.Revision < v2.Revision)
return 1;
else
return -1;
return 0;
}
}
#endregion
}
该段代码主要实现了版本检查功能,包括以下几个部分:
Model:定义了用于存储版本检查信息的类 VersionCheckInfo
和版本检查参数设置的类 VersionCheckParameterSet
。
VersionCheckRequest:定义了版本检查请求的接口 IVersionCheckRequest
,以及基于 Web 请求的版本检查类 VersionCheckWebRequest
。这些类用于发起与服务器的通信,获取最新版本信息。
VersionCheck:实现了版本检查的主要逻辑。它包括了检查是否启用自动检查更新、开始检查更新、重置更新状态以及比较版本号的功能。
1.macad-utils-windowplacement
using System.Runtime.InteropServices;
using System.Windows.Interop;
using Macad.Common.Interop;
using Macad.Common.Serialization;
namespace Macad.Window
{
// 窗口位置信息类,实现了 ISerializeValue 接口
[SerializeType]
public sealed class WindowPlacement : ISerializeValue
{
// 窗口位置信息结构体
[SerializeMember]
Win32Api.WINDOWPLACEMENT Placement
{
get { return _Placement; }
set { _Placement = value; }
}
Win32Api.WINDOWPLACEMENT _Placement;
//--------------------------------------------------------------------------------------------------
// 获取窗口位置信息
bool _GetPlacement(System.Windows.Window window)
{
return Win32Api.GetWindowPlacement(new HandleRef(this, new WindowInteropHelper(window).Handle), out _Placement);
}
//--------------------------------------------------------------------------------------------------
// 设置窗口位置信息
bool _SetPlacement(System.Windows.Window window)
{
_Placement.showCmd = _Placement.showCmd == Win32Api.SW_SHOWMINIMIZED ? Win32Api.SW_SHOWNORMAL : _Placement.showCmd;
_Placement.flags = 0;
return Win32Api.SetWindowPlacement(new HandleRef(this, new WindowInteropHelper(window).Handle), ref _Placement);
}
//--------------------------------------------------------------------------------------------------
// 保存窗口位置信息到本地设置
public static void SaveWindowPlacement(System.Windows.Window window, string name)
{
var placement = new WindowPlacement();
if (placement._GetPlacement(window))
AppContext.Current.SaveLocalSettings(name, placement);
}
//--------------------------------------------------------------------------------------------------
// 从本地设置加载窗口位置信息
public static void LoadWindowPlacement(System.Windows.Window window, string name)
{
var placement = AppContext.Current.LoadLocalSettings(name);
placement?._SetPlacement(window);
}
//--------------------------------------------------------------------------------------------------
// 静态成员,用于序列化窗口位置信息
static readonly ISerializer _PlacementSerializer;
// 静态构造函数,初始化窗口位置信息序列化器
static WindowPlacement()
{
_PlacementSerializer = Serializer.GetSerializer(typeof(Win32Api.WINDOWPLACEMENT));
}
// 实现 ISerializeValue 接口的 Write 方法,将窗口位置信息序列化
public bool Write(Writer writer, SerializationContext context)
{
return _PlacementSerializer.Write(writer, _Placement, context);
}
// 实现 ISerializeValue 接口的 Read 方法,从序列化数据中读取窗口位置信息
public bool Read(Reader reader, SerializationContext context)
{
var obj = _PlacementSerializer.Read(reader, null, context);
if (obj == null)
return false;
_Placement = (Win32Api.WINDOWPLACEMENT) obj;
return true;
}
//--------------------------------------------------------------------------------------------------
}
}
该段代码定义了用于保存和加载窗口位置信息的 WindowPlacement
类。其主要功能包括:
定义窗口位置信息结构体:包含了窗口的状态、位置和显示方式等信息。
获取和设置窗口位置信息:通过调用 Win32 API 实现了获取和设置窗口位置信息的功能。
保存和加载窗口位置信息:提供了静态方法 SaveWindowPlacement
和 LoadWindowPlacement
,用于将窗口位置信息保存到本地设置和从本地设置加载窗口位置信息。
实现序列化接口:实现了 ISerializeValue
接口,使得 WindowPlacement
类的对象可以被序列化和反序列化,以便保存和加载窗口位置信息。