Hello World
Spiga

定制Paste from Visual Studio插件(上)

2009-12-16 10:56 by 老赵, 4423 visits

我在上一篇文章里谈了我常用Paste from Visual Studio(下文称VSPaste)的插件,这大大方便了我写博客时贴代码的工作。不过今天早上有朋友在我博客后面留言说:“VSPaste没法显示行号,不知大家有没有办法解决?”其实这点很容易,写个小程序,把VSPaste生成的HTML再进行一番处理不就可以了嘛。不过最方便的做法还是让VSPaste直接生成带行号的代码块,不是吗?那么,我们就来自己动手丰衣足食解决这个问题吧。

这个问题说简单不简单,说复杂不复杂。其“不简单”之处在于VSPaste是不提供源代码的。而“不复杂”又在于……您一会儿就明白了。那么我们就开始吧,别担心,我和您一样不知道Windows Live Writer的插件体系。不过这不影响我们用正常的推理逻辑来解决问题。

了解VSPaste

第一步自然是要了解VSPaste插件,而这唯一的途径便是它的分发版本。您可以下载到VSPaste.msi文件,安装后就可以使用这个插件了。这个插件的部署原理很简单,只是在Windows Live Writer安装目录(如C:\Program Files\Windows Live\Writer\)的Plugins文件夹下放一个dll而已(话说这么容易的方式,为什么就非要提供一个安装文件?这么做很专业吗?),于是我们用.NET Refactor便可以打开这个文件一看究竟。

我相信您也会和我一样首先关注VSPaste类。于是我们查看打开它的CreateContent方法:

public class VSPaste
{
    public override DialogResult CreateContent(
        IWin32Window dialogOwner,
        ref string newContent)
    {
        try
        {
            if (Clipboard.ContainsData(DataFormats.Rtf))
            {
                newContent =
                    "<pre class=\"code\">" + 
                    Undent(
                        HTMLRootProcessor.FromRTF(
                            (string)Clipboard.GetData(DataFormats.Rtf))) + 
                    "</pre><a href=\"http://11011.net/software/vspaste\"></a>";
                return DialogResult.OK;
            }
        }
        catch
        {
            MessageBox.Show(
                "VS Paste could not convert that content.",
                "VS Paste Problem",
                MessageBoxButtons.OK,
                MessageBoxIcon.Hand);
        }

        return DialogResult.Cancel;
    }

    ...
}

几乎没有比这再简单的逻辑了:我们在Visual Studio里复制了一段代码之后,这段文字会被保存为RTF格式,程序将其取出之后,再把它转化为HTML,最后在前后再加上些标签就行了。

尝试解决方案

VSPaste的难点其实是RTF到HTML的转化工作——这从来就是个难题,虽然已经有无数人投身于此。其实在一开始,我也并不想着要“修改”VSPaste,看到CreateContent这点代码相信您也觉得重新写一个更为简单。但是,打开HTMLRootProcessor.FromRTF方法之后我便放弃了,因为实在太复杂。然后我在网上找了找RTF转HTML的类库,还是找不到立即可可用的,于是最后还是下定决心从VSPaste入手。

如果要修改VSPaste,最方便的做法自然是将一个程序集的C#代码导出,而不少.NET Reflector的插件可以轻易完成这个工作。不过,您再回去看看程序集里的成员,就会发现其实这点并不容易。因为……编译后自动生成的一些类和成员,并无法再重新用C#编译器编译通过(主要是命名方面的问题)。不过可能经过简单的修改就行了,但我不知道——而且我们其实只要改一点点逻辑就行了,我不想搞那么复杂。既然如此,那么我们还是从IL入手吧。

别怕,我也不会IL。

要获得VSPaste程序集的IL,自然需要用到ildasm.exe,如下:

ildasm VSPaste.dll /output:VSPaste.il

于是我们就得到了VSPaste.il文件,您可以用某个文本编辑器打开,然后搜索CreateContent便可以定位到这个方法:

