首页 > 其他 > 详细

BAML资源的解析笔记

时间:2020-06-12 23:32:06      阅读:76      评论:0      收藏:0      [点我收藏+]

  前不久,一位爱好汉化的朋友联系到我,他对自己有了些略高的要求,不止是汉化,还希望有更多……在我看来这种行为是有违道德的,但还是盛情难却,正好研究一下,然后集成进我的.Net RuntimeExplorer中。

  BAML是XAML的二进制版本,没有明确的资料说明其格式,不过它并不神秘,使用reflector在PresentationFramework这个程序集中便可找到答案。

  在.ne中对于解析这种格式大概有二种调用方法:第一是有关于WPF窗体,我们可以直接调用Application.LoadComponent方法生成一个窗体的实例,其内部是使用BamlReader2006这个命名空间创建实例的,我们可以对此实例进行任何修改,然后调用XamlWriter.Save来输出成文本。这种方法的缺点是不能保存成BAML格式,而且还必须生成窗体实例,窗体的默认构造函数会一并运行,会造成不可预料的事情,并且不能脱离目标进程进行操作。

  第二是对于BAML资源的解析,封装在System.Windows.Markup.Localizer命名空间中,如名所示,这是对本地化资源进行读写操作的,其内部使用BamlReader与BamlWriter完成操作。这种方法的缺点是在读取方面无法得到所需的有用信息,在BamlReader中忽略了本地化资源以外的一部分信息,且读写操作必须在目标进程内操作。

  如果跳过一些封装直接调用XAML的reader或writer也是行不通的,进程中必须存在WPF窗体所有的运行时类型,CLR组件还好,如果有自定义组件那就不行了,这就是上述两条不能脱离目标进程的原因。

  所以,对于以文件或从其它程序集中提取的Baml进行解读是无法使用.net原有的功能的。通过对.net中代码的分析,Baml的所有信息是以Record记录的形式逐条存放的,而XAML则是XML格式。这里要说明一下,如果只是汉化之类的修改属性值的话,还是简单的,市面上有多款现成的软件可以做到,但我的要求并非如此,而是要修改窗体的整体结构,添加删改其中的组件,则必须将其还原成XAML形式进行操作,再保存回Baml格式,所以必须对59种Record类型进行逐个处理,恢复他们之间的父子关联,手动创建XAML。基础的类型和枚举的定义当然是照着reflector中抄的,具体的解析代码确无法直接照搬,一是在.net中全部是私有类型,二是因为BamlReader2006这个类型中会引用大量的运行时类型,代码中要把大量的无用且无意义代码去掉很是头痛,思来想去只能重头写来了。

  尽管在GITHUB上有大神多年前的代码,但其只能READ无法WRITE,还是要靠自己,大概一个多月吧,代码的思路基本清晰,可能性测试也已完成,这里留下一些重点信息概要。

  在Record中TypeId指的是Type,Type.Name则相等于Xml的Node.Name;AttributeId指的是Type的属性名,相等于XmlNode的属性名,这两种ID是有负值的,负值则是指CLR中已定义类型或元素,请看KnownProperties与KnownElements这两个枚举中的定义,可以通过反射来调用静态类型KnownTypes中的方法来进行转换。

  xmlns中的prefix值与元素属性的对应关联有点复杂,由于找不到文档说明,花了不少时间硬啃代码。 这要分为两部分来说明,一是有关于对CLR中现存元素的引用,BamlXmlnsProperty这个类型的XmlNamespace属性就是命名空间,如果这个命名空间的值是一个URI,就要对AssemblyIds属性进行解析,这是个数组,它表示要进行缓存的AssemblyId,那么就需要通过反射遍历已经装载的Assembly的XmlnsDefinitionAttribute,这样就把类型的命名空间与xmlns的URI联系起来,就可以通过TypeFullName来找到对应的prefix值;第二部分是指对程序集中类型的引用,这类xmlns的值都是以clr-namespace:开头的,通常指得是在程序集中的命名空间,但这些值没有唯一性与确定性,它将与BamlPIMapping中的内容相对应,在这里才会将xmlns与真实的命名空间关联上。

  在BAML中属性的值不是基元类型有多种处理方法,值是字符串引用使用BamlPropertyStringReference,值是类型引用使用BamlPropertyTypeReference,值是容器使用BamlPropertyComplexStart等类型,如果值是CLR中定义的非基元类型时将使用BamlPropertyCustom来表示,还有BamlPropertyWithStaticResourceId、BamlPropertyWithExtension、BamlPresentationOptionsAttribute这几种。对于BamlPropertyComplexStart及其派生的类型,它他将以嵌套XmlNode的形式来表示其值,基本上可以看作为XmlSerialize了。对于BamlPropertyWithExtension略复杂些,从名字看都知道是扩展了,说明其属性值就是我们在正常编辑XAML时那种有{}符号的值。

  对于静态资源的处理有两种类型,一种是XmlNode整个节点,它以BamlStaticResourceStart开始,以BamlStaticResourceEnd结束,然后通过BamlStaticResourceId来引用到正常的Xml流中;另一种以BamlOptimizedStaticResource定义内容,通过BamlPropertyWithStaticResourceId引用为XmlNode的属性值。

  对于ResourceDictionary或是类似字典的引用时将出现BamlDefAttributeKeyString、BamlDefAttributeKeyType、BamlKeyElementStart、BamlKeyElementEnd。这几位略有复杂,它们的作用域仅当前Node内,它们会有一个ValuePosition值,这个值表示的是与其关联的Record与当前Node中第一个子(顶)Node的偏移量,就是在二进制的Baml流中两条Record的Position的差值,计算每个子Node与顶Node的Posistion差,如果与指定的ValuePosition相同则为那个DefAttributeKey的引用,实在不明白可以去看GITHUB上大神的代码。

  相较之下,发现BAML中有一些内容是无法以XAML显示的,例如ConstructorParameters的相关内容、ProcessingInstruction、RoutedEvent、ClrEvent与ConnectionId等等,在.Net的代码中也没找到相关的处理方法,只能忽略了。

  上面内容写的有些含糊其辞,很抱歉,我也不知如何表达这些复杂的东西,看不懂没办法,在.NET的代码中自求多福吧。

BAML资源的解析笔记

原文:https://www.cnblogs.com/ccddnet/p/12954135.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!