上一篇文章里我们进行了简单的实验,验证了通过修改IL生成新插件的可行性,不过我们要做的事情还有很多,因为我们实际要做的事情其实是……插入行号。这需要我们补充新的逻辑,并且对CreateContent进行修改。那么我们又该如何写这大段大段的IL呢?没关系,其实这些事情不懂IL也可以做。
添加行号
首先,我们需要写一个AddLines方法,修改一下HTML:
public static string AddLines(string html)
{
var lines = html.Trim().Split('\n');
string pattern =
"<span style=\"color:black; font-weight:bold;\">{0:" +
new String('0', lines.Length.ToString().Length) +
"}: </span>";
for (int i = 0; i < lines.Length; i++)
{
lines[i] = String.Format(pattern, i + 1) + lines[i];
}
return String.Join("\n", lines.ToArray());
}
这段方法的作用是在每行HTML之前加入一个<span />来显示行号,其中通过总行数来计算行号的“位数”,这样便可以在显示如“01”、“02”这样的行号。那么,这段方法又如何给CreateContent方法调用呢?
生成结构相同的IL
别急,还是先来简单观察一下目前CreateContent方法的IL代码——不求看懂,但求了解个两行内容:
.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>"
IL_0038: call string [mscorlib]System.String::Concat(string,
string,
string)
...
别的不管,看到两个call指令吗?相信即便您不懂IL,也可以推测出它们是在“调用方法”。从中我们也可以发现,这个call指令……不就是指定一个方法的名称(包括命名空间)和参数吗?既然如此,我们构建一个类似代码“结构”也实在太容易了:
public class HTMLRootProcessor
{
public static string FromRTF(string s) { return null; }
}
namespace VSPaste
{
public class VSPaste
{
public static string AddLines(string html) { /* ... */ }
public static string Undent(string s) { return null; }
public DialogResult CreateContent(
IWin32Window dialogOwner,
ref string newContent)
{
try
{
if (Clipboard.ContainsData(DataFormats.Rtf))
{
var content = (string)Clipboard.GetData(DataFormats.Rtf);
var html = Undent(HTMLRootProcessor.FromRTF(content));
var withLines = AddLines(html);
newContent = "<pre class=\"code\">" + withLines + "</pre>";
return DialogResult.OK;
}
}
catch
{
MessageBox.Show(
"VS Paste could not convert that content.",
"VS Paste Problem",
MessageBoxButtons.OK,
MessageBoxIcon.Hand);
}
return DialogResult.Cancel;
}
}
}
于是,我们把这段代码随意放在某个程序集中,然后同样使用ildasm.exe获得其IL代码:
.class public auto ansi beforefieldinit VSPaste.VSPaste
extends [mscorlib]System.Object
{
.method public hidebysig static string
AddLines(string html) cil managed
{
// Code size 121 (0x79)
...
} // end of method VSPaste::AddLines
...
.method public hidebysig instance valuetype [System.Windows.Forms]Syste
CreateContent(class [System.Windows.Forms]System.Windows.Forms.
string& newContent) cil managed
{
// Code size 97 (0x61)
.maxstack 4
...
IL_001d: call string HTMLRootProcessor::FromRTF(string)
IL_0022: call string VSPaste.VSPaste::Undent(string)
IL_0027: stloc.1
IL_0028: ldloc.1
IL_0029: call string VSPaste.VSPaste::AddLines(string)
IL_002e: stloc.2
IL_002f: ldarg.2
IL_0030: ldstr "<pre class=\"code\">"
IL_0035: ldloc.2
IL_0036: ldstr "</pre>"
IL_003b: call string [mscorlib]System.String::Concat(string,
string,
string)
...
} // end of method VSPaste::CreateContent
} // end of class VSPaste.VSPaste
可以看到,在CreateContent方法中,也有和之前相同的FromRTF方法与Undent方法的调用,以及我们新的AddLines方法。因此,我们只要打开VSPaste.il,先把AddLines方法的IL复制到VSPaste类中,然后用新的CreateContent方法体(即从.maxstack开始的部分)替换旧的实现即可。
成果
保存VSPaste.il,使用ilasm.exe生成dll,再把它复制到Windows Live Writer的Plugins目录中。那么我就第一个试用吧:
01: public static string AddLines(string html)
02: {
03: var lines = html.Trim().Split('\n');
04: string pattern =
05: "<span style=\"color:black; font-weight:bold;\">{0:" +
06: new String('0', lines.Length.ToString().Length) +
07: "}: </span>";
08:
09: for (int i = 0; i < lines.Length; i++)
10: {
11: lines[i] = String.Format(pattern, i + 1) + lines[i];
12: }
13:
14: return String.Join("\n", lines.ToArray());
15: }
效果如何,还不错吧?当然,这个逻辑其实还不够成熟,不过通过这个思路,您可以把VSPaste修改为任何需要的样子。当然,这两篇文章只是一种“体验”,这种修改方式并不“健康”也不值得提倡。而且我忽然意识到,如果我们真只是为了使用VSPaste中RTF转HTML的功能,其实也可以简单地引用它,然后调用其中的方法……反正都是public的……
相关文章
- 定制Paste from Visual Studio插件(上)
- 定制Paste from Visual Studio插件(下)