以前在 Windows Live Writer 中一直使用 Paste from Visual Studio 插件来在博文中粘贴代码。这个插件对 Visual Studio 2008 很好用,但是从 Visual Stduio 2010 中粘贴代码的话,每个中文字符后面,会跟着一个乱码,如下 ClipboardTester.cs 所示:
01: using System; 02: using System.Windows; 03: 04: namespace Skyiv.Tester 05: { 06: static class ClipboardTester 07: { 08: [STAThread] 09: static void Main() 10: { 11: var obj = Clipboard.GetDataObject(); 12: foreach (var v in obj.GetFormats()) 13: { 14: Console.WriteLine("[{0}]", v); 15: Console.WriteLine("-------- 开a始? --------"); 16: try { Console.WriteLine("{0}", obj.GetData(v)); } 17: catch (Exception ex) { Console.WriteLine(ex); } 18: Console.WriteLine("-------- 结á束? --------"); 19: Console.WriteLine(); 20: } 21: } 22: } 23: }
我以前是手工一个个删除这些乱码。
查找根源
现在让我们来查找为什么会有乱码的原因吧。首先用以下命令编译上面给出的 ClipboardTester.cs 程序:
E:\CS\VSPaste> csc ClipboardTester.cs -r:C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationCore.dll Microsoft(R) Visual C# 2010 编译器 4.0.30319.1 版 版权所有(C) Microsoft Corporation。保留所有权利。
然后在 Visual Studio 2010 中使用“Ctrl-A”、“Ctrl-C”将这段源程序代码送到系统剪贴板,再运行 ClipboardTester.exe 从系统剪贴板中取出数据:
E:\CS\VSPaste> ClipboardTester [Text] -------- 开始 -------- using System; using System.Windows; namespace Skyiv.Tester { static class ClipboardTester { [STAThread] static void Main() { var obj = Clipboard.GetDataObject(); foreach (var v in obj.GetFormats()) { Console.WriteLine("[{0}]", v); Console.WriteLine("-------- 开始 --------"); try { Console.WriteLine("{0}", obj.GetData(v)); } catch (Exception ex) { Console.WriteLine(ex); } Console.WriteLine("-------- 结束 --------"); Console.WriteLine(); } } } } -------- 结束 -------- [UnicodeText] -------- 开始 -------- using System; using System.Windows; namespace Skyiv.Tester { static class ClipboardTester { [STAThread] static void Main() { var obj = Clipboard.GetDataObject(); foreach (var v in obj.GetFormats()) { Console.WriteLine("[{0}]", v); Console.WriteLine("-------- 开始 --------"); try { Console.WriteLine("{0}", obj.GetData(v)); } catch (Exception ex) { Console.WriteLine(ex); } Console.WriteLine("-------- 结束 --------"); Console.WriteLine(); } } } } -------- 结束 -------- [System.String] -------- 开始 -------- using System; using System.Windows; namespace Skyiv.Tester { static class ClipboardTester { [STAThread] static void Main() { var obj = Clipboard.GetDataObject(); foreach (var v in obj.GetFormats()) { Console.WriteLine("[{0}]", v); Console.WriteLine("-------- 开始 --------"); try { Console.WriteLine("{0}", obj.GetData(v)); } catch (Exception ex) { Console.WriteLine(ex); } Console.WriteLine("-------- 结束 --------"); Console.WriteLine(); } } } } -------- 结束 -------- [Rich Text Format] -------- 开始 -------- {\rtf\ansi{\fonttbl{\f0 NSimSun;}}{\colortbl;\red0\green0\blue255;\red43\green14 5\blue175;\red163\green21\blue21;}\f0 \fs19 \cf1 using\cf0 System;\par \cf1 usi ng\cf0 System.Windows;\par \par \cf1 namespace\cf0 Skyiv.Tester\par \{\par \ cf1 static\cf0 \cf1 class\cf0 \cf2 ClipboardTester\cf0 \par \{\par [\cf2 STAThread\cf0 ]\par \cf1 static\cf0 \cf1 void\cf0 Main()\par \{\par \cf1 var\cf0 obj = \cf2 Clipboard\cf0 .GetDataObject();\par \cf1 for each\cf0 (\cf1 var\cf0 v \cf1 in\cf0 obj.GetFormats())\par \{\par \cf2 Console\cf0 .WriteLine(\cf3 "[\{0\}]"\cf0 , v);\par \cf2 Console \cf0 .WriteLine(\cf3 "-------- \uinput2\u24320 ?a\uinput2\u22987 ê? --------"\cf 0 );\par \cf1 try\cf0 \{ \cf2 Console\cf0 .WriteLine(\cf3 "\{0\}"\cf0 , obj.GetData(v)); \}\par \cf1 catch\cf0 (\cf2 Exception\cf0 ex) \{ \cf 2 Console\cf0 .WriteLine(ex); \}\par \cf2 Console\cf0 .WriteLine(\cf3 "- ------- \uinput2\u32467 ?á\uinput2\u26463 ê? --------"\cf0 );\par \cf2 C onsole\cf0 .WriteLine();\par \}\par \}\par \}\par \}\par } -------- 结束 -------- E:\CS\VSPaste>
从上面可以看出,出现乱码的根源在于 Visual Studio 2010 往系统剪贴板送 RTF 格式的内容时出错了。
修改 Paste from Visual Studio 插件
我们无法改变 Visual Studio 2010 的做法,只好修改 Paste from Visual Studio 插件了。以下步骤参照老赵的“定制 Paste from Visual Studio 插件”一文。
首先,使用 Reflector 查看 VSPaste.dll:
参照上图中的代码,编写一个 VSPaste2.cs:
01: using System; 02: using System.Windows.Forms; 03: using System.Text.RegularExpressions; 04: 05: public class HTMLRootProcessor 06: { 07: public static string FromRTF(string s) { return null; } 08: } 09: 10: namespace VSPaste 11: { 12: public class ContentSource 13: { 14: public virtual DialogResult CreateContent(IWin32Window dialogOwner, ref string newContent) 15: { 16: return DialogResult.Cancel; 17: } 18: } 19: 20: public class VSPaste : ContentSource 21: { 22: public static string Undent(string s) { return null; } 23: 24: public static string AddLines(string html) 25: { 26: string[] lines = html.Trim().Split('\n'); 27: string pattern = "<span style=\"color:black; font-weight:bold;\">{0:" + 28: new String('0', lines.Length.ToString().Length) + "}: </span>"; 29: for (int i = 0; i < lines.Length; i++) 30: lines[i] = String.Format(pattern, i + 1) + lines[i]; 31: return String.Join("\n", lines); 32: } 33: 34: public static string DeleteChineseGarbage(string str) 35: { 36: return Regex.Replace(str, @"(?<ch>\\u-?\d+ )..", "${ch}?"); 37: } 38: 39: public override DialogResult CreateContent(IWin32Window dialogOwner, ref string newContent) 40: { 41: try 42: { 43: if (Clipboard.ContainsData(DataFormats.Rtf)) 44: { 45: string data = DeleteChineseGarbage((string)Clipboard.GetData(DataFormats.Rtf)); 46: string str3 = AddLines(Undent(HTMLRootProcessor.FromRTF(data))); 47: newContent = "<pre class=\"code\">" + str3 + "</pre>"; 48: return DialogResult.OK; 49: } 50: } 51: catch 52: { 53: MessageBox.Show("VS Paste could not convert that content.", "VS Paste Problem", MessageBoxButtons.OK, MessageBoxIcon.Hand); 54: } 55: return DialogResult.Cancel; 56: } 57: } 58: }
上述程序中,第 34 行到第 37 行的 DeleteChineseGarbage 方法用于删除中文乱码。第 45 行调用这个方法。用以下命令编译和反汇编:
E:\CS\VSPaste> csc /t:library VSPaste2.cs Microsoft(R) Visual C# 2010 编译器 4.0.30319.1 版 版权所有(C) Microsoft Corporation。保留所有权利。 E:\CS\VSPaste> ildasm VSPaste2.dll /output:VSPaste2.il
得到的 VSPaste2.il 文件如下所示:
001: // Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.1 002: 003: 004: 005: 006: // Metadata version: v2.0.50727 007: .assembly extern mscorlib 008: { 009: .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. 010: .ver 2:0:0:0 011: } 012: .assembly extern System.Windows.Forms 013: { 014: .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. 015: .ver 2:0:0:0 016: } 017: .assembly extern System 018: { 019: .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. 020: .ver 2:0:0:0 021: } 022: .assembly VSPaste2 023: { 024: .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 025: .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 026: 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. 027: .hash algorithm 0x00008004 028: .ver 0:0:0:0 029: } 030: .module VSPaste2.dll 031: // MVID: {A944F44D-8C74-42FF-956B-B14EADD37E70} 032: .imagebase 0x00400000 033: .file alignment 0x00000200 034: .stackreserve 0x00100000 035: .subsystem 0x0003 // WINDOWS_CUI 036: .corflags 0x00000001 // ILONLY 037: // Image base: 0x00280000 038: 039: 040: // =============== CLASS MEMBERS DECLARATION =================== 041: 042: .class public auto ansi beforefieldinit HTMLRootProcessor 043: extends [mscorlib]System.Object 044: { 045: .method public hidebysig static string 046: FromRTF(string s) cil managed 047: { 048: // 代码大小 7 (0x7) 049: .maxstack 1 050: .locals init (string V_0) 051: IL_0000: nop 052: IL_0001: ldnull 053: IL_0002: stloc.0 054: IL_0003: br.s IL_0005 055: 056: IL_0005: ldloc.0 057: IL_0006: ret 058: } // end of method HTMLRootProcessor::FromRTF 059: 060: .method public hidebysig specialname rtspecialname 061: instance void .ctor() cil managed 062: { 063: // 代码大小 7 (0x7) 064: .maxstack 8 065: IL_0000: ldarg.0 066: IL_0001: call instance void [mscorlib]System.Object::.ctor() 067: IL_0006: ret 068: } // end of method HTMLRootProcessor::.ctor 069: 070: } // end of class HTMLRootProcessor 071: 072: .class public auto ansi beforefieldinit VSPaste.ContentSource 073: extends [mscorlib]System.Object 074: { 075: .method public hidebysig newslot virtual 076: instance valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult 077: CreateContent(class [System.Windows.Forms]System.Windows.Forms.IWin32Window dialogOwner, 078: string& newContent) cil managed 079: { 080: // 代码大小 7 (0x7) 081: .maxstack 1 082: .locals init (valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult V_0) 083: IL_0000: nop 084: IL_0001: ldc.i4.2 085: IL_0002: stloc.0 086: IL_0003: br.s IL_0005 087: 088: IL_0005: ldloc.0 089: IL_0006: ret 090: } // end of method ContentSource::CreateContent 091: 092: .method public hidebysig specialname rtspecialname 093: instance void .ctor() cil managed 094: { 095: // 代码大小 7 (0x7) 096: .maxstack 8 097: IL_0000: ldarg.0 098: IL_0001: call instance void [mscorlib]System.Object::.ctor() 099: IL_0006: ret 100: } // end of method ContentSource::.ctor 101: 102: } // end of class VSPaste.ContentSource 103: 104: .class public auto ansi beforefieldinit VSPaste.VSPaste 105: extends VSPaste.ContentSource 106: { 107: .method public hidebysig static string 108: Undent(string s) cil managed 109: { 110: // 代码大小 7 (0x7) 111: .maxstack 1 112: .locals init (string V_0) 113: IL_0000: nop 114: IL_0001: ldnull 115: IL_0002: stloc.0 116: IL_0003: br.s IL_0005 117: 118: IL_0005: ldloc.0 119: IL_0006: ret 120: } // end of method VSPaste::Undent 121: 122: .method public hidebysig static string 123: AddLines(string html) cil managed 124: { 125: // 代码大小 130 (0x82) 126: .maxstack 5 127: .locals init (string[] V_0, 128: string V_1, 129: int32 V_2, 130: string V_3, 131: char[] V_4, 132: int32 V_5, 133: bool V_6) 134: IL_0000: nop 135: IL_0001: ldarg.0 136: IL_0002: callvirt instance string [mscorlib]System.String::Trim() 137: IL_0007: ldc.i4.1 138: IL_0008: newarr [mscorlib]System.Char 139: IL_000d: stloc.s V_4 140: IL_000f: ldloc.s V_4 141: IL_0011: ldc.i4.0 142: IL_0012: ldc.i4.s 10 143: IL_0014: stelem.i2 144: IL_0015: ldloc.s V_4 145: IL_0017: callvirt instance string[] [mscorlib]System.String::Split(char[]) 146: IL_001c: stloc.0 147: IL_001d: ldstr "<span style=\"color:black; font-weight:bold;\">{0:" 148: IL_0022: ldc.i4.s 48 149: IL_0024: ldloc.0 150: IL_0025: ldlen 151: IL_0026: conv.i4 152: IL_0027: stloc.s V_5 153: IL_0029: ldloca.s V_5 154: IL_002b: call instance string [mscorlib]System.Int32::ToString() 155: IL_0030: callvirt instance int32 [mscorlib]System.String::get_Length() 156: IL_0035: newobj instance void [mscorlib]System.String::.ctor(char, 157: int32) 158: IL_003a: ldstr "}: </span>" 159: IL_003f: call string [mscorlib]System.String::Concat(string, 160: string, 161: string) 162: IL_0044: stloc.1 163: IL_0045: ldc.i4.0 164: IL_0046: stloc.2 165: IL_0047: br.s IL_0066 166: 167: IL_0049: ldloc.0 168: IL_004a: ldloc.2 169: IL_004b: ldloc.1 170: IL_004c: ldloc.2 171: IL_004d: ldc.i4.1 172: IL_004e: add 173: IL_004f: box [mscorlib]System.Int32 174: IL_0054: call string [mscorlib]System.String::Format(string, 175: object) 176: IL_0059: ldloc.0 177: IL_005a: ldloc.2 178: IL_005b: ldelem.ref 179: IL_005c: call string [mscorlib]System.String::Concat(string, 180: string) 181: IL_0061: stelem.ref 182: IL_0062: ldloc.2 183: IL_0063: ldc.i4.1 184: IL_0064: add 185: IL_0065: stloc.2 186: IL_0066: ldloc.2 187: IL_0067: ldloc.0 188: IL_0068: ldlen 189: IL_0069: conv.i4 190: IL_006a: clt 191: IL_006c: stloc.s V_6 192: IL_006e: ldloc.s V_6 193: IL_0070: brtrue.s IL_0049 194: 195: IL_0072: ldstr "\n" 196: IL_0077: ldloc.0 197: IL_0078: call string [mscorlib]System.String::Join(string, 198: string[]) 199: IL_007d: stloc.3 200: IL_007e: br.s IL_0080 201: 202: IL_0080: ldloc.3 203: IL_0081: ret 204: } // end of method VSPaste::AddLines 205: 206: .method public hidebysig static string 207: DeleteChineseGarbage(string str) cil managed 208: { 209: // 代码大小 22 (0x16) 210: .maxstack 3 211: .locals init (string V_0) 212: IL_0000: nop 213: IL_0001: ldarg.0 214: IL_0002: ldstr "(\?<ch>\\\\u-\?\\d+ ).." 215: IL_0007: ldstr "${ch}\?" 216: IL_000c: call string [System]System.Text.RegularExpressions.Regex::Replace(string, 217: string, 218: string) 219: IL_0011: stloc.0 220: IL_0012: br.s IL_0014 221: 222: IL_0014: ldloc.0 223: IL_0015: ret 224: } // end of method VSPaste::DeleteChineseGarbage 225: 226: .method public hidebysig virtual instance valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult 227: CreateContent(class [System.Windows.Forms]System.Windows.Forms.IWin32Window dialogOwner, 228: string& newContent) cil managed 229: { 230: // 代码大小 115 (0x73) 231: .maxstack 4 232: .locals init (string V_0, 233: string V_1, 234: valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult V_2, 235: bool V_3) 236: IL_0000: nop 237: .try 238: { 239: IL_0001: nop 240: IL_0002: ldsfld string [System.Windows.Forms]System.Windows.Forms.DataFormats::Rtf 241: IL_0007: call bool [System.Windows.Forms]System.Windows.Forms.Clipboard::ContainsData(string) 242: IL_000c: ldc.i4.0 243: IL_000d: ceq 244: IL_000f: stloc.3 245: IL_0010: ldloc.3 246: IL_0011: brtrue.s IL_0050 247: 248: IL_0013: nop 249: IL_0014: ldsfld string [System.Windows.Forms]System.Windows.Forms.DataFormats::Rtf 250: IL_0019: call object [System.Windows.Forms]System.Windows.Forms.Clipboard::GetData(string) 251: IL_001e: castclass [mscorlib]System.String 252: IL_0023: call string VSPaste.VSPaste::DeleteChineseGarbage(string) 253: IL_0028: stloc.0 254: IL_0029: ldloc.0 255: IL_002a: call string HTMLRootProcessor::FromRTF(string) 256: IL_002f: call string VSPaste.VSPaste::Undent(string) 257: IL_0034: call string VSPaste.VSPaste::AddLines(string) 258: IL_0039: stloc.1 259: IL_003a: ldarg.2 260: IL_003b: ldstr "<pre class=\"code\">" 261: IL_0040: ldloc.1 262: IL_0041: ldstr "</pre>" 263: IL_0046: call string [mscorlib]System.String::Concat(string, 264: string, 265: string) 266: IL_004b: stind.ref 267: IL_004c: ldc.i4.1 268: IL_004d: stloc.2 269: IL_004e: leave.s IL_0070 270: 271: IL_0050: nop 272: IL_0051: leave.s IL_006b 273: 274: } // end .try 275: catch [mscorlib]System.Object 276: { 277: IL_0053: pop 278: IL_0054: nop 279: IL_0055: ldstr "VS Paste could not convert that content." 280: IL_005a: ldstr "VS Paste Problem" 281: IL_005f: ldc.i4.0 282: IL_0060: ldc.i4.s 16 283: IL_0062: call valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string, 284: string, 285: valuetype [System.Windows.Forms]System.Windows.Forms.MessageBoxButtons, 286: valuetype [System.Windows.Forms]System.Windows.Forms.MessageBoxIcon) 287: IL_0067: pop 288: IL_0068: nop 289: IL_0069: leave.s IL_006b 290: 291: } // end handler 292: IL_006b: nop 293: IL_006c: ldc.i4.2 294: IL_006d: stloc.2 295: IL_006e: br.s IL_0070 296: 297: IL_0070: nop 298: IL_0071: ldloc.2 299: IL_0072: ret 300: } // end of method VSPaste::CreateContent 301: 302: .method public hidebysig specialname rtspecialname 303: instance void .ctor() cil managed 304: { 305: // 代码大小 7 (0x7) 306: .maxstack 8 307: IL_0000: ldarg.0 308: IL_0001: call instance void VSPaste.ContentSource::.ctor() 309: IL_0006: ret 310: } // end of method VSPaste::.ctor 311: 312: } // end of class VSPaste.VSPaste 313: 314: 315: // ============================================================= 316: 317: // *********** 反汇编完成 *********************** 318: // 警ˉ告: 创建了 Win32 资ê源文件 VSPaste2.res
现在,使用以下命令反汇编 VSPaste.dll:
E:\CS\VSPaste> ildasm VSPaste.dll /output:VSPaste.il
把 VSPaste2.il 文件中第 226 行到第 300 行的 VSPaste::CreateContent 方法替代 VSPaste.il 中的同名方法。并且把 VSPaste2.il 文件中第 206 行到第 224 行的 VSPate::DeleteChineseGarbage 方法加入到 VSPaste.il 文件中去。最后,使用以下命令重新进行汇编:
E:\CS\VSPaste> ilasm /dll /quiet VSPaste.il
将得到的 VSPaste.dll 拷贝到 Windows Live Writer 安装目录的 Plugins 目录,在我的机器上是 C:\Program Files\Windows Live\Writer\Plugins\,就行了。现在,让我们运行 Windows Live Writer,咦,VSPaste 插件不见了。怎么回事?
想了半天,最后使用 .NET Framework 2.0 中的 csc.exe 和 ilasm.exe 代替 .NET Framework 4 的,重做以上步骤。
在我的机器上,这两个 exe 文件位于 C:\Windows\Microsoft.NET\Framework\v2.0.50727\ 目录。因为没有找到 .NET Framework 2.0 的 ildasm.exe,就使用 .NET Framework 3.5 的,它位于 C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\ 目录。
这下果然行了。
测试一下效果:
01: using System; 02: using System.Windows; 03: 04: namespace Skyiv.Tester 05: { 06: static class ClipboardTester 07: { 08: [STAThread] 09: static void Main() 10: { 11: var obj = Clipboard.GetDataObject(); 12: foreach (var v in obj.GetFormats()) 13: { 14: Console.WriteLine("[{0}]", v); 15: Console.WriteLine("-------- 开始 --------"); 16: try { Console.WriteLine("{0}", VSPaste.VSPaste.DeleteChineseGarbage(obj.GetData(v).ToString())); } 17: catch (Exception ex) { Console.WriteLine(ex); } 18: Console.WriteLine("-------- 结束 --------"); 19: Console.WriteLine(); 20: } 21: } 22: } 23: }
果然没有乱码了。
另外一个 Paste from Visual Studio 插件
后来,又找到另外一个 Paste from Visual Studio 插件,效果如下:
- using System;
- using System.Windows;
- namespace Skyiv.Tester
- {
- static class ClipboardTester
- {
- [STAThread]
- static void Main()
- {
- var obj = Clipboard.GetDataObject();
- foreach (var v in obj.GetFormats())
- {
- Console.WriteLine("[{0}]", v);
- Console.WriteLine("-------- 开始 --------");
- try { Console.WriteLine("{0}", VSPaste.VSPaste.DeleteChineseGarbage(obj.GetData(v).ToString())); }
- catch (Exception ex) { Console.WriteLine(ex); }
- Console.WriteLine("-------- 结束 --------");
- Console.WriteLine();
- }
- }
- }
- }
该插件使用另外一套方法来把 RTF 格式转换为 Html 格式。我是直接从“让Paste as Visual Studio Code支持Visual Studio 2010粘贴的中文--自己动手丰衣足食”下载现成的 LavernockEnterprises.PasteAsVSCode.dll 文件来使用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述