• Visual Studio Package 插件开发(Visual Studio SDK)


    背景

      这段时间公司新做了一个支付系统,里面有N个后台服务,每次有更新修改,拷贝打包发布包“不亦乐乎”。。。于是我想要不要自己定制个打包插件。

      部分朋友可能会认为,有现成的可以去找一个,干嘛不用持续集成工具啊。

      1、公司没用,也不打算用

      2、自己想折腾下,好奇

      因此主要是分享这次的学习过程和结果。

    涉及知识点

      大概构想下,选择需要打包的项目-重新编译-拷贝生成文件到指定目录-OK,剩下就是交给测试去做了。

      1、Visual Studio Package 初步使用

      2、.Net 执行Dos命令

      3、MSBuild简单运用

      4、File.Copy的使用

    下载安装

      Visual Studio 2013 SDK包的下载地址:http://www.microsoft.com/en-us/download/details.aspx?id=40758&fa43d42b-25b5-4a42-fe9b-1634f450f5ee=True

      完毕后,在新建项目-C#-扩展性,可以见到Visual Studio Package模板选项(见下图)

      

    第一个vs插件程序

      下一步,勾选Menu Command;下一步,修改Command Name;下一步,取消单元测试,Finish。恭喜,您的第一个插件程序就这么诞生了。

      编译后,到生成目录下,执行.vsix文件,安装完后,重启VS,点击[工具]就会见到您的插件工具。同时你也可以在扩展与更新界面进行对您的插件进行卸载。

     

    项目文件简介

      初建项目,有很多文件,有几个下面是需要了解一下的。

      1、PackingTools.vsct

      2、PackingToolsPackage.cs

      3、source.extension.vsixmanifest

      PackingTools.vsct

         vsct是个Xml文件,它用来对插件按钮进行配置的。

         Group是组节点,项目初建,它的Parent为IDM_VS_MENU_TOOLS,对于这个我们可以查看  您的vs安装目录Visual Studio2013VSSDKVisualStudioIntegrationCommonIncvsshlids.h,打开文件,我们可以看到除IDM_VS_MENU_TOOLS以外还有IDM_VS_MENU_ADDINS、IDM_VS_MENU_HELP等等,我们尝试改成IDM_VS_MENU_HELP。

          Buttons节点下,可以添加多个Button信息,我们尝试添加一个Button进去,同时GuidSymbol加多一个IDSymbol进去。修改对应新按钮的id 和 priority。

          

          我们启动F5,调试看看,插件按钮位置变了,也是我们第一次修改了按钮布局。

          

      PackingToolsPackage.cs

          插件程序的入口,我们细看代码,发现在Initialize方法里,就对批量打包这个按钮进行事件绑定,我们尝试一下,把MenuItemCallback里的逻辑删了,展示一个wpf窗体出来。

          添加新建项-添加wpf窗体后,需要在项目里新引用一个System.Xaml.dll才能编译通过。

          接着,我们在PackingToolsPackage.cs的MenuItemCallback的方法里写new MainWindow().Show(),F5运行->点击批量打包,则会弹出下图。

          

          主界面就这么出来了,当然,你可以单例一个窗体,不用new。。。 

      source.extension.vsixmanifest

          对Visual Studio 扩展的配置,如:文件模板,项目工程模板,依赖程序集,Visual Studio 工具包logo,VS安装的版本等等。

    读取已打开的项目信息

      这个其实并没什么特别难的逻辑,只是开始找资料花了一些时间,直接上代码。

     1 private void MenuItemCallback(object sender, EventArgs e)
     2         {
     3             IVsSolution pSolution = GetService(typeof(SVsSolution)) as IVsSolution;
     4 
     5             if (pSolution != null)
     6             {
     7                 var projectArray = new string[10];
     8                 uint aa;
     9                 //通过这个方法取到打开解决方案的项目信息
    10                 pSolution.GetProjectFilesInSolution((uint)__VSGETPROJFILESFLAGS.GPFF_SKIPUNLOADEDPROJECTS,
    11                     (uint)__VSGETPROJFILESFLAGS.GPFF_SKIPUNLOADEDPROJECTS, projectArray, out aa);
    12 
    13                 //转换信息,让girdView展示
    14                 var projectList =
    15                     projectArray.ToList()
    16                         .Where(a => a.Contains(".csproj")).Select(a => new ProjectList
    17                         {
    18                             Name = Path.GetFileNameWithoutExtension(a),
    19                             Path = a
    20                         }).ToList();
    21 
    22                 MainForm.GetInstance(projectList).Show();
    23             }
    24         }
    View Code

      对于IVsSolution这个接口的命名空间下,还有各种各样的类,对Visual Studio Package开发有兴趣的同学可以去看看。传送门

    MSBuild的使用

      Microsoft Build Engine 是MSBuild的全称,是一个独立的存在生成平台,不需要依赖vs,但是vs的生成、发布等等功能都是基于MSBuild去构建的,它能读取.sln、.csproj、.pubxml等xml文件里的参数进行生成解决和项目。这里就不做过多的介绍和深入,有需要可以点击这里进行查看文档

      MSBuild.exe在C:WindowsMicrosoft.NETFrameworkv4.0.30319 这个路径下,值得注意的是Framework 的位数和版本,这个影响选择MSBuild.exe的路径。

      尝试一下,打开cmd,输入-> C:WindowsMicrosoft.NETFrameworkv4.0.30319 "您的项目文件路径" /t:Rebuild /p:Configuration=Release /p:VisualStudioVersion=12.0

      

      这句话指,用msbuild重新生成Release版本,注意项目路径是有双引号的。

      然而,我们需要在.net程序里使用这段dos指令,因此我们写编写一个dos指令帮助类

     1 #region Dos指令帮助类
     2     /// <summary>
     3     /// Dos指令帮助类
     4     /// </summary>
     5     public class DosCommanHelper
     6     {
     7         #region 执行指令
     8         /// <summary>
     9         /// 执行指令
    10         /// </summary>
    11         /// <param name="command">指令</param>
    12         /// <param name="seconds">最长等待时间(秒)</param>
    13         /// <returns></returns>
    14         public static string ExeCommand(string command, int seconds = 3)
    15         {
    16             var output = "";
    17             if (string.IsNullOrWhiteSpace(command))
    18                 return output;
    19 
    20             var process = new Process(); //创建进程对象
    21             var startInfo = new ProcessStartInfo
    22             {
    23                 FileName = "cmd.exe", //设定需要执行的命令
    24                 Arguments = "/C " + command, //“/C”表示执行完命令后马上退出
    25                 UseShellExecute = false, //不使用系统外壳程序启动
    26                 RedirectStandardInput = false, //不重定向输入
    27                 RedirectStandardOutput = true, //重定向输出
    28                 CreateNoWindow = true //不创建窗口
    29             };
    30 
    31             process.StartInfo = startInfo;
    32             try
    33             {
    34                 if (process.Start())
    35                 {
    36                     if (seconds == 0)
    37                     {
    38                         process.WaitForExit();
    39                     }
    40                     else
    41                     {
    42                         process.WaitForExit(seconds * 1000);
    43                     }
    44                     output = process.StandardOutput.ReadToEnd();
    45                 }
    46             }
    47             catch (Exception e)
    48             {
    49                 Console.WriteLine(e);
    50             }
    51             finally
    52             {
    53                 process.Close();
    54             }
    55             return output;
    56         }
    57         #endregion
    58     }
    59     #endregion
    View Code

    拷贝生成文件

      从上面我们已经读取到了解决方案对应的项目信息,包括路径,新建的项目默认生成到.csproj文件目录下的bin/Release里。

      我们利用Path.GetDirectoryName和Path.Combine方法,获取对应路径,再自己编写文件操作帮助类,对应Release里的文件复制到指定位置。

     1 #region 文件操作帮助类
     2     /// <summary>
     3     /// 文件操作帮助类
     4     /// </summary>
     5     public static class FilesHelper
     6     {
     7         #region 复制文件
     8         /// <summary>
     9         /// 复制文件
    10         /// </summary>
    11         /// <param name="fromPath">原路径</param>
    12         /// <param name="toPath">新路径</param>
    13         /// <returns></returns>
    14         public static bool CopyFiles(string fromPath, string toPath)
    15         {
    16             try
    17             {
    18                 if (!Directory.Exists(fromPath))
    19                     throw new Exception("no fromPath");
    20 
    21                 if (Directory.Exists(toPath))
    22                     Directory.Delete(toPath, true);
    23 
    24                 Directory.CreateDirectory(toPath);
    25 
    26                 var fromfiles = Directory.GetFiles(fromPath).ToList();
    27                 fromfiles.ForEach(file =>
    28                 {
    29                     var newFile = Path.Combine(new[] { toPath, Path.GetFileName(file) });
    30                     File.Copy(file, newFile, true);
    31                 });
    32 
    33                 var fromFolders = Directory.GetDirectories(fromPath).ToList();
    34                 fromFolders.ForEach(folder =>
    35                 {
    36                     var destDir = Path.Combine(new[] { toPath, Path.GetFileName(folder) });
    37                     CopyFiles(folder, destDir);
    38                 });
    39 
    40                 return true;
    41             }
    42             catch
    43             {
    44                 return false;
    45             }
    46         }
    47         #endregion
    48     }
    49     #endregion
    View Code

      最后我们只需要完善发布按钮事件,获取列表选择项->获取打包到的指定路径->遍历列表项数据->执行MSBuild指令->复制文件到指定路径->完毕

     1  private void Button_Click_1(object sender, RoutedEventArgs e)
     2         {
     3             var list = MyListView.SelectedItems.Cast<ProjectList>().ToList();
     4 
     5             var fromPath = PathLabel.Content;
     6 
     7             list.ForEach(item =>
     8             {
     9                 DosCommanHelper.ExeCommand(string.Format(@"C:WindowsMicrosoft.NETFrameworkv4.0.30319MSBuild.exe {0} /t:Rebuild /p:Configuration=Release /p:VisualStudioVersion=12.0", item.Path));
    10 
    11                 var theFileOfDirectoryName = Path.GetDirectoryName(item.Path);
    12                 var toPath = Path.Combine(theFileOfDirectoryName, @"binRelease");
    13                 FilesHelper.CopyFiles(fromPath.ToString(), Path.Combine(toPath, item.Name));
    14             });
    15         }
    View Code

    完毕

      源码我这里没有提供,还是希望读了这篇文章感兴趣的小伙伴动手折腾下,Visual Studio Package还可以做模版开发等等,我也没太多的去深入了解,感兴趣的可以去google一下关键字Visual Studio Package、vssdk、vsix、插件开发。

      以上纯属自己初步折腾的结果,为了写文章弄出来的简单demo,还有很多可优化的地方,例如各种验证判断,插件按钮的动态显示、读取项目的类型过滤、web项目的发布,文件过滤复制等等。。。。

      本篇文章有什么写错的或者更好的建议麻烦大家在评论写给我,我会一一补充修改。如果对大家有帮助,还希望推荐一下,谢谢。

  • 相关阅读:
    ArcGIS Server 9.3 JavaScript API实战一个具体的小系统示例介绍
    感冒太难受了
    ACM考试的题目,收藏,有空做做
    用Java在Dos下,用纯字符画图
    考ACM需要的知识(转)
    换个风格,换种心情
    (C#)ACM考试题的第一题
    .net连Access的OleDb数据库连接字符串写法
    (C#)ACM考试题的第二题
    辗转相除
  • 原文地址:https://www.cnblogs.com/skychen1218/p/6044864.html
Copyright © 2020-2023  润新知