A few years ago, I had a project to automate our team's build process which involves several SourceSafe operations. I searched in vain for good sample programs but none were comprehensive enough; and most guides and samples that I could find were in Visual Basic. This sample project is therefore some kind of repository of VC++ codes of how to interface with SourceSafe operations, namely:
- Connecting to SourceSafe database
- Operations such as CheckIn, CheckOut, Undo Checkout, Label, Add projects, files etc.
- Traversing the SourceSafe repository and representing it in a tree control
- Getting a file history list and retrieving the version from it
I have recently seen a lot more samples and articles about SourceSafe here in CodeProject, but I'll share this article anyway. Who knows, it may help others as well.
The sample program provided is ONLY a demonstration of what you can do with SourceSafe automation. Error checking was grossly omitted for brevity. The author is not responsible for any loss of data resulting from running the program or using the codes in the sample program.
Points of Interest
As a sample application article, I believe the source code will speak for itself. I will however just highlight several aspects of SourceSafe automation in Visual C++.
Interfacing with Visual SourceSafe
There are two ways to interface with VSS.
1. Type Library - Import via SSAPI.DLL
This is done by generating and using ssapi.tlh and ssapi.tli. It has the advantage of having the smart pointers already declared and wrapper methods for error handling (throws COM error
HRESULT, is non
S_OK). However, this method is less portable because it needs the path to the SSAPI.dll. For example: this could be in your stdafx.h.
#import "C:\Program Files\DevStudio\Vss\win32\ssapi.dll" no_namespace
Creating a SS database connection is as follows:
mp_vssDatabase->Open(psz_VSSini, psz_User, psz_Password);
Note the differences when using the ssauto.h.
2. Raw COM interface - Microsoft provided ssauto.h
The file is available from the MSDN site. The header contains only the raw COM interface class and defined constants. If you want to use smart pointers, you'll have to define them yourself. The attached sample project does exactly that. I defined them in the stdafx.h as follows.
#include <span class="code-string">"ssauto.h"</span>
Creating a SS database connection is as follows:
CLSIDFromProgID(L"SourceSafe", &clsid ));
CoGetClassObject( clsid, CLSCTX_ALL, NULL, IID_IClassFactory, (void**)&pClf );
pClf->CreateInstance( NULL, IID_IVSSDatabase, (void **) &mp_vssDatabase );
Note the differences when using the ssapi.dll.
Traversing the Visual SourceSafe Database
The sample has a tree control (
CSSTreeCtrl) which provides a good example of navigating the database. The tree loads up fast because, when the tree is initialized, it only populates the nodes which are directly connected to the path when traversing from root to the default project (taken from
VSSDatabase::get_CurrentProject method). Other nodes are opened when the user actually opens them up by clicking on the tree control graphics.
h_InsertPath method below shows how to recursively load the partial structure into the tree control. The
deepestNodeFound item can be used later to select/expand the tree.
HTREEITEM CSSTreeCtrl::h_InsertPath(IVSSItemPtr vss_item, HTREEITEM h_parent,
CString & rstr_traverseToPath, HTREEITEM & rh_deepestNodeFound)
HTREEITEM h_item = NULL;
CString str_Filename = static_cast<LPCTSTR>(str_name);
if (h_parent == NULL)
str_Filename = gsz_root;
tvis.hParent = h_parent;
tvis.hInsertAfter = TVI_LAST;
tvis.item.mask = TVIF_CHILDREN | TVIF_IMAGE |
TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM;
tvis.item.lParam = NULL;
tvis.item.pszText = str_Filename.GetBuffer(str_Filename.GetLength());
long l_count, l_checkOut;
if (i_type == VSSITEM_FILE)
if (l_checkOut == VSSFILE_NOTCHECKEDOUT)
tvis.item.iImage = ITEM_FILE_NORMAL_IMAGE;
tvis.item.iSelectedImage = ITEM_FILE_NORMAL_IMAGE;
tvis.item.iImage = ITEM_FILE_CHECKOUT_IMAGE;
tvis.item.iSelectedImage = ITEM_FILE_CHECKOUT_IMAGE;
else if (i_type == VSSITEM_PROJECT)
tvis.item.iImage = ITEM_PROJECT_NORMAL_IMAGE;
tvis.item.iSelectedImage = ITEM_PROJECT_SELECTED_IMAGE;
tvis.item.cChildren = (l_count > 0) ? 1:0;
h_item = InsertItem(&tvis);
CString str_currentPath = str_ItemToPath(h_item);
if (rstr_traverseToPath == str_currentPath)
rh_deepestNodeFound = h_item;
if (h_parent && rstr_traverseToPath.Find(str_currentPath) != 0)
if (i_type == VSSITEM_PROJECT)
for (long i = 0; i < l_count; i++)
catch (_com_error &e)
ATLTRACE(_T("COM error: %s\n"), e.ErrorMessage());
For the rest of the code, please refer to the
CSSTreeCtrl source code in the sample project.
Retrieving a SourceSafe Item History
The history of an item is accessible via
IVSSVersions which provides an interface to
IEnum from which we could iterate each history item.
void v_DumpItemHistory(IVSSItemPtr vss_item)
int i_type = 0;
if (vss_item->get_Versions((i_type == VSSITEM_PROJECT)?
VSSFLAG_HISTIGNOREFILES:0, &versions) == S_OK)
while (p_versionsEnum->Next(1, &object, &l_fetched) == S_OK)
ATLTRACE(_T("Version %d by %s"), l_value, bstr_text);
ATLTRACE(_T(" On %s %s\n"),
catch (_com_error & e)
A more comprehensive example is in the sample project's
Downloading an Old Version of an Item
You can retrieve an old version as long as you have the version number. The old version of an item is also represented as a
VSSItem object where you can call
Get() method to download into the working directory. Note that you cannot checkout an old version.
&vss_oldItem) == S_OK)
The Sample Application
I highlight the sample application itself because it is also a good example of:
- WTL Application
- Subclassing tree control in WTL
- Message reflection
- Resizing dialog
Other CodeProject articles about SourceSafe.
Here are links to references I used during my "struggle":