Visual Studio Add-In 擴展VS 簡易CodeReview插件


最近裝上了VS11,發現VS11 RC中找不到宏管理器了,之前用宏命令寫的CodeReview插件也沒有辦法使用了。索性寫下Add-In版本的

新建VS外接程序項目,之后向導中的選項都可以不選

完成之后編輯Connect.cs文件

using System;

using Extensibility;

using EnvDTE;

using EnvDTE80;

using Microsoft.VisualStudio.CommandBars;

using System.Resources;

using System.Reflection;

using System.Globalization;

 

namespace CodeReview

{

/// <summary>用於實現外接程序的對象。</summary>

/// <seealso class='IDTExtensibility2' />

public class Connect : IDTExtensibility2, IDTCommandTarget

{

private DTE2 _applicationObject;

private AddIn _addInInstance;

CommandBar stdCmdBar;

CommandBarControl stdCmdBarCtl;

 

/// <summary>實現外接程序對象的構造函數。請將您的初始化代碼置於此方法內。</summary>

public Connect()

{

}

 

/// <summary>實現 IDTExtensibility2 接口的 OnConnection 方法。接收正在加載外接程序的通知。</summary>

/// <param term='application'>宿主應用程序的根對象。</param>

/// <param term='connectMode'>描述外接程序的加載方式。</param>

/// <param term='addInInst'>表示此外接程序的對象。</param>

/// <seealso class='IDTExtensibility2' />

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_AfterStartup || connectMode == ext_ConnectMode.ext_cm_Startup)

{

object[] contextGUIDS = new object[] { };

Commands2 commands = (Commands2)_applicationObject.Commands;

//string toolsMenuName = "Tools";

 

//將此命令置於"工具"菜單上。

//查找 MenuBar 命令欄,該命令欄是容納所有主菜單項的頂級命令欄:

//Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"];

 

//在 MenuBar 命令欄上查找"工具"命令欄:

//CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];

//CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;

 

//如果希望添加多個由您的外接程序處理的命令,可以重復此 try/catch 塊,

// 只需確保更新 QueryStatus/Exec 方法,使其包含新的命令名。

try

{

//將一個命令添加到 Commands 集合:

Command command = commands.AddNamedCommand2(_addInInstance, "CodeReview", "CodeReview", "Simple CodeReview for VS2012", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);

 

//將對應於該命令的控件添加到"工具"菜單:

//if ((command != null) && (toolsPopup != null))

//{

// command.AddControl(toolsPopup.CommandBar, 1);

//}

//添加命令集合

stdCmdBar = commands.AddCommandBar("CodeReview VS11", vsCommandBarType.vsCommandBarTypeToolbar, null, 1) as CommandBar;

//添加命令button

stdCmdBarCtl = command.AddControl(stdCmdBar, stdCmdBar.Controls.Count + 1) as Microsoft.VisualStudio.CommandBars.CommandBarControl;

stdCmdBarCtl.Caption = "Click for CodeReview";

 

//按扭可用

stdCmdBarCtl.Enabled = true;

 

}

catch (Exception ex)

{

//如果出現此異常,原因很可能是由於具有該名稱的命令

// 已存在。如果確實如此,則無需重新創建此命令,並且

// 可以放心忽略此異常。

}

}

}

 

private void OnTextLineChange(TextPoint start,TextPoint end,int hint)

{

System.Windows.Forms.MessageBox.Show("selection");

}

 

/// <summary>實現 IDTExtensibility2 接口的 OnDisconnection 方法。接收正在卸載外接程序的通知。</summary>

/// <param term='disconnectMode'>描述外接程序的卸載方式。</param>

/// <param term='custom'>特定於宿主應用程序的參數數組。</param>

/// <seealso class='IDTExtensibility2' />

public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)

{

if (stdCmdBarCtl != null)

stdCmdBarCtl.Delete();

if (null != stdCmdBar)

stdCmdBar.Delete();

 

}

 

/// <summary>實現 IDTExtensibility2 接口的 OnAddInsUpdate 方法。當外接程序集合已發生更改時接收通知。</summary>

/// <param term='custom'>特定於宿主應用程序的參數數組。</param>

/// <seealso class='IDTExtensibility2' />        

