Ext.Net中文随机乱码修复记

…衆ロ難τιáo~ 提交于 2019-12-09 19:23:23

Ext.Net各版本在渲染脚本的时候有一定概率会产生中文随机乱码。这个问题已经困扰笔者很长时间,网上也有很多人在问,而且也有人将问题提交到了Ext.Net官方论坛上,这个问题直到2.5版本的时候才被官方修复。虽然问题已经修复了,但是还是有个问题,那就是对于至今还在用v1版本的老工程怎么办?

还能怎么办,自己动手呗!

翻阅Ext.Net官方论坛,发现跟帖中只写明了2.5版本修复问题,修复过程还得看SVN,但是像笔者这种只能用免费版的人来说,看SVN下载源代码简直是个白日梦。看来只能自己发现问题的根源了。

废话不说了,马上抄起Reflactor大法一窥究竟。

经过笔者对1.6版本进行了一上午的比对、调试和跟踪,发现问题出在了InitScriptFilter和AjaxRequestFilter这两个类上。

摘入InitScriptFilter类的代码片段如下:

public class InitScriptFilter : BaseFilter
{
  private readonly StringBuilder html;
  private readonly Stream response;
  
  public override void Write(byte[] buffer, int offset, int count)
  {
    this.html.Append(HttpContext.Current.Response.ContentEncoding.GetString(buffer, offset, count));
  }
{

分析上面的代码可知,Write传入的是字节数组,然后使用ContentEncoding将字节数组转化成字符,拼接到html后面。这里就有个问题,当编码是GB2312或UTF8的时候,中文字将被编码成2字节或3字节,如果一个中文字的一半正好处在buffer的尾部,被分两次Write,那么这里就会产生乱码。

同样,AjaxRequestFilter也是一样的情况。

原因找到了,现在开始写补丁代码修复这个问题。

2.5版本中的官方修正是使用List<byte>的缓冲区代替直接写入到html字符串中,在Flush的时候再统一进行Encoding转成字符串。

这个方法在1.6版本中很难进行修正,因为我们的修正操作是要在IL代码中进行的,修改太多会很麻烦。

为了简便起见,我设计的方案是这样的:

在Write的时候对字节数组编码转成字符串时,如果遇到不能成功转码的情况,就将不能转码的字节排除并记下,延后到下一次Write的时候跟后面的字节数组一起再进行转码。代码片段如下:

private Queue<byte> lastBytes;    // 添加到类成员变量

public InitScriptFilter(Stream stream)   // 构造函数
{
    this.response = stream;
    this.html = new StringBuilder();
    lastBytes = new Queue<byte>();    // 添加这句
}

public override void Write(byte[] buffer, int offset, int count)  // 重写整个Write函数
{
    byte[] buf = new byte[count + lastBytes.Count];
    int i = 0;
    while (lastBytes.Count > 0) buf[i++] = lastBytes.Dequeue();
    Array.Copy(buffer, offset, buf, i, count);

    Encoding encoding = Encoding.GetEncoding(HttpContext.Current.Response.ContentEncoding.CodePage, new EncoderReplacementFallback(), new DecoderExceptionFallback());
    string str = null;
    for (i = 0; i <= 2; i++)
    {
        try
        {
            str = null;
            str = encoding.GetString(buf, 0, buf.Length - i);
            break;
        }
        catch (DecoderFallbackException) { }
    }
    if (str != null)
    {
        for (int j = i; j >= 1; j--) lastBytes.Enqueue(buf[buf.Length - j]);
        this.html.Append(str);
    }
}

好了,修正代码写好了,怎么修正到DLL里面去呢?笔者用了ildasm和ilasm组合大法。

首先用ildasm将Ext.Net.dll反编译,获得Ext.Net.il和好几千个资源文件。所使用的命令如下:

ildasm /utf8 /output=Ext.Net.il Ext.Net.dll

然后再用把写好的修正代码也编译一份DLL然后用ildasm反编译。

之后用Notepad++等文本编辑器将新代码覆盖到Ext.Net.il中的相应位置。AjaxRequestFilter类也用相同的方法处理。

改完之后,用ilasm进行重新编译。命令如下:

ilasm /dll /optimize /resource=Ext.Net.res /output=Ext.Net.fixed.dll Ext.Net.il

在等待了命令行输出了很大一堆文字后,本以为可以顺利地编译成功,但是意想不到的事情发生了。ilasm输出了一段错误信息:

Error: failed to read expected 10599816 bytes from mgd resource file 'Ext.Net.Build.Ext.Net.extjs.resources.images.slate.box.tb-blue.gif'
Could not create output file, error code=0x80004005

天哪,这什么问题,好像不是IL代码的问题。没办法,笔者只好爬上梯子求助好久不见谷哥。

据谷哥所述,ilasm有个毛病,不能编译资源文件超过1024个的程序集,并且程序集的总长度不能超过10M,如果超过了这两个要求,就会出错。只能将所有的资源文件单独列出来,使用VS进行编译,然后用ilmerge工具将两个程序集合并。

好了,解决方案在了,现在开始动手。

将Ext.Net.il中所有.mresource { }样子的代码全部注释掉,然后再用ilasm进行重编译,这次成功获得了一个不带任何资源的程序集。

用VS新建一个类库工程,将ildasm反编译出来的所有资源文件加入工程中,并且将所有文件的生成操作设置成“嵌入的资源”。这里需要注意,必须把所有资源文件以Ext.Net.Build.开头的名字去掉Ext.Net.,这个Ext.Net.是ildasm加上去的,程序集中的资源名实际上并没有这个命名空间前缀。然后编译,生成一个只带资源文件的程序集Ext.Net.res.dll。

最后用ilmerge将两个程序集组合成一个程序集,命令如下:

ilmerge /ndebug /v2 /out:Ext.Net.dll Ext.Net.fixed.dll Ext.Net.res.dll

至此,修正了随机字符乱码的Ext.Net就成功编译完成了。

以上过程适用Ext.Net v2.5之前的所有版本,各版本的实际代码可能有所不同,操作时需按照实际代码进行修改。

谢谢各位!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!