Getting the Latest Files from Microsoft Visual SourceSafe Remotely

Prerequisites

This article assumes a knowledge of Visual Studio.NET, C#, C++, and some knowledge of Web services. Also, IIS needs to be installed and set up on the server that hosts Visual SourceSafe.

Introduction

Wouldn’t it be nice to be able to get the latest project you require out of Visual SourceSafe from out of the office? Okay, so you can use offsite versions of Visual SourceSafe, but you have to pay for most of these applications. It would be nice to do this programatically. You never know; the next project you are working on may require you to do this or something similar.

The solution I have found for getting files from Visual SourceSafe remotely is to use Web services.

What I introduce here to do this is to use Visual SourceSafe’s COM interface to connect to it and get the methods it exposes; then wrap some of these methods up into Web services. The Web services will be written in C#.NET because I find this very intuitive when writing Web services. Also, you will write a C++.NET client to consume these Web services.

Connecting and getting files from Visual SourceSafe is quite straightforward once you have the methods of its COM interfaces exposed. The interesting interfaces are:

  • IVSSItems
  • IVSSDatabase
  • IVSSItem

I have created a C# ASP.NET Web service project that will use these interfaces. To use the above interfaces, you need to add a reference to a C# project. The reference you need to add is:

Microsoft SourceSafe 6.0 Type Library

This can be added by right-clicking your project in Visual Studio and clicking Add Reference from the popup menu. From the dialog box that appears, you select the COM tab and locate the above-mentioned COM object and click OK to add it. This will add a reference to the COM object. Now, to use this COM object, you need to create an alias for a namespace. The namespace will be SourceSafeTypeLib, so you just add the following to your code:

using SourceSafeTypeLib;

This now means you can create a reference to the COM interfaces, for example:

IVSSDatabase m_DB;

To create this object, just do the following:

m_DB = new VSSDatabase( );

Connecting to SourceSafe

To connect to a SourceSafe database, you have to specify the source safe database path, username, and password. The following function takes care of this:

public string Connect( string database, string username,
                       string password )
{
   m_db = new SourceSafeTypeLib.VSSDatabase();
   try
   {
      m_db.Open( database, username, password );
   }
   catch ( System.Runtime.InteropServices.COMException ex )
   {
      return ex.Message + " -> " + ex.StackTrace;
   }
   catch ( System.Net.WebException we )
   {
      return we.Message + " -> " + we.StackTrace;
   }
   return "Connected";
}

This function will return a string that holds “Connected” if successful. If not, it will return the reason why it couldn’t connect.

Okay. The next thing you need to do when connected is to get the latest files you want. The solution I came up with here was to allow the server to get the latest files to its own local hard drive and then download these files to the client. You cannot get the files from the server directly to the client; this is a two-fold operation. Look at the function I came up with:

public string GetLatest( string path, string localpath,
                         string username )
{
   m_username = username;
   m_FileDetails.RemoveRange( 0, m_FileDetails.Count );
   m_UniqueFileDetails.RemoveRange( 0, m_UniqueFileDetails.Count );
   string strResult = "IVSSDatabase is null";
   if( m_db != null )
   {
      IVSSItem vitem = m_db.get_VSSItem( path, false );
      GetItems( path, localpath, vitem );
      strResult = "Completed";
   }
   m_index = 0;
   return strResult;
}

The function takes in the path of which project to get, a local path that specifies where to put the files, and a username. The username is used to tag to the local path. Thus, if the local path were set to c:build area and the username was Joe Bloggs, the path would be c:build areaJoeBloggs; this is the area the files would be put on the server. Why do this, you may ask? Why not just use the path name; why the need for the username? Well, what if somebody were asking for the same set of files at the same time? The server would get the files to the same area without this “uniqueness.”

You may notice two variables in this function, called m_FileDetails and m_UniqueFileDetails. These two member variables are ArrayLists that hold the path names of the files you have requested. m_FileDetails stores the path names without the username tagged, whereas m_UniqueFileDetails holds the path names with the username tagged. Later, you will see why I used these two array lists.

Okay so far? As part of the above function, there is a call made to another function. This is GetItems, which takes in the project path, local path, and a reference to IVSSItem. This function recursively gets the files/project requested. It looks as follows:

public void GetItems( string path, string localpath, IVSSItem item )
{
   string temp = localpath;
   if( item.Type == 1 )    // ignore if  not project
   {
   }
   else
   {
      IVSSItems ppItems;
      ppItems = item.get_Items( false );
      string strProj = item.Name;
      foreach( IVSSItem childItem in ppItems )
      {
         localpath = temp;
         string strUniquePath = localpath;
         m_index = 0;
         string strname = childItem.Name;
         string strPath = GetPaths( childItem );
         string strC = strPath;
         strC += "\";
         strC += strname;
         localpath += strC;
         string strLocalPath = localpath;
         m_username = m_username.Replace(" ","");
         strUniquePath += m_username;
         strUniquePath += "\";
         strUniquePath += strC;
         childItem.Get( ref strUniquePath, 0 );    // store as
                                                   // unique path
         localpath = temp;

         m_FileDetails.Add( strLocalPath );

         m_UniqueFileDetails.Add( strUniquePath );

         GetItems( path, localpath, childItem );
      }
   }
}

I’m not going to explain this function line by line, but just tell you that it basically just gets all the files out you have requested recursively. So, for example, if you requested the project $/Development/MyLatest App, all the files under this project and its associated folders will be “gotten.” You can see I’m using the array lists mentioned before in this function; you can see that they are used to store the pathnames of the files. One thing worth pointing out is that this function calls another one named GetPaths; this function is used to build up the folder name. The function looks as follows:

public string GetPaths( IVSSItem pItem )
{
   string b;
   string cStr = "";

   IVSSItem pParent = pItem.Parent;
   if( pParent != null )
   {
      b = pParent.Name;
      if( b.Length > 0 )
      {
         cStr += b;
         m_strPaths[m_index] = cStr;
         m_index++;
         GetPaths( pParent );
      }
   }
   string strPath = "";
   for( int i = m_index; i > 0; i -- )
   {
      strPath += "\";
      strPath += m_strPaths[ i - 1 ];
   }
   return strPath;
}

This is another recursive function that builds up the folder name.

More by Author

Must Read