[C#]取得VSS的檔案狀態
我們家是用VSS做版控工具
之前有需求要取得程式在程式庫的狀態
先說明一下整個換版及出入庫流程
由於AP主機和程式庫主機是分開的,所以採用web service方式去取得程式庫的資訊
實作如下 :
STEP 1 引用DLL(Interop.SourceSafeTypeLib.dll)
小弟是拿下列範例中的DLL檔案
An application to fetch the release sources from Visual SouceSafe based on an Excel migration plan
STEP 2 建立物件檔
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using SourceSafeTypeLib;
using System.IO;
namespace VssUtil
{
public class VssReader
{
[Flags]
//出入庫狀態
public enum CheckStatusEnum
{
不存在 = -1,
已簽入 = 0,
已簽出By他人 = 1,
已簽出By登入帳號 = 2
};
VSSDatabase vssdb = new VSSDatabase();
DateTime? notAfter = null;
public VssReader(string ssdir, string ssuser, string sspwd)
{
vssdb.Open(ssdir, ssuser, sspwd);
}
#region 顯示資料
/// <summary>
/// 取得專案檔案結構XML,傳入saveFolder參數可將最後版本檔案存入指定目錄
/// 傳入notAfter則可選取某個時間之前簽入的檔案
/// </summary>
/// <param name="path"></param>
/// <param name="saveFolder"></param>
/// <param name="notAfter"></param>
/// <returns></returns>
public XDocument ReadProject(string path,
string saveFolder = null, DateTime? notAfter = null)
{
this.notAfter = notAfter;
XDocument xd = XDocument.Parse("<P />");
Explore(path, xd.Root, saveFolder);
return xd;
}
/// <summary>
/// 以遞迴方式取得專案結構的所有檔案,若傳入savePath則會一併取得檔案寫入
/// </summary>
/// <param name="path"></param>
/// <param name="container"></param>
/// <param name="savePath"></param>
private void Explore(string path, XElement container, string savePath = null)
{
VSSItem proj = vssdb.get_VSSItem(path, false);
//在XML建立目錄元素
XElement dir = new XElement("D",
new XAttribute("P", proj.Spec), new XAttribute("N", proj.Name));
if (!string.IsNullOrEmpty(savePath))
{
//若有指定儲存位置,計算路徑並確保資料夾已建立
savePath = Path.Combine(savePath, proj.Name);
if (!Directory.Exists(savePath))
Directory.CreateDirectory(savePath);
}
foreach (VSSItem item in proj.get_Items(false))
{
//若為VSSITEM_PROJECT,等同目錄,要再展開其下的子項目
if (item.Type == (int)VSSItemType.VSSITEM_PROJECT)
Explore(item.Spec, dir, savePath);
else
{
//在XML建立檔案項目元素
XElement file = new XElement("F",
new XAttribute("P", item.Spec),
new XAttribute("N", item.Name),
new XAttribute("C", item.IsCheckedOut));
bool saved = false;
//取得該項目的所有版本資訊
foreach (VSSVersion v in item.Versions)
{
//若有指定簽入時間限制,忽略晚於限制時間的版本
if (notAfter != null & v.Date.CompareTo(notAfter) > 0)
continue;
//在檔案項目元素下新增版本元素
file.Add(new XElement("V",
new XAttribute("N", //若為標籤,則在前方加上l當作版號
string.IsNullOrEmpty(v.Label) ?
v.VersionNumber.ToString() : "l" + v.Label),
new XAttribute("U", v.Username),
new XAttribute("T",
v.Date.ToString("yyyy/MM/dd HH:mm:ss"))
));
//存入第一個吻合的版本(忽略標籤版本)
if (!string.IsNullOrEmpty(savePath) &&
string.IsNullOrEmpty(v.Label) && !saved)
{
string filePath = Path.Combine(savePath, item.Name);
v.VSSItem.Get(ref filePath, 0);
saved = true;
}
//若有簽入時間限制,則只包含吻合的第一筆
if (notAfter != null) break;
}
dir.Add(file);
}
}
container.Add(dir);
}
/// <summary>
/// 取得檔案清單
/// </summary>
/// <param name="path"></param>
/// <param name="saveFolder"></param>
/// <param name="notAfter"></param>
/// <returns></returns>
public string GetFileList(string path,
string saveFolder = null, DateTime? notAfter = null)
{
StringBuilder sb = new StringBuilder();
XDocument doc = ReadProject(path, saveFolder, notAfter);
int result = -1;
foreach (XElement element in doc.Descendants("F"))
{
result = int.Parse(element.Attribute("C").Value);
sb.AppendLine(element.Attribute("P").Value + ";" + ((CheckStatusEnum)result).ToString());
}
return sb.ToString();
}
/// <summary>
/// 檢查程式庫中檔案的狀態
/// </summary>
/// <param name="path">程式庫檔案</param>
/// <returns></returns>
public string GetFileStatus(string path)
{
try
{
VSSItem item = vssdb.get_VSSItem(path, false);
return ((CheckStatusEnum)item.IsCheckedOut).ToString();
}
catch (System.Runtime.InteropServices.COMException cmo)
{
return CheckStatusEnum.不存在.ToString();
}
}
/// <summary>
/// 取得目錄及其子目錄名稱
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public List<string> GetFolderName(string path)
{
List<string> folders = new List<string>();
XDocument doc = ReadProject(path);
foreach (XElement element in doc.Descendants("D"))
{
folders.Add(element.Attribute("P").Value);
}
return folders;
}
#endregion
#region 程式庫物件(供web services使用)
/// <summary>
/// 取得程式庫物件
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public VsItem GetLibraryItem(string path)
{
VsItem vsitem = new VsItem();
vsitem.fullpath = string.Copy(path);
vsitem.status = GetFileStatus(path);
return vsitem;
}
/// <summary>
/// 取得程式庫物件陣列
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public List<VsItem> GetLibraryItemList(List<string> path)
{
List<VsItem> libraryList = new List<VsItem>();
foreach (string pathitem in path)
{
VsItem vsitem = new VsItem();
vsitem.fullpath = string.Copy(pathitem);
vsitem.status = GetFileStatus(pathitem);
libraryList.Add(vsitem);
}
return libraryList;
}
#endregion
#region 功能操作
/// <summary>
/// 從程式庫取得檔案
/// </summary>
/// <param name="localPathfolder"></param>
/// <param name="vssPath"></param>
/// <returns></returns>
public bool GetFileFromVSS(string localPathfolder, string vssPath)
{
try
{
if (Directory.Exists(localPathfolder) == false)
{
Directory.CreateDirectory(localPathfolder);
}
VSSItem item = vssdb.get_VSSItem(vssPath, false);
string filePath = Path.Combine(localPathfolder, item.Spec).Replace("\\$/", "\\").Replace("/", "\\");
item.Get(ref filePath, 0);
return true;
}
catch (Exception e)
{
throw e;
}
}
/// <summary>
/// 簽出
/// </summary>
/// <param name="localPath"></param>
/// <param name="vssPath"></param>
/// <returns></returns>
public bool CheckOut(string comment, string localPath, string vssPath)
{
try
{
VSSItem item = vssdb.get_VSSItem(vssPath, false);
item.Checkout(comment, localPath);
return true;
}
catch (Exception e)
{
throw e;
}
}
/// <summary>
/// 取消簽出
/// </summary>
/// <param name="localPath"></param>
/// <param name="vssPath"></param>
/// <returns></returns>
public bool UndoCheckout(string localPath, string vssPath)
{
try
{
VSSItem item = vssdb.get_VSSItem(vssPath, false);
item.UndoCheckout(localPath);
return true;
}
catch (Exception e)
{
throw e;
}
}
/// <summary>
/// 簽入
/// </summary>
/// <param name="localPath"></param>
/// <param name="vssPath"></param>
/// <returns></returns>
public bool CheckIn(string comment, string localPath, string vssPath)
{
try
{
VSSItem item = vssdb.get_VSSItem(vssPath, false);
item.Checkin(comment, localPath);
return true;
}
catch (Exception e)
{
throw e;
}
}
/// <summary>
/// 加入新檔案
/// </summary>
/// <param name="comment"></param>
/// <param name="localPath"></param>
/// <param name="vssPath"></param>
/// <returns></returns>
public bool Add(string comment, string localPath, string vssParentPath)
{
try
{
CreateProject("", vssParentPath);
VSSItem item = vssdb.get_VSSItem(vssParentPath, false);
item.Add(localPath, comment);
return true;
}
catch (Exception e)
{
throw e;
}
}
/// <summary>
/// 建立專案目錄(從現有目錄)
/// </summary>
/// <param name="comment"></param>
/// <param name="vssParentPath"></param>
/// <param name="ProjectName"></param>
/// <returns></returns>
public bool CreateProjectWithParent(string comment, string vssParentPath,string ProjectName)
{
try
{
VSSItem item = vssdb.get_VSSItem(vssParentPath, false);
item.NewSubproject(ProjectName, comment);
return true;
}
catch (Exception e)
{
return false;
}
}
/// <summary>
/// 建立專案目錄
/// </summary>
/// <param name="comment"></param>
/// <param name="vssPath"></param>
/// <returns></returns>
public bool CreateProject(string comment, string vssPath)
{
try
{
string[] sep = new string[] { "/" };
string[] paths = vssPath.Substring(2, vssPath.Length - 2).Split(sep, StringSplitOptions.RemoveEmptyEntries);
string projectPath = "$";
int counter = 1;
//建立第一個目錄
CreateProjectWithParent(comment, "$/", paths[0]);
foreach (string pathitem in paths)
{
projectPath += "/" + pathitem;
if (counter < paths.Length)
{
CreateProjectWithParent(comment, projectPath, paths[counter]);
}
counter++;
}
return true;
}
catch (Exception e)
{
throw e;
}
}
#endregion
#region 其他功能
/// <summary>
/// 從字串寫檔案
/// </summary>
/// <param name="FileName"></param>
/// <param name="queryResult"></param>
public void WriteFile(string FileName, string queryResult)
{
FileStream fs = new FileStream(FileName, FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
string buffer = string.Empty;
sw.Write(queryResult);
sw.Flush();
sw.Close();
fs.Close();
}
#endregion
}
/// <summary>
/// 程式庫物件
/// </summary>
[Serializable]
public class VsItem
{
/// <summary>
/// 程式庫完整路徑
/// </summary>
public string fullpath { get; set; }
/// <summary>
/// 程式庫狀態
/// </summary>
public string status { get; set; }
}
}
引用方法如下
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO;
using Model = VssUtil;
using System.Diagnostics;
namespace VssControl
{
class Program
{
static void Main(string[] args)
{
try
{
Model.VssReader vssReader = new Model.VssReader(@"\\127.0.0.1\VSSWeb\srcsafe.ini", "admin", string.Empty);
//讀取目錄清單
foreach (string folderitem in vssReader.GetFolderName("$/DotNet4"))
{
Console.WriteLine(folderitem);
}
//Get file 從VSS
vssReader.GetFileFromVSS(@"H:\vs2", "$/App_Code/UserInfo.cs");
//取得檔案狀態
Console.WriteLine(vssReader.GetFileStatus("$/App_Code/UserInfo.cs"));
// Add File
vssReader.Add("", @"H:\123.txt", "$/App_Code/AB");
//Check Out
vssReader.CheckOut("", @"C:\vs2\App_Code\AB\123.txt", "$/App_Code/AB/123.txt");
//Check in
vssReader.CheckIn("這是簽入註解", @"C:\vs2\App_Code\AB\123.txt", "$/App_Code/AB/123.txt");
vssReader.CheckOut("", @"C:\vs2\App_Code\AB\123.txt", "$/App_Code/AB/123.txt");
//UndoCheckout
vssReader.UndoCheckout(@"C:\vs2\App_Code\AB\123.txt", "$/App_Code/AB/123.txt");
//建立專案目錄(第一層目錄需要建立)
vssReader.CreateProject("", "$/App_Code/AB/CD/EF/GH/IJK");
//建立專案目錄第一層目錄
vssReader.CreateProjectWithParent("", "$/", "aabbcc");
}
catch (System.Runtime.InteropServices.COMException cmo)
{
Console.Write(cmo.Message);
Console.Read();
}
}
}
}
STEP3 建立Web services
只建立查詢的方法
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using Model = VssUtil;
using System.Web.Configuration;
/// <summary>
/// VssFileManager 的摘要描述
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// 若要允許使用 ASP.NET AJAX 從指令碼呼叫此 Web 服務,請取消註解下一行。
// [System.Web.Script.Services.ScriptService]
public class VssFileManager : System.Web.Services.WebService {
private string ssDir = WebConfigurationManager.AppSettings["ssDir"].ToString();
private string ssAccount = WebConfigurationManager.AppSettings["ssAccount"].ToString();
private string ssPassword = WebConfigurationManager.AppSettings["ssPassword"].ToString();
private Model.VssReader vssReader = null;
public VssFileManager () {
//如果使用設計的元件,請取消註解下行程式碼
//InitializeComponent();
if (vssReader == null)
{
vssReader = new Model.VssReader(ssDir, ssAccount, ssPassword);
}
}
/// <summary>
/// 取得程式庫檔案狀態(已簽入,已簽出,不存在)
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
[WebMethod]
public string GetFileStatus(string filePath)
{
return vssReader.GetFileStatus(filePath);
}
/// <summary>
/// 取得單筆程式庫檔案查詢結果
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
[WebMethod]
public VssUtil.VsItem GetLibraryItem(string filePath)
{
return vssReader.GetLibraryItem(filePath);
}
/// <summary>
/// 取得多筆程式庫檔案查詢結果
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
[WebMethod]
public List<VssUtil.VsItem> GetLibraryItemList(List<string> path)
{
return vssReader.GetLibraryItemList(path);
}
}
web.config內容
<!--
如需如何設定 ASP.NET 應用程式的詳細資訊,請造訪
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<appSettings>
<add key="ssDir" value="\\127.0.0.1\VSSWeb\srcsafe.ini"/>
<add key="ssAccount" value="admin"/>
<add key="ssPassword" value=""/>
</appSettings>
<system.web>
<compilation debug="false" targetFramework="4.0">
<assemblies>
<add assembly="Microsoft.VisualStudio.SourceSafe.Interop, Version=5.2.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
</system.web>
</configuration>
STEP4 在頁面中引用
{
VssFileManager.VssFileManager vssMgr = new VssFileManager.VssFileManager();
lab_QueryResult.Text = "[檔案]" + filePath + " : " + vssReader.GetFileStatus(filePath);
}
這樣就完成了
備註
如果出現VSS資料庫檔案無法讀取的狀況
可以參照下列方式解決
Access to file ..\vss\data\rights.dat" denied
How to: Set Share Permissions for a Database
可以利用command line方式去改變資料夾的使用權限
DOS 使用指令變更Windows檔案【資料夾】的屬性及安全性
參考資料
An application to fetch the release sources from Visual SouceSafe based on an Excel migration plan
How to: Create a C# Test Project
Visual SourceSafe 6.0 Automation
VSS Automation - Interop.dll not availalbe?