在CLR中本地化正在运行的.NET窗口

前段时间在研究某游戏辅助,老外出品,支持七种语言,可这辅助相关的插件却少有中文,因为作者都是老外,并且他们不愿意添加中文。有一些没有加密的插件就被善良的国内用户使用工具软件手工汉化了,但是经过混淆加密的插件就比较困难了,一是需要解密,二是插件数量多更新快,最后弄得只好放弃。有一天,一位用户问我,能不能做一个补丁程序,不去解密也不去修改源程序,只是在窗口显示的时候把文字汉化,他的意思就是hook窗体显示的过程,显然,这是个很不错的想法。

很多年前,曾经流行过一个名叫“南极星”的软件,后来又出了个“金山快译”,这两个都是可以把程序界面进行本地化的外挂,而我要做的却是补丁,也就是内挂,虽然形式不同,但原理上总是相通的吧。抱着这种想法,开始了新的代码之旅,但没过多久我就彻底放弃了,主要原因是只支持window窗口,对WPF无效,因为WPF窗体中的内容不是有效控件,而且在“自动”方面上更是难以为继。

我的关子已经卖得够多了吧,你知道的,最终我还是达到了目的。

.NET的程序只有在在.NET的环境中才会如渔得水般的灵活。对于普通winform和WPF窗体,在窗口构造函数运行之后,我们只需要遍历窗口类的所有私有字段就可以得到全部的控件了,直接修改其对应的文字,这就是汉化的全过程!那么问题来了,一如何拿到窗口的实例引用,二如何在窗口显示以前修改文字,三对于用户自定义的窗体控件如何处理。下面一一解答。

先来说第二个问题,是通过MethodReplace的不正当手段实现的,原理就是在CLR中修改方法MethodDesc中代码地址来实现的,在调用源方法的方法还没有被Jit前用新方法的地址来代替源方法,使源方法在被调用时整个方法体转向到新方法中(具体过程请Google)。因为显示窗口的方法都是由.NET提供现成编译好的本地代码,是无法替换地址的,所以最好的切入点就是窗口的构造函数。通过当前Domain的AssemblyLoad事件,可以轻易的在程序集被装载时拿到控制权,然后遍历程序集中所有的窗口类,然后对它们的构造函数进行地址转向,这样就抢在了创建窗口的方法被Jit之前。想一想,所有窗口中的控件都需要在构造函数中创建,这通常是通过调用一个名为InitializeComponent的方法,但是这个方法名是不靠谱的,因为它会被混淆,所以仍然要在新方法中使用Opcodes.Calli字段以正确的签名来调用构造方法的源地址(具体请参看MSDN)来完成窗体内控件的初始化过程,这样一来问题就解决了,直接遍历修改即可。等等,怎么修改的内容不全面,还有遗漏。好吧,我忘了有些列表的内容是在窗口第一次被显示时被初始化的,这个问题是VS的IDE造成的,弄得大家都喜欢把用户初始化的数据放在窗口的Load事件中。如何应对?很简单,我把汉化的过程也添加到Load事件中,这样就让汉化的过程发生在窗口原始Load事件之后了。

在第二个问题完美解决时,第一个问题也被解决了,窗口的构造函数是实例方法,实例方法的第一个参数永远是this,因此,添加事件、反射修改私有字段都得以实现,还可以实现更多本地化以外的事情……

第三个问题相对就简单多了,唯独的只是繁杂。我们知道绝大多数近件的文字都放在名为Text的属性之中,直接修改或是反射修改就可以,但有少数控仍需要特殊处理,例如:ToolTip控件的Caption属性,菜单的层级结构,列表近件中的内容集合,PropertyGrid中的特性名称。再有,就是一些自定义风格的窗口类,它们控件的文字可能会放在名为Text、Title、SubTitle、Caption、Value1、Value2等等属性中,利用反射遍历查询一下就可以了,当然还要为这些类型和字段做缓存的,这会提升效率。

后来,功能虽然实现了,但它并不好用,用户体验很差劲,主要是自动翻译的效果非常不理想,但我实在是无法解决这种复杂的专业问题的,所以,果断放弃自动翻译,转为输出列表,然后再有目的的翻译。

时间: 2024-10-06 03:56:43

在CLR中本地化正在运行的.NET窗口的相关文章

CLR中的程序集加载

本次来讨论一下基于.net平台的CLR中的程序集加载的机制: [注:由于.net已经开源,可利用vs2015查看c#源码的具体实现] 在运行时,JIT编译器利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所引用的类型.在AssemblyRef元数据表的记录项中,包含构成程序集的强名称的各个部分.JIT编译器获取包括名称(无扩展名和路径).版本.语言文化和公钥标记,将这些连接成一个字符串.JIT编译器将该标识匹配的一个程序集加载到AppDomain中.] CLR内

node.js中的交互式运行环境-REPL

<Node.js权威指南>第2章Node.js中的交互式运行环境--REPL 开发者可以在该环境中很方便地输入各种JavaScript表达式并观察表达式的运行结果. 在学习Node.js框架的过程中,通过该运行环境的使用,我们可以很方便地了解Node.js中定义的各种对象所拥有的各种属性及方法.本节为大家介绍在REPL运行环境中操作变量 2.2 在REPL运行环境中操作变量 在REPL运行环境中,可以使用var关键字来定义一个变量并为其赋值,但是在输入了对其赋值进行的表达式后,该表达式的执行结

Windows 8 IIS中配置PHP运行环境的方法

在Windows 8 的IIS(8.0)中搭建PHP运行环境: 一:安装IIS服务器 1.进入控制面板>>程序和功能>>打开或关闭Windows 功能,找到Internet信息服务,记得选中CGI这一项 2.安装完成后在浏览器中打开localhost,查看是否能显示IIS的欢迎页面: 二:下载安装配置PHP环境 1.下载PHP,官网地址:http://windows.php.net/download/, 2.将下载的zip压缩包解压到D盘下,解压后为E:\php; 3.进入控制面板

Tracker 是一个运行于浏览器书签栏的 JavaScript 嗅探工具,她将被启动于其他网页之后,为了协助您了解目标网页中 JavaScript 的运行情况

1 概述 Tracker 是一个运行于浏览器书签栏的 JavaScript 嗅探工具,她将被启动于其他网页之后,为了协助您了解目标网页中 JavaScript 的运行情况,包括:执行覆盖率.执行行数.是否存在执行或语法错误等信息,当你对一个目标网页使用 Tracker,该网页的上方将加载进来一个 Tracker 导航栏. 不用安装,快速开始 也欢迎体验桌面客户端版 Tracker 是一个运行于浏览器书签栏的 JavaScript 嗅探工具,她将被启动于其他网页之后,为了协助您了解目标网页中 Ja

在IIS中配置PHP运行环境简单步骤-注意事项

在IIS中配置PHP运行环境简单步骤 安装 IIS 7.0 打开 Control Panel\Programs\Programs and Features\Turn Windows features on or off, 在打开的对话框中勾选上这两个选项: 单击OK等待片刻, IIS就安装完成了. 安装 PHP 5.2.6 1. 下载: PHP 5.2.6 ;  其它版本的 PHP 下载. 2. 将压缩包解压, 放到一个目录中, 比如 D:\php 3. 将 php.ini-dist 复制到 C

linux中安装wine运行windows程序

http://blog.csdn.net/pipisorry/article/details/41653361 Wine的介绍 Wine是Wine Is Not an Emulator(Wine不是模拟器)的缩写,其实是一个转换层(或程序装入器),能够在Linux及与POSIX兼容的其他类似操作系统上运行Windows应用程序.Wine在Linux上无法模拟Windows应用程序,而是提供了另外的方法来实施DLL(典型的Windows应用程序可以调用这些DLL)和代替Windows NT内核的进

Android技术16:编写Android中直接可运行的二进制文件

我们都知道Android中所有应用程序都运行在Android的Dalvik虚拟机上,一般程序不直接与操作系统打交道,即便调用底层的方法也通过JNI技术.不过我们可以直接使用C语言编写二进制文件,直接在底层运行.下面演示其步骤. 1.安装下载编译器和链接器软件.Sourcery G++ Lite Edition for ARM. arm-none-linux-gnueabi-gcc.exe是编译命令 bin/arm-none-linux-gnueabi-ld.exe是链接命令 2.编写C代码 为了

IIS7中配置FastCGI运行PHP

环境说明: 操作系统:使用windows 2008 server 64位系统,IIS7.5PHP版本:官方下载PHP 5.4.16 VC9 x86 Non Thread SafeZIP版本.PHP路径:C:\php-5.4.16\ 配置步骤: 解压PHP文件,修改目录名放到C盘.目录地址为C:\php-5.4.16 复制php.ini-production改名为php.ini,先参考PHP.ini参数说明修改.并修改PHP对FastCGI支持: winmail站点打开按下图添加相关的选项:选择“

Linux内核学习--写一个c程序,并在内核中编译,运行

20140506 今天开始学习伟大的开源代表作:Linux内核.之前的工作流于几个简单命令的应用,因着对Android操作系统的情愫,"忍不住"跟随陈利君老师的步伐,开启OS内核之旅.学习路径之一是直接从代码入手,下面来写一个hello.c内核模块. 说明: 这个路径/usr/src/linux-headers-2.6.32-22/include/linux是引用的头文件. 内核模块固定格式:module_init()/ module_exit(),module函数是从头文件中来的.