如何擴展Visual Studio 的編譯時功能
很多時候會想在vs編譯的時候自定義一些事情或者動作,
例如:
拷貝生成的文件到特定的目錄。
部署程序到測試目錄或者環境,例如注冊到windows服務,更新GAC等。
根據編譯環境生成特定的配置文件(例如web.config) PS: 身在一個復雜環境, 這是我最想要的功能。
自動執行外部exe。
同步DLL和其他資源文件。
1.最簡單的自然是用Visual Studio自帶的編譯事件,這東西使用方便,又是Visual Studio自帶的功能,就是功能弱了一點(好吧 其實是很弱)
將項目生成的DLL文件拷貝到特定目錄,(如果你想拷貝一整個文件夾 用xcopy; 當然,熟悉命令行的人可以弄出更多的玩法)
如下圖所示

2.另外一種比較推薦的方式是自定義編譯擴展(可以執行C#代碼...功能強大多了), 看下面這個項目文件的最后幾句 (項目文件就是 項目名.csproj)
<Import Project="..\..\Build\Tasks\Build.tasks" />
<Target Name="BeforeBuild">
<Message Text="Start Automatic Generate Configuration File, Source File: $(ProjectDir)web.config" Importance="high">
</Message>
<ConfigurationEnvironmentTask TemplateFile="$(ProjectDir)web.template.config" TargetFile="$(ProjectDir)web.config" DataSource="$(EnvironmentName).config" />
</Target>
</Project>
這幾句話的意思是
1. 包含一個task文件 (該文件包含了ConfigurationEnvironmentTask 的定義,這是一個自定開發的類,其主要作用是根據環境生成web.config文件)
2.輸出一行提示信息 Start Automatic..... (該信息將顯示在Output Window)
3. 調用ConfigurationEnvironmentTask 并傳入一些參數(TemplateFile 等都是自己定義的參數)
Build.tasks的文件內容其實很簡單,主要就是說明了這個task定義在哪里 (Build.dll)
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask AssemblyFile="Build.dll" TaskName="Build.ConfigurationEnvironmentTask"/>
</Project>
接下來的重點就是如何實現這個Task了,以下是Build.dll的關鍵代碼
View Code
namespace Build
{
#region namespace
using System.Globalization;
using System.IO;
using System.Xml;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
#endregion
#region Standard Config File Sections
/// <summary>
/// this class generate config / xml file by template
/// </summary>
public class ConfigurationEnvironmentTask : Task
{
#region Public Propreties
/// <summary>
/// Gets or Sets template file path (full path : C:\web.config)
/// </summary>
[Required]
public ITaskItem TemplateFile { get; set; }
/// <summary>
/// Gets or Sets traget file path, will automatic overwrite this file (full path : C:\web.config)
/// </summary>
[Required]
public ITaskItem TargetFile { get; set; }
/// <summary>
/// Gets or Sets configuration file, will get settings from this path (full path : C:\web.config , relative path: dev.config)
/// </summary>
[Required]
public ITaskItem DataSource { get; set; }
#endregion
/// <summary>
/// execute replace logic
/// </summary>
/// <returns>ture successful, false failure</returns>
public override bool Execute()
{
string templateContent = File.ReadAllText(this.TemplateFile.ToString());
XmlDocument xmldoc = new XmlDocument();
string fileName = null;
string fileFolder = null;
if (Path.IsPathRooted(this.DataSource.ToString()))
{
fileFolder = Path.GetDirectoryName(this.DataSource.ToString());
}
else
{
fileFolder = Path.GetDirectoryName(this.TargetFile.ToString());
}
fileName = Path.GetFileName(this.DataSource.ToString());
if (fileName == null || string.IsNullOrEmpty(fileName) || fileName.ToLower(CultureInfo.CurrentCulture).Trim() == ".config")
{
fileName = @"local.config";
}
this.Log.LogMessage("DataSource:" + this.DataSource.ToString());
this.Log.LogMessage("IsPathRooted:" + Path.IsPathRooted(this.DataSource.ToString()));
this.Log.LogMessage("fileFolder:" + fileFolder);
this.Log.LogMessage("fileName:" + fileName);
if (!File.Exists(fileFolder + @"\" + fileName))
{
throw new FileNotFoundException("Can not find File :" + fileFolder + @"\" + fileName);
}
xmldoc.Load(fileFolder + @"\" + fileName);
foreach (XmlNode node in xmldoc.SelectNodes("/Settings/*"))
{
templateContent = templateContent.Replace("$(" + node.Name + ")", node.InnerText).Trim(new char[] { ' ', '\r', '\n', '\t' });
}
File.WriteAllText(this.TargetFile.ToString(), templateContent);
return true;
}
}
#endregion Standard Config File Sections
}
代碼寫得很爛,這不是生產環境的code
關鍵就是Execute這個方法,
大意就是根據指定的config文件讀取出鍵值對,將web.config中類似于 $(key)的內容替換掉
PS:在正式環境中,其實我們是用放在一個網站上的一個文件和環境變量實現的
當然在這個方法中你想干嘛干嘛,修改配置文件也好,發郵件也好,自動備份也好...
對了 記得讓你的項目引用Build.dll (推薦dll和task文件放在同一個目錄)
然后點擊編譯或者Ctrl+Shift+B吧
本文中的所有代碼在Visual Studio 2010和 MSBuild Extenstion 4.0 編譯和運行成功
此外MS Build Exenstion中的一些功能很不錯,而且網上也有很多第三方的Task都挺不錯的, 例如部署IIS網站,FTP等,有興趣可以試試,也許能簡化不少工作

浙公網安備 33010602011771號