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 Studio2013\VSSDK\VisualStudioIntegration\Common\Inc\vsshlids.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:\Windows\Microsoft.NET\Framework\v4.0.30319 這個路徑下,值得注意的是Framework 的位數和版本,這個影響選擇MSBuild.exe的路徑。

  嘗試一下,打開cmd,輸入-> C:\Windows\Microsoft.NET\Framework\v4.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:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.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, @"bin\Release");
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項目的發布,文件過濾復制等等。。。。

  本篇文章有什么寫錯的或者更好的建議麻煩大家在評論寫給我,我會一一補充修改。如果對大家有幫助,還希望推薦一下,謝謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM