">程序配置与 <bindingRedirect/>
在完成部分接口把程序打包成exe时重新校对了一下 NewtonJson 的版本,之前在 Nuget 上下载的是 13 版本,但是 GStarCAD 目录下的版本是 12。将版本调整之后 exe 无法正常使用:
未经处理的异常:SystemI0.Fi1eLoadException:未能加载文件或程序集“Newtonsoft.Json,Version=13.0.0.0 Culture=neutral Pub1icKeyToken=30ad4fe6b2a6aeed”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。(异常来自 HRESULT:0x80131040)--->SystemIO.Fi1eLoadException:未能加载文件或程序集“Newtonsoft.Ison,Version=12.0.0.0,Culture=neutral, PublicKe yToken=30ad4fe6b2a6aeed”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。(异常来自 HRESULT:0x80131040)
再次检查 Nuget 版本已经调整过来了,但是编译之后的 exe 还是会报错 NewtonJson 版本错误。其实只需要调整一下 app.config
文件:
<?xml version="1.0" encoding="utf-8" ?> <configuration > <startup > <supportedRuntime version ="v4.0" sku =".NETFramework,Version=v4.8" /> </startup > <runtime > <assemblyBinding xmlns ="urn:schemas-microsoft-com:asm.v1" > <dependentAssembly > <assemblyIdentity name ="Newtonsoft.Json" publicKeyToken ="30ad4fe6b2a6aeed" culture ="neutral" /> <bindingRedirect oldVersion ="0.0.0.0-13.0.0.0" newVersion ="13.0.0.0" /> </dependentAssembly > </assemblyBinding > </runtime > </configuration >
问题在于 <bindingRedirect/>
这个标签,微软官方API文档 这里有比较详细的解释。
将一个程序集版本重定向到另一个版本。
<bindingRedirect oldVersion ="existing assembly version" newVersion ="new assembly version" />
oldVersion
:指定最初请求的程序集的版本。 程序集版本号的格式为 major.minor.build.revision
。 该版本号的每个部分的有效值介于 0 和 65535 之间。newVersion
:指定要用来取代最初请求的版本的程序集版本(格式为:n.n.n.n
) ,此值可以指定 oldVersion 之前的版本。官方还给出了一个示例演示如何将一个程序集版本重定向到另一个版本:
<configuration > <runtime > <assemblyBinding xmlns ="urn:schemas-microsoft-com:asm.v1" > <dependentAssembly > <assemblyIdentity name ="myAssembly" publicKeyToken ="32ab4ba45e0a69a1" culture ="neutral" /> <bindingRedirect oldVersion ="1.0.0.0" newVersion ="2.0.0.0" /> </dependentAssembly > </assemblyBinding > </runtime > </configuration >
所以把这个标签删掉,程序就可以正常运行了,这是一个知识盲点记录下来。
读取 .dat
文件数据 所需要读取的 PaperSet.dat
长相如下,包含了国标图幅的各个数据尺寸:
Name B L a c e k BD LD A0 841 1189 25 10 20 1 16 12 A1 594 841 25 10 20 1 12 8 A2 420 594 25 10 10 1 8 6 A3 297 420 25 5 10 1 2 2 A4 210 297 25 5 10 1 2 2 A0X2 1189 1682 25 10 20 3 16 24 A0X3 1189 2523 25 10 20 3 16 36 A1X3 841 1783 25 10 20 3 12 24 A1X4 841 2378 25 10 20 3 12 32 A2X3 594 1261 25 10 20 3 8 18 A2X4 594 1682 25 10 20 3 8 24 A2X5 594 2102 25 10 20 3 8 24 A3X3 420 891 25 10 10 2 6 12 A3X4 420 1189 25 10 10 2 6 16 A3X5 420 1486 25 10 10 2 6 10 A3X6 420 1783 25 10 10 3 6 12 A3X7 420 2080 25 10 10 3 6 14 A4X3 297 630 25 5 10 2 2 8 A4X4 297 841 25 5 10 2 2 12 A4X5 297 1051 25 5 10 2 2 12 A4X6 297 1261 25 5 10 3 2 12 A4X7 297 1471 25 5 10 3 2 14 A4X8 297 1682 25 5 10 3 2 16 A4X9 297 1892 25 5 10 3 2 18
观察数据结构,每行数据均以若干空格分隔图幅名称、长度、宽度等等数据,所以我们需要逐行去读取,先简单声明一个图幅信息类包含图幅名称和长宽:
public class Paper { public Paper (string Name, double Width, double Length ) { this .Name = Name; this .Length = Length; this .Width = Width; } public string Name { get ; } public double Length { get ; } public double Width { get ; } }
之后通过 StreamReader
逐行读取(首行不读取)即可:
List<Paper> result = new List<Paper>(); string path = Path.Combine(installPath, @"MCADSetting\PaperSet.dat" );using (StreamReader reader = new StreamReader(path)){ int index = 0 ; string line; while ((line = reader.ReadLine()) != null ) { if (index++ == 0 ) continue ; string [] item = line.Split(new [] { ' ' , '\t' }, StringSplitOptions.RemoveEmptyEntries); if (item.Length < 2 ) continue ; Paper paper = new Paper(item[0 ], Double.Parse(item[1 ]), Double.Parse(item[2 ])); result.Add(paper); } }
String.Split
方法可以参考微软的这篇 如何在 C# 中使用 String.Split 分隔字符串 , StringSplitOptions.RemoveEmptyEntrie
s 参数来排除返回数组中的任何空字符串。要对返回的集合进行更复杂的处理,可使用 LINQ 来处理结果序列。
CADNET & PLM 接口使用 GetCADApplication 获取 CAD 应用程序,首先明确我们的思路:
若 CAD 程序已经存在,则直接跳转至已打开的 CAD 程序 否则新建 CAD 应用程序 try { IGcadApplication app = Marshal.GetActiveObject("GStarCAD.Application" ) as IGcadApplication; app.Visible = true ; } catch (Exception exception){ app = new GcadApplicationClass(); app.Visible = true ; ...... }
这里捕获用到了 System.Runtime.InteropServices
中的 Marshal.GetActiveObject()
方法。
注意 app.Visible
可以让后台的应用程序前置显示,在忘记关闭 Quit()
时会出现多个后台运行程序,在项目的前期测试造成了不少麻烦。
CloseFile 该命令会传入一个图纸路径 filePath
,关闭该指定路径的图纸。
关闭图纸的业务场景相对简单,只需要判断两点:
当前 CAD 是否打开过任何图纸 在已打开过的图纸中是否有和 filePath
相对应的图纸 最后对找到的图纸进行关闭操作即可。
if (app.Documents.Count <= 0 ) return ; foreach (GcadDocument doc in app.Documents){ if (string .Equals(doc.FullName, filePath, StringComparison.OrdinalIgnoreCase)) { doc.Close(); return ; } } return ;
需要注意的是这里需要用 doc.FullName
而非 doc.Name
,以规避出现诸如 C:\test.dwg
和 D:\test.dwg
在 Name
中均为 test.dwg
的末端图纸路径重名的情况。
OpenFile NewtonSoftJson自定义Convertor序列化Json 这个项目一个比较关键的过程就是去解析图纸数据并转换为 Json 格式,前面 PLM 接口负责解析数据,那么该如何将这些数据转换为 Json 呢?
查阅官网的 Custom JsonConverter ,给出了一个比较完整的示例:
public class KeysJsonConverter : JsonConverter { private readonly Type[] _types; public KeysJsonConverter (params Type[] types ) { _types = types; } public override void WriteJson (JsonWriter writer, object value , JsonSerializer serializer ) { JToken t = JToken.FromObject(value ); if (t.Type != JTokenType.Object) { t.WriteTo(writer); } else { JObject o = (JObject)t; IList<string > propertyNames = o.Properties().Select(p => p.Name).ToList(); o.AddFirst(new JProperty("Keys" , new JArray(propertyNames))); o.WriteTo(writer); } } public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) { throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter." ); } public override bool CanRead { get { return false ; } } public override bool CanConvert (Type objectType ) { return _types.Any(t => t == objectType); } } public class Employee { public string FirstName { get ; set ; } public string LastName { get ; set ; } public IList<string > Roles { get ; set ; } }
Employee employee = new Employee { FirstName = "James" , LastName = "Newton-King" , Roles = new List<string > { "Admin" } }; string json = JsonConvert.SerializeObject(employee, Formatting.Indented, new KeysJsonConverter(typeof (Employee)));Console.WriteLine(json); Employee newEmployee = JsonConvert.DeserializeObject<Employee>(json, new KeysJsonConverter(typeof (Employee))); Console.WriteLine(newEmployee.FirstName);
UML类图绘制