.method public hidebysig virtual instance valuetype [System....
        CreateContent(class [System.Windows.Forms]System.Win...
                      string& newContent) cil managed...
{
  // Code size       101 (0x65)
  .maxstack  4
  .locals init (valuetype [System.Windows.Forms]System.Windo...
           bool V_1)
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldsfld     string [System.Windows.Forms]System...
    IL_0007:  call       bool [System.Windows.Forms]System.W...
    IL_000c:  ldc.i4.0
    IL_000d:  ceq
    IL_000f:  stloc.1
    IL_0010:  ldloc.1
    IL_0011:  brtrue.s   IL_0042
    ...
    IL_0029:  call       string HTMLRootProcessor::FromRTF(string)
    IL_002e:  call       string VSPaste.VSPaste::Undent(string)
    IL_0033:  ldstr      "</pre><a href=\"http://11011.net/software/vspaste\"></a>"
    IL_0038:  call       string [mscorlib]System.String::Concat(string,
                                                              string,
                                                              string)
    ...

好一堆“乱码”,不过其中可以定位到某些关键字符串,例如上面那行链接。那么我们先做一个尝试,把它去除如何?于是我们把不动其他内容,只是把那行代码修改成:

IL_0033:  ldstr      "</pre>"

OK,保存,然后使用ilasm.exe将其编译成VSPaste2.dll文件:

...>ilasm VSPaste.il /output:VSPaste2.dll /dll

Microsoft (R) .NET Framework IL Assembler.  Version 2.0.50727.4016
Copyright (c) Microsoft Corporation.  All rights reserved.
Assembling 'VSPaste.il'  to DLL --> 'VSPaste2.dll'
Source file is ANSI

Assembled method VoidProcessor::Open
Assembled method VoidProcessor::Close
...
Class 7
Class 8
Class 9
Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
Writing PE file
Operation completed successfully

好,关闭Windows Live Writer,再将VSPaste2.dll文件复制到Plugins目录下,然后重新打开Windows Live Writer,使用一下,是不是很有效?

谁说一定要学习IL才能摆弄这些,是不?

相关文章

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

23 条回复

  1. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-12-16 11:00:00

    清晰的思路一般比il的成本更高啊

  2. 飞无痕落无声
    *.*.*.*
    链接

    飞无痕落无声 2009-12-16 11:01:00

    沙发!

  3. GuaiKe
    *.*.*.*
    链接

    GuaiKe 2009-12-16 11:07:00

    很不错啊~~~

  4. 老赵
    admin
    链接

    老赵 2009-12-16 11:18:00

    @韦恩卑鄙 alias:v-zhewg
    不过清晰的思路可以用其他方面来培养,而且可以用在更多地方。
    也就说思路属于能力范畴,而IL与之相比就是具体技术了。

  5. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-12-16 11:24:00

    @Jeffrey Zhao
    就好像学通了哲学 什么人际关系学、心理学都是小菜一样。
    我觉得用清晰思维解决问题后 说"不一定需要il" 多少有点欺负人捏。。。

  6. 老赵
    admin
    链接

    老赵 2009-12-16 11:34:00

    @韦恩卑鄙 alias:v-zhewg
    但的确是不一定需要IL嘛,话说最近我打算补上“老赵谈IL”的最后一篇,需要各种思路啊。
    不过我还在等GTSC给我解决那个Profiler的问题,太tmd慢了,整一周了。

  7. 清茶
    *.*.*.*
    链接

    清茶 2009-12-16 12:20:00

    写的代码我怎么看不懂啊,看来还要加强学习啊!

  8. 老赵
    admin
    链接

    老赵 2009-12-16 12:21:00

    @清茶
    哪段代码看不懂?
    IL的话不用看,C#的话说明你没仔细看……

  9. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-12-16 12:53:00

    其实我觉得IL很好懂么。。。。

    指令就那么几个,比C#简单多了。

  10. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-12-16 12:58:00

    而且,类似于这样的IL:

        IL_0029:  call       string HTMLRootProcessor::FromRTF(string)
        IL_002e:  call       string VSPaste.VSPaste::Undent(string)
    


    其实对应的C#是:
    VSPaste.Undent( HTMLRootProcessor.FromRTF( ... ) );
    


    这样的嵌套调用对于IL来说就是两个call堆叠起来,这不是很像F#么?

    HTMLRootProcessor.FromRTF ...
      |> VSPaste.Undent
    

  11. 老赵
    admin
    链接

    老赵 2009-12-16 13:01:00

    @Ivony...
    其实知道一点点汇编的人来说,看明白IL是很容易……

  12. 老赵
    admin
    链接

    老赵 2009-12-16 13:02:00

    @Ivony...
    但是方法如果有不止一个参数,或者包含了this,那么load参数的顺序阿,调用方法的啊还是有点麻烦的……

  13. DiryBoy
    *.*.*.*
    链接

    DiryBoy 2009-12-16 13:04:00

    我不清楚VSPaste有没有更新过,之前用的时候发现那个Undent是不能正常工作的,自己修改过一下,顺便把空链接去掉了,现在一直在用这个版本。
    http://www.cnblogs.com/Diryboy/archive/2008/12/02/1345407.html

  14. 老赵
    admin
    链接

    老赵 2009-12-16 13:08:00

    @DiryBoy
    你咋就写过了呢……

  15. DiryBoy
    *.*.*.*
    链接

    DiryBoy 2009-12-16 13:22:00

    @Jeffrey Zhao
    当时用起来不爽就改了。不过没有加上行号的功能~~

  16. 老赵
    admin
    链接

    老赵 2009-12-16 13:24:00

    @DiryBoy
    主要是个方法……

  17. DiryBoy
    *.*.*.*
    链接

    DiryBoy 2009-12-16 13:40:00

    @Jeffrey Zhao
    我当时写得混乱了一点,现在看你的文章清晰多了。

  18. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-12-16 13:42:00

    Jeffrey Zhao:
    @Ivony...
    其实知道一点点汇编的人来说,看明白IL是很容易……




    IL比汇编还是简单多了,那啥,IL还是面向对象的。。。。

    其实完全可以把IL看作一种没有表达式只有指令的面向对象的语言。

    除了,除了好像封装对IL没啥约束力。。。。

  19. shenzhen
    *.*.*.*
    链接

    shenzhen 2009-12-17 00:27:00

    赵老师。你没提供修改后的dll下载啊?

  20. 银河
    *.*.*.*
    链接

    银河 2009-12-18 11:40:00

    老赵果然厉害。 :)
    这种解决问题的思路值得我们学习。

  21. 张荣华
    *.*.*.*
    链接

    张荣华 2009-12-18 16:03:00

    这种解决问题的思考过程值得学习啊,

    shenzhen:赵老师。你没提供修改后的dll下载啊?


    还是按照老赵的过程自己实现一次更好玩啊,不能做偷懒啊。

  22. 小李刀刀
    116.52.9.*
    链接

    小李刀刀 2010-04-04 15:27:09

    老赵,我直接用.NET Reflector导出源代码,做了多处修改之后编译通过而且可用。但是得到了结果却不懂得原理,能否指点一下:http://www.cnblogs.com/dvbhack/archive/2010/04/04/vspaste-customization-with-net-reflector.html

  23. 老赵
    admin
    链接

    老赵 2010-04-04 16:44:53

    @小李刀刀

    你想知道哪些原理呢?

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我