public void OnAddInsUpdate(ref Array custom)

{

}

 

/// <summary>實現 IDTExtensibility2 接口的 OnStartupComplete 方法。接收宿主應用程序已完成加載的通知。</summary>

/// <param term='custom'>特定於宿主應用程序的參數數組。</param>

/// <seealso class='IDTExtensibility2' />

public void OnStartupComplete(ref Array custom)

{

}

 

/// <summary>實現 IDTExtensibility2 接口的 OnBeginShutdown 方法。接收正在卸載宿主應用程序的通知。</summary>

/// <param term='custom'>特定於宿主應用程序的參數數組。</param>

/// <seealso class='IDTExtensibility2' />

public void OnBeginShutdown(ref Array custom)

{

}

 

/// <summary>實現 IDTCommandTarget 接口的 QueryStatus 方法。此方法在更新該命令的可用性時調用</summary>

/// <param term='commandName'>要確定其狀態的命令的名稱。</param>

/// <param term='neededText'>該命令所需的文本。</param>

/// <param term='status'>該命令在用戶界面中的狀態。</param>

/// <param term='commandText'>neededText 參數所要求的文本。</param>

/// <seealso class='Exec' />

public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)

{

if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)

{

if (commandName == "CodeReview.Connect.CodeReview")

{

status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;

return;

}

}

}

 

/// <summary>實現 IDTCommandTarget 接口的 Exec 方法。此方法在調用該命令時調用。</summary>

/// <param term='commandName'>要執行的命令的名稱。</param>

/// <param term='executeOption'>描述該命令應如何運行。</param>

/// <param term='varIn'>從調用方傳遞到命令處理程序的參數。</param>

/// <param term='varOut'>從命令處理程序傳遞到調用方的參數。</param>

/// <param term='handled'>通知調用方此命令是否已被處理。</param>

/// <seealso class='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 == "CodeReview.Connect.CodeReview")

{

handled = true;

CodeReviewForm form = new CodeReviewForm();

InitForm(ref form);

form.Show();

return;

}

}

}

 

private void InitForm(ref CodeReviewForm form)

{

form.FunctionName = GetFunctionName();

EnvDTE.TextSelection txtSelection = _applicationObject.ActiveDocument.Selection as EnvDTE.TextSelection;

form.OriginCode = txtSelection.Text;

form.CodeLineNumber = txtSelection.TopPoint.Line.ToString();

 

string projectName = "";

if (null != _applicationObject.ActiveWindow || null != _applicationObject.ActiveWindow.Project)

{

projectName = _applicationObject.ActiveWindow.Project.Name;

}

 

form.FileFullName = _applicationObject.ActiveDocument.FullName;

}

 

private string GetFunctionName()

{

 

 

ProjectItem projectItem = _applicationObject.ActiveDocument.ProjectItem;

FileCodeModel fileCodeModel = projectItem.FileCodeModel;

if (fileCodeModel.Language == CodeModelLanguageConstants.vsCMLanguageCSharp)

{

EnvDTE.TextSelection txtSelection = _applicationObject.ActiveDocument.Selection as EnvDTE.TextSelection;

CodeElement codeEmelemt = null;

try

{

codeEmelemt = fileCodeModel.CodeElementFromPoint(txtSelection.TopPoint, vsCMElement.vsCMElementFunction);

}

catch { }

if (null == codeEmelemt)

return "";

else

return codeEmelemt.Name;

}

return "";

}

}

}

 

Connect類中的幾個方法是IDTExtensibility2, IDTCommandTarget接口的實現,執行順序可以參見MSDNhttp://msdn.microsoft.com/zh-cn/library/vstudio/ms228754.aspx

在項目中添加一個Windows Form資源,如下:

編輯Form的CS代碼

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Forms;

 

namespace CodeReview

