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.

Getting the Latest Files from Microsoft Visual SourceSafe Remotely

So, let me summarise what you've done so far. You have a C# ASP.NET project to which you have added a reference to the SourceSafe COM object that is needed to use it. You have created a function that allows you to connect to the SourceSafe database you require and have created functions that will get the latest project out of SourceSafe to the server you have asked for.

The steps you have done so far then are:

Have a C# ASP.NET Web service project that:

  • Connects to SourceSafe
  • Gets the latest project with its files from SourceSafe to the server

You now have the functionality to connect to SourceSafe and get the latest project with its files to the server, but it seems that there is something missing. How do you get the project down to the client's machine? Remember, the files at the moment have just been stored on the server's local hard drive. What you need is a method of transferring these files to the client's machine. The method—or rather, the technique—I use for this is to create a function that creates a file stream and passes binary data back to the client coded as Base64. This sounds more complex than it actually is. Please note that if you were transferring large amounts of data, it may be best to code them as DIME (do a Google on this for more information!). The function to create this file stream is as follows:

[WebMethod]
public byte[] LoadFile(String path)
{
   FileStream fsImage = new FileStream( path, FileMode.Open,
                                        FileAccess.Read);
   byte[] bytThumb = new byte[fsImage.Length];
   fsImage.Position = 0;
   fsImage.Read(bytThumb, 0, (int)fsImage.Length);
   fsImage.Close();
   return bytThumb;
}
Note: This function is set up as a Web method. This allows a client to communicate to this method from over the Web.

The client makes a call to this function, which returns a byte array of data. Then, this data is stored on the client's machine at the requested path location. A little bit like this:

pTemp = svr->LoadFile( strUniqueFile );
try
{
   FileStream* file = new FileStream( strFile, FileMode::Create );
   file->Write( pTemp, 0, pTemp->Length );
   file->Close();
}
catch( UnauthorizedAccessException* uex )
{
}
catch( IOException* se )
{
}

This will copy the file from the server to the client's machine.

I want to summarise again what has taken place: You have connected to SourceSafe, requested the latest files (project), and then asked for these files to be transferred to the client's machine. All the connectivity to SourceSafe is done through its COM interfaces. All the methods are wrapped up into Web service methods that are written in C# and allow a client to communicate with them over the Web.

There are many issues that you need to resolve when getting files from SourceSafe remotely to a client machine. For instance, what if the project you were getting out was a folder with no files? Obviously, this does happen. Does this folder exist on the client machine? You need to make checks for this and create the relevant directories. In the code I present in the client, you will see how I have done this. I don't want to go into detail as how to do this because I'm sure you have many ideas yourselves, but how I've done it in the client code seems very simplistic and I feel it's a good method. Other issues are that getting files out of SourceSafe and copying back to the client can take time, so, what about http request time out issues? Again, this is taken care of in the client code I've attached.

How to Use the Attached Code

Two project files are attached. One is the client project. the other is the Web service project. To use the Web service project, you will need to copy the project to the server machine and create a virtual directory. You need to use IIS manager to create the virtual directory. Point the virtual directory to the Web service project (where you just copied it to) and all should be well. The client project will need you to update the Web reference. Load the project into Visual Studio.NET and click on localhost. In its properties, please set the Unique Identifier to the server location of the Web service. In my example, it is set to:

http://10.44.14.1/LoadFile/Service1.asmx

Then, right-click on localhost and select Update Web Reference. This is needed to create a new proxy to access the Web service. There are solutions, so you don't have to do this, but I don't really want to go into them. One of them is generating the proxy code on the fly. Now, build the client application and hopefully it should all run. You will need to put your username and password in to connect to the SourceSafe database. The proxy server stuff at the moment is commented out in the code, but should work if you uncomment the code and you need to go through a proxy. I've not been able to check this because I don't go through one. You will need your proxy server address, username, and password for this. In most cases you can leave the domain name blank.

There is more code in the client application that isn't used, but may be useful if you just want to connect to SourceSafe via an intranet, thus not having to use Web services.

Summary

I hope this article has proved useful in some way. Basically, all that is happening is you connect to SourceSafe and get the files via Web services. These Web services connect to SourceSafe using SourceSafe's COM interfaces. Files are downloaded to the client machine from another Web service method that downloads them as binary data.

The code should be inspected for any further information you may require.



About the Author

Steve Green

I'm a Software Engineer with a company which writes Diagnostic Applications for vehicles. In my spare time ( what I have of it! ) I love playing Golf and Football and spending as much time as I can with my lovely baby son David.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • Live Event Date: September 16, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you starting an on-premise-to-cloud data migration project? Have you thought about how much space you might need for your online platform or how to handle data that might be related to users who no longer exist? If these questions or any other concerns have been plaguing you about your migration project, check out this eSeminar. Join our speakers Betsy Bilhorn, VP, Product Management at Scribe, Mike Virnig, PowerSucess Manager and Michele …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds