Visual Studio插件开发
――VB.NET-C# 语言转换插件
我前段时间在《程序员》上看到一篇肖文编译的文章,说的是C#-VB 语言转换的一个Visual Studio2005 插件,当时也没有在意,因为我用的是C# ,一般情况下不会用到C#-VB 的插件。最近在网上找了些源码,比较短的那种,完成某种小功能的,但有的是VB.net 的,要么自己改成c# ,要么去一些转换网站,都觉得不是很方便。于是也有种想法了,何不依样画葫芦,来一个VB-C# 的插件呢,虽然技术含量不多,但也好了解下Visual Studio2005 中的插件开发是怎么回事了。
首先,我肯定不是自己写转换代码,那我做不到,网上基于Web 的代码转换器不少,我们可以随便找几个来用就行了,http://www.carlosag.net/Tools/CodeTranslator/Default.aspx 就是不错的一个,两种语言互转。另外就是这个http://www.kamalpatel.net/ ,其实,在我在《程序员》上看到肖文编译的那篇文章的时候,只有C#-VB 的转换,没有VB-C# 的,我当时就想,你怎么可能只支持一种呢,果不然,今天我上该网站时,它就已经出现了新的功能VB-C# 的,
看到没有,的确是新增的。但是我今天点了该链接,暂时还有点问题,估计很快就会好的了。所以比较遗憾,我没有测试它了。
转换器其实就是使用的上述的网站的转换器。我们可以多找几个这样的网站,这样如果一个网站暂时不可用时,或者你觉得某个网站的转换器给出的转换后的代码不是太好的时候,你就可选择其它的。也正是基于这个原因,我们为了以后能加入新的转换器,所以还是先定义一个接口为妙:
interface IConvertCode
{
string Convert(string vbCode);
string ConvertName
{
get;
}
}
代码1 :接口
Convert 方法把vb.net 的代码看成是字符串,返回的c# 代码也是字符串。ConvertName 属性返回的是转换器的名称,这样就方便选择自己喜欢的转换器。
第一个实现该接口的就是该http://www.kamalpatel.net/ 网站上的,也就是Kamal Patel 的"VB.NET to C# ", 该网站的这个功能是基于web 服务的,那就很简单了,我们就在项目中引用web 服务就OK 了。这里注意的是,因为我上面说了该功能是新增的,我写该文章的时候也用不了,它是基于web 服务的,所以它的web 服务地址我也找不到,所以现在我在代码中用的是它的"C# to VB.NET "的服务,如果什么时候它的"VB.NET to C# "可以用了,就可以很方便简单修改下就可以了。
class KPConvert : IConvertCode
{
public string Convert(string vbCode)
{
ConvertCSharp2VBService convert = new ConvertCSharp2VBService();
return convert.Execute(vbCode);
}
public string ConvertName
{
get
{
return "Kamal Patel's Converter";
}
}
}
代码2 :调用web 服务
看看就明白了,这是把C# 代码转为VB.NET 的,所以到时http://www.kamalpatel.net/ 网站上VB.NET 转C# 的功能正常之后,你就去上面找到对象的web 服务地址,在项目中引用,在改下该类中的相关代码就可以了。
另外一个很著名的工具就是Carlos Aguilar Mares 的基于ajax 的代码转换器,http://www.carlosag.net/Tools/CodeTranslator/Default.aspx 这是它的网站,它早就实现了C# 与VB.NET 之间的互转功能。它这儿,代码是以表单域集合传给carlosag.net/Tools/CodeTranslator/translate.ashx 。代码3 就是它的实现:
class CAConvert : IConvertCode
{
public string Convert(string vbCode)
{
NameValueCollection formFields = new NameValueCollection();
formFields.Add("code", vbCode);
formFields.Add("Language", "VB");
formFields.Add("DestinationLanguage", "C#");
WebClient client = new WebClient();
return Encoding.ASCII.GetString(client.UploadValues("http://www.carlosag.net/Tools/CodeTranslator/translate.ashx", "post", formFields));
}
public string ConvertName
{
get
{
return "Carlos Aguilar's Converter";
}
}
}
代码3 :使用基于表单的转换器
现在就要为Visual Studio 2005 创建插件了。我们可以在解决方案里,添加一个新的插件工程来创建插件,这在扩展(Extensibility )项目里,已经含有预制的模板(注意:我看代码量不多,又比较简单所以我的源码是把转换代码和插件工程混在一起了,应该是把转换的单独建立一个工程,再插件一个工程,这样就比较清楚点)
这个向导让你很轻松就帮你建好了该插件工程。与Visual Studio 2003 不一样,Visual Studio 2005 的插件工程包含可以将插件正确连接到开发环境的基本代码。这个插件类实现了IExtensiblity2 接口,它可以使Visual Studio 与插件进行通信。要注意的一些关键方法是,
Onconnection 和QueryStatus 。
其实你这时就可以按F5 启动项目了,但是插件默认是在"Tools "菜单中的,我要把它放在Edit 菜单的Paste 项之后,这就得改Onconnection 方法了,这个你用默认的文件跟我的这个文件比较后,你就应该明白很多东西了。
对这个方法有些重要的修改之处。首先,这个工程包含了一个含有多种语言的顶级Visual Studio 菜单项,名为CommandBar 的文本文件。下面这行代码就是定位一个双字节母语名称的菜单项。
editMenuName = resourceManager.GetString(resourceName);
如果是设置为U.S.English 的计算机上运行,它会在CommandBar.resx 文件中查找名为enEdit 的源字符串,如果是简体中文的,它就会找zh-CHSEdit 的源字符串。
需要详细的了解修改之处,你必须仔细看下修改后的Onconnection 方法了。
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
if(connectMode == ext_ConnectMode.ext_cm_UISetup)
{
object []contextGUIDS = new object[] { };
Commands2 commands = (Commands2)_applicationObject.Commands;
string editMenuName;
try
{
ResourceManager resourceManager = new ResourceManager("PasteAsCSharp.CommandBar", Assembly.GetExecutingAssembly());
CultureInfo cultureInfo = new System.Globalization.CultureInfo(_applicationObject.LocaleID);
string resourceName = string.Empty;
if (cultureInfo.Name == "zh")
{
resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "-CHSEdit");
}
else
{
resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Edit");
}
editMenuName = resourceManager.GetString(resourceName);
}
catch
{
editMenuName = "Edit";
}
Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"];
CommandBarControl editControl = menuBarCommandBar.Controls[editMenuName];
CommandBarPopup editPopup = (CommandBarPopup)editControl;
try
{
Command command = commands.AddNamedCommand2(_addInInstance, "PasteAsCSharp", "Paste as CSharp", "Executes the command for PasteAsCSharp", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported+(int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
if((command != null) && (editPopup != null))
{
command.AddControl(editPopup.CommandBar, 12);
}
}
catch(System.ArgumentException)
{
}
}
}
代码3 :修改后的Onconnection 方法。
这里请稍微注意一点,
if (cultureInfo.Name == "zh")
{
resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "-CHSEdit");
}
else
{
resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Edit");
}
按理来所,这里根本就不要这么写的,就用else 里面代码就可以了,但是在我前段时间的中文版的Visual Studio 2005 就有问题,因为简体中文是zh-CHS ,这样它加上"Edit "就是正确的了,zh-CHSEdit, 但我的却是"zh ",所以加上"Edit "后,就成了"zhEdit ",这样就定位不到正确的资源了,如果你不知道我在说什么,你看看CommandBar.resx 文件就明白了
下面的代码定位实际的菜单对象:
CommandBarControl editControl = menuBarCommandBar.Controls[editMenuName];
CommandBarPopup editPopup = (CommandBarPopup)editControl;
一旦菜单栏定位好了,新的Paste as CSharp 菜单就能被添加进来。这一步可以通过创建一个Command 对象来实现,然后把Command 添加进菜单就可以了。
这里要注意的是,AddNameCommand2 这个方法的第二参数不能随便改动的,第三个是可以改动,它是显示在菜单项上的文本。
Command command = commands.AddNamedCommand2(_addInInstance, "PasteAsCSharp", "Paste as CSharp", "Executes the command for PasteAsCSharp", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported+(int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
command.AddControl(editPopup.CommandBar, 12);
这样把Command 添加在菜单里位置为12 的地方,恰好在Paste 菜单之后。
还有要保证插件在只有当活动文档的后缀名为.cs 时才是可以使用的。
public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
{
if(neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
{
if(commandName == "PasteAsCSharp.Connect.PasteAsCSharp")
{
if (_applicationObject.ActiveDocument != null && _applicationObject.ActiveDocument.FullName.ToLower().EndsWith(".cs"))
{
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
return;
}
}
}
}
代码5: 插件可用控制
到此为止,我们还需要做的是编写插件的Exec 方法,实际执行代码转换并把结果粘贴到编辑器中。
其实这已经是比较简单的部分了。看下界面就知道个八九不离十了。何况有源码呢,只说下那个"转换后格式化"的ChecBox 控件, 其实也特简单,选中它后,在把代码粘贴入编辑器后,接着执行Edit.FormatDocument 命令格式化文档。就相当于在Visual Studio 2005 中按下"Ctl+E" ,然后再按"D", 应该都用过这个功能吧,平常,我们看到,源文件中的大括号似乎对应的不是很整齐了,我们不就用该功能自动调整下吗。
图3: 转换界面
还是看下比较重要的Exec 方法吧:
public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "PasteAsCSharp.Connect.PasteAsCSharp")
{
ConvertForm f = new ConvertForm();
if (f.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
TextSelection textSelection = (TextSelection)_applicationObject.ActiveDocument.Selection;
textSelection.Insert(f.CSCode, 0);
}
if (f.IfFormat)
{
_applicationObject.ExecuteCommand("Edit.FormatDocument", "");
}
handled = true;
return;
}
}
}
创建了外接程序后,必须先在 Visual Studio 注册此外接程序,才可以在外接程序管理器中激活它。在 Visual Studio 的先前版本中,这是通过使用注册表项实现的,但目前是通过使用 XML 文件实现的。 在创建该插件工程的时候,就自动在
"Documents and Settings"All Users"My Documents"Visual Studio 2005"Addins)
- 或 -
"Documents and Settings"<user name>"My Documents"Visual Studio 2005"Addins)
创建一个.Addin文件,你在工程中可以看到的:
<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility">
<HostApplication>
<Name>Microsoft Visual Studio</Name>
<Version>8.0</Version>
</HostApplication>
<Addin>
<FriendlyName>PasteAsCSharp</FriendlyName>
<Description>Paste As CSharp</Description>
<Assembly>D:"Visual Studio 2005 Projects"PasteAsCS"PasteAsCSharp"bin"PasteAsCSharp.dll</Assembly>
<FullClassName>PasteAsCSharp.Connect</FullClassName>
<LoadBehavior>1</LoadBehavior>
<CommandPreload>1</CommandPreload>
<CommandLineSafe>0</CommandLineSafe>
</Addin>
</Extensibility>
该文件根据你当时创建插件工程时的不同选项,会稍有不同,有了这个文件,部署就基本搞定了。 这种简化的注册方法允许对托管代码插件程序采用 XCopy 式的安装方法。如果将所有文件放入正确的位置,插件程序就可以正常运行。此外,这种方法使用带有注释的 XML 来定义插件程序的注册设置,使信息变得比注册表项更易于理解和编辑。( 如果想了解更多,请查看msnd ,你装的是中文版的msdn 的话,就查"外接程序注册"等相关内容,msdn 中把插件程序称之为"外接程序" )
Visual Studio 2005 创建插件非常简单(我也是比较菜的,但也没有碰到什么大问题,由此可见,的确不难哦),首先,它会生成将插件置于Visual Studio 菜单中的基本代码。然后,通过F5 键,包含插件的Visual Studio 测试用例就可以运行。是就调试就比较简单了。
我根据前人写的这个插件是否有用,本人也不太清楚,但对我还是有点用的。我的写的代码扩展性也许不是太好,这就需要各位字节改进了,我只是抛砖引玉了 。
TrackBack:http://sifang2004.cnblogs.com/archive/2006/06/09/421723.html