{

public partial class CodeReviewForm : Form

{

public CodeReviewForm()

{

InitializeComponent();

}

 

//添加可訪問性

#region

public string FunctionName

{

get { return this.txtFuncName.Text; }

set { this.txtFuncName.Text = value; }

}

 

public string Problem

{

get { return this.txtProblem.Text; }

set { this.txtProblem.Text = value; }

}

 

public string OriginCode

{

get { return this.txtOriginCode.Text; }

set { this.txtOriginCode.Text = value; }

}

 

public string Suggest

{

get { return this.txtSuggest.Text; }

set { this.txtSuggest.Text = value; }

}

 

public string Responsible

{

get { return this.txtResonsible.Text; }

set { this.txtResonsible.Text = value; }

}

 

public string FixTime

{

get { return this.txtFinishTime.Text; }

set { this.txtFinishTime.Text = value; }

}

 

/// <summary>

/// 行號

/// </summary>

public string CodeLineNumber

{ get; set; }

/// <summary>

/// 項目名稱

/// </summary>

public string ProjectName

{ get; set; }

 

/// <summary>

/// 文件全名

/// </summary>

public string FileFullName

{

get;

set;

}

 

private string savePath = @"D:\\CodeReview\";

public string SavePath

{

get {

if (!System.IO.Directory.Exists(savePath))

{

System.IO.Directory.CreateDirectory(savePath);

}

return savePath;

}

}

 

#endregion

private void btnCancel_Click(object sender, EventArgs e)

{

this.Close();

this.Dispose();

}

 

private void btnSave_Click(object sender, EventArgs e)

{

string content = GetRecordContent();

string path = GetSaveFileName();

using (System.IO.StreamWriter sw = new System.IO.StreamWriter(path, true))

{

sw.Write(content);

sw.Close();

}

this.Close();

this.Dispose();

}

 

private string GetSaveFileName()

{

string fileName = "CodeReview"+DateTime.Now.ToString("yyyy-MM-dd")+".txt";

string savePath = SavePath + fileName;

if(!System.IO.File.Exists(savePath))

{

using (System.IO.FileStream fs = System.IO.File.Create(savePath))

{

fs.Close();

}

}

return savePath;

}

 

private string GetRecordContent()

{

StringBuilder sb = new StringBuilder();

sb.Append("[時間]\t" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));

sb.Append(System.Environment.NewLine);

sb.Append("[組件]\t" + this.ProjectName);

sb.Append(System.Environment.NewLine);

sb.Append("[文件]\t" + this.FileFullName);

sb.Append(System.Environment.NewLine);

sb.Append("[行號]\t" + this.CodeLineNumber);

sb.Append(System.Environment.NewLine);

sb.Append("[方法]\t" + this.FunctionName);

sb.Append(System.Environment.NewLine);

sb.Append("[代碼]\t" + this.OriginCode);

sb.Append(System.Environment.NewLine);

sb.Append("[問題]\t" + this.txtProblem.Text);

sb.Append(System.Environment.NewLine);

sb.Append("[建議]\t" + this.txtSuggest.Text);

sb.Append(System.Environment.NewLine);

sb.Append("[修改人]\t" +this.txtResonsible.Text);

sb.Append("\t" + "[計划完成時間]\t" + this.FixTime);

sb.Append(System.Environment.NewLine);

sb.Append("-----------------------------------------------------------------------------------------");

sb.Append(System.Environment.NewLine);

sb.Append(System.Environment.NewLine);

 

return sb.ToString();

}

}

 

}

 

啟動VS調試,在高度的VS工具菜單中找到外接程序管理器,打開后勾選確定。如果想下次自動啟用,勾選啟用列

確定之后,在命令欄中會顯示CodeReveiw按扭

選中代碼后點擊按扭會彈出Form,點擊確定后會將內容保存到d:\CodeReview目錄下。

這只是個原型代碼,可以做很多優化和擴展,如:可以將CodeReview內容定為一個契約,可以將保存方法抽象為一個接口,再實現向數據庫或WebServices等保存內容。保存方式可以用WinFrom選擇,如果是本地文件,再加上一個路徑選Dialog。

 

我嘗試着響應VS EvnDET事件來更改CodeReview命令按扭的可用性,當編輯文檔中選中文本內容按扭可用,當選中內容為空時按扭不可用,但在MSDN中未找到有該事件,在MSDN的論壇中得知微軟未提供此事件。要實現這個功能可能要捕獲mouseup事件,用hook來實現了。另外,如果要應用 EnvDET的事件,需要將事件提升為Connect的變量,在方法中給變量賦值,否則GC會回收event,無法響應事件。

 

最近很迷茫……


免責聲明!

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



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