Microsoft Visual SourceSafe OLE Automation

 

Ken Felder
Microsoft Corporation

October 1995

Contents

Introduction

The Visual SourceSafe Object Model

Visual SourceSafe Events (the IVSSEvents Interface)

Error Handling

Introduction

This specification describes the OLE Automation interfaces to Microsoft® Visual SourceSafe™ version 5.0. Future versions of Visual SourceSafe will attempt to make as few changes as possible to these interfaces. Versions earlier than Visual SourceSafe 5.0 do not support these interfaces in any way.

A Microsoft Visual Basic® application can get a Visual SourceSafe object and begin executing commands (such as "Get this file" or "Tell me whether this file is currently checked out"). It can also register itself to receive events from Visual SourceSafe. ("Let me know if someone checks this file out.") The two can be used together: for instance, if you programmatically tell Visual SourceSafe to check a file out, an event is triggered, letting Visual SourceSafe add-ins (and your own) know that the file is about to be checked out. Hence, the following code would cause a system hang by creating an endless loop of events.

Sub BeforeCheckout (File as VSSItem, Local as String)
File.Checkout
End Sub

Avoiding this kind of loop is the responsibility of the user and only requires a little common sense.

Hooking Up to Drive Visual SourceSafe

Finding a Visual SourceSafe object to drive is similar to finding any other OLE object. A Visual Basic programmer would:

Dim Database as new VSSDatabase 

and then call:

Database.Open 

A C++ programmer would:

CoCreateInstance 

the same object, and once again call the Open method.

The globally unique identifier (GUID) for the Visual SourceSafe database object, found in the file SSAPI.DLL, is 783CD4E2-9D54-11cf-B8EE-00608CC9A71F. You can view the GUID and other information in the .DLL by using the Object Browser in Visual Basic or the utility OLE2VW32.EXE (which comes with Microsoft Visual C++®).

Hooking Up to Receive Visual SourceSafe Events

To register for Visual SourceSafe events, add your tool to the user's registry so that Visual SourceSafe can CoCreateInstance your event handling object.

In addition, add yourself to the file SSADDIN.INI. You find this file by looking in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SourceSafe\SCCServerPath, which will give the path to a file called SSSCC.DLL. SSADDIN.INI should live in that same directory. Then add a line containing <your ProgID>=1 to this file to register yourself as an add-in.

As an example, suppose that the registry variable were set to C:\VSS\WIN32\SSSCC.DLL. You would want a file called C:\VSS\WIN32\SSADDIN.INI. If that file did not exist, you would create it; if it did exist, you would open it. Then, supposing your programmatic identifier (ProgID) were "CoolAddIn," you would add the line "CoolAddIn=1" to the file. This would tell Visual SourceSafe to load your add-in based on a registry lookup on that ID.

Visual SourceSafe connects to your add-in via the standard connection point interfaces. The following is a brief overview of the system; see the OLE reference manuals for more detail.

  1. Visual SourceSafe finds your ProgID in SSADDIN.INI.
  2. Based on that, Visual SourceSafe calls CoCreateInstance on your ProgID to instantiate your object.
  3. Visual SourceSafe queries your object for an interface called IVSSEventHandler. Your object must support this interface which contains only one method: Init.
  4. Visual SourceSafe calls your Init method, passing in a pointer to an interface called IVSS on a Visual SourceSafe object.
  5. You then QueryInterface from IVSS to another interface on the same object, called IConnectionPointContainer. This interface supports a method called FindConnectionPoint.
  6. From that interface, you call FindConnectionPoint(IID_IVSSEvents), which returns an interface called IConnectionPoint.
  7. From that interface, you call the Advise method, passing a pointer to your own IVSSEvents interface.
  8. Visual SourceSafe is then hooked up and will start calling methods in your IVSSEvents interface.

A few notes about this process.

  • Your add-in that receives Visual SourceSafe events must be an in-process .DLL. So, if you want to catch Visual SourceSafe events, and then drive Visual SourceSafe based on them, you must put all your driving code into that .DLL or, if it is in a Visual Basic application, set up your own communication from the event handler to the application.
  • Visual SourceSafe may shut down and start up your add-in at will.
  • You should notCoCreateInstance Visual SourceSafe to open another database while you are in the middle of an event. You should only work on the Visual SourceSafe database that triggered the event. You can see information about this database by retrieving the VSSDatabase property from the IVSS interface (which you were handed in step 4 above). From that database interface, you can gain access to all the objects, properties, and methods in the database.

Other Technical Notes

A few miscellaneous, but important, remarks:

  • The Visual SourceSafe objects are implemented by dual interfaces, meaning that they can be driven by either C++/COM (early bound) or Visual Basic/Automation (late bound) calls. Users calling from Visual Basic must use Visual Basic 5.0 or later, as this interface relies on certain features that were not available in earlier versions.
  • This interface has no direct user interface. Any user interface must be supplied by the programmer.
  • The Visual SourceSafe interfaces are thread safe.
  • You can make multiple calls that access different databases. However, internally, Visual SourceSafe will actually shut down one database and open another when you switch! This can be quite slow, so it should be done judiciously. In addition, certain restrictions apply. You cannot open another database while you are in the middle of enumerating a history and you cannot open a database from within an event call.
  • The Visual SourceSafe objects are served by an in-proc server (a .DLL). The objects can not be aggregated.
  • Once you create a Visual SourceSafe VSSItem (file or project) in memory, that object stays in memory until it is released. It may therefore become out of date! For instance, if you instantiate a VSSItem representing a file, that in-memory item knows whether the file is checked out. But if you ask it an hour later, its information may no longer be correct. So it is not recommended to keep these objects in memory for too long.
  • Similar considerations apply to the VSSItems collection because it never changes in memory. Even if your code deletes a file, that file will not disappear from the collection!

The Visual SourceSafe Object Model

The Visual SourceSafe object model, as seen in Figure 1, is very simple.

Figure 1. The Visual SourceSafe object model.

VSSDatabase A Visual SourceSafe database.
VSSItem A project or file. Note that there is also a VSSItems, which is a collection for all the children in one project.
VSSVersion One way of representing a specific version of a file or project. A VSSVersions is a collection of all the versions of a particular file or project.
VSSCheckout A checkout record on a file. Note that once again there is a collection, because one file may have many simultaneous checkouts.

The VSSDatabase Object and IVSSDatabase

The VSSDatabase object represents one Visual SourceSafe database, logged in as one user. It supports the IVSSDatabase interface, which contains the methods and properties described below.

This interface is very important because it is the starting point of a Visual SourceSafe session. You always start by calling the Open method to login, and then you get a VSSItem, which is the launching point for all Visual SourceSafe commands.

Database properties

HRESULT VSSItem ([in]BSTR Spec, [in,defaultvalue(0)]boolean bDeleted, (out,retval)IVSSItem **ppIVSSItem);
  • Read-only property.
  • This is the key property in this interface. You pass in a spec, such as "$/A/B" (which represents a Visual SourceSafe project) or "$/A/B/FU.FRM" (which represents a Visual SourceSafe file). This returns a VSSItem object**that represents that object. Thereafter, you can perform Visual SourceSafe commands and queries on that object by using the IVSSItem interface. Note that if you pass in a nonzero value for bDeleted, you are asking for a file or project that is deleted inside the Visual SourceSafe database.
HRESULT SrcSafeIni ([out,retval]BSTR *pSrcSafeIni);
  • Read-only property.
  • Gives you the path and name of the SRCSAFE.INI file (which in turn is associated with a particular Visual SourceSafe database). This is used in conjunction with the Open call in the following way. When you are adding a new project to Visual SourceSafe for the first time, it is valid to call Open without specifying a SRCSAFE.INI file. You then add the project, but you do not know what database it was added to! So you get the SrcSafeIni property of the database and persist it somewhere. The next time you want to work with this project, you call Open and pass in that same SRCSAFE.INI file; this ensures that you have the same database, so you should be able to find the same project.
HRESULT DatabaseName ([out,retval]BSTR *pDatabaseName);
  • Read-only property.
  • Gives you the user-defined name of the Visual SourceSafe database. This is the name that users see when they select a database to open in Visual SourceSafe.
HRESULT UserName ([out,retval]BSTR *pUsername);
  • Read-only property.
  • Gives you the currently logged-in user name.
HRESULT CurrentProject ([out,retval]BSTR *pPrj);
HRESULT CurrentProject ([in]BSTR Prj);
  • Read-write property.
  • Represents the user's current project, which can be retrieved or changed.

Database methods

HRESULT Open ([in,defaultvalue(0)]BSTR SrcSafeIni, [in,defaultvalue(0)]BSTR Username, [in,defaultvalue(0)]BSTR Password);

This function is how you log into Visual SourceSafe. The first parameter specifies the path and name of a SRCSAFE.INI file to use and is therefore a way of choosing a Visual SourceSafe database. The other parameters specify a user name and password; if you do not specify them, Visual SourceSafe will find the user name from Windows or a user-defined environment variable, if possible. If the user cannot be logged in from this information, the call will fail.

If you do not pass in a SrcSafeIni parameter, Visual SourceSafe will attempt to find the SRCSAFE.INI file itself, using essentially the same algorithm that the Visual SourceSafe Explorer uses. This is a good thing to do if you just want to use whatever Visual SourceSafe database the user tends to work with; however, note that successive calls to Open may wind up in different databases! One safe way to work is to call Open with no SrcSafeIni variable and add your project to Visual SourceSafe, and then get and save the SrcSafeIni property. The next time you want to find that project again, call Open passing in that SrcSafeIni value, and you will be sure of opening the same database.

The VSSItem Object and IVSSItem

This object can represent either a file or project in Visual SourceSafe, or an old version of a file or project. It exposes the IVSSItem interface, which is where almost all Visual SourceSafe commands and queries are found. (Old versions are discussed in more detail later, in the section on versions.)

There are two primary ways to get an IVSSItem pointer. One is through a call to IVSSDatabase::VSSItem where you essentially make the very specific request: "give me an IVSSItem pointer for $/My/Web/FU.HTML." The other is through the VSSItems object, which is a collection of all the children of a project: "give me IVSSItem pointers for all the children of $/My/Web."

Item properties

HRESULT Spec ([out,retval]BSTR *pSpec);
  • Read-only property.
  • Valid for files and projects.
  • Gives you the item's full project spec, such as $/My/Web/FU.HTML. This spec may be how you got to the IVSSItem in the first place. However, if you got to it by enumerating the children of a project, you can then use this property to actually find the names of all the items.
HRESULT LocalSpec ([out,retval]BSTR *pLocal);
HRESULT LocalSpec ([in]BSTR Local);
  • Read-write property.
  • Valid to read for files or projects; valid to write for projects and invalid to write for files.
  • Represents the local item that corresponds to this Visual SourceSafe item. For a project, this is the user's working folder for the project. For a file, it is the file name in the working folder for the project.
HRESULT Name ([out,retval]BSTR *pName);
HRESULT Name ([in]BSTR Name);
  • Read-write property.
  • Valid for files and projects.
  • Returns the name of a project or file without the path. So if Spec returns $/A/B/FU.CPP, this property will be FU.CPP. Setting this variable is the way to programmatically rename a file or project.
HRESULT Parent ([out,retval]IVSSItem **ppIParent);
  • Read-only property.
  • Valid for files and projects.
  • Returns a VSSItem interface for the item's parent project. For instance, if you pass this the item that represents $/A/B/FU.CPP, you receive back an item that represents $/A/B.
HRESULT Type ([out,retval]int *piType);
  • Read-only property.
  • Valid for files and projects.
  • Returns VSSITEM_PROJECT for projects, VSSITEM_FILE for files.
HRESULT Deleted ([out,retval]boolean *pbDeleted);
HRESULT Deleted ([in]boolean bDeleted);
  • Read-write property.
  • Valid for files and projects.
  • In Visual SourceSafe, a normal delete simply "tags" the item as deleted. Hence, deleted is simply a flag which can be turned on and off for a particular file or project. When an item is deleted, it will not be returned by most function calls and should not generally be displayed to the user alongside nondeleted files. The only operations that are valid to perform on a deleted item is to Undelete it (by turning off the flag) or Destroy it (which permanently removes the item from the Visual SourceSafe database).
HRESULT IsCheckedOut ([out,retval]long *piStatus);
  • Read-only property.
  • Valid for files only (invalid for projects).
  • Returns VSSFILE_NOTCHECKEDOUT if the file is not checked out, VSSFILE_CHECKEDOUT if the file is checked out to another user, and VSSFILE_CHECKEDOUT_ME if the file is checked out to the current user. For more detailed information on the checkout status of the file, use the Checkouts property to get a VSSCheckouts for the file.
HRESULT IsDifferent ([in,defaultvalue(0)]BSTR Local, [out,retval]boolean *pbDifferent);
  • Read-only property.
  • Valid for files only (invalid for projects).
  • If you do not pass in a Local, this compares the file in Visual SourceSafe against its local equivalent. If you do pass in the parameter, this compares the file in Visual SourceSafe against the file that you pass. Either way, it returns a Boolean value indicating whether the two files are different or not.
HRESULT Binary ([out,retval]boolean *pbBinary);
HRESULT Binary ([in]boolean bBinary);
  • Read-write property.
  • Valid for files only (invalid for projects).
  • Represents whether a particular file is binary (as opposed to being straight ASCII text).
HRESULT VersionNumber ([out,retval]long *piVersion);
  • Read-only property.
  • Valid for files and projects.
  • Represents the current version number of the file or project. In most cases, this means the most recent version number. However, in the case of a VSSItem that represents an old version, this of course returns that version number. More subtly, if the VSSItem represents a pinned file, this returns the version number that is pinned. In all cases, this is the version number of the item that you would receive if you called the Get method on this VSSItem.

Collections that are retrieved as properties of an item

HRESULT Items ([in,defaultvalue(0)]boolean IncludeDeleted, [out,retval]IVSSItems **ppIItems);
  • Read-only property.
  • Valid for projects only (invalid for files).
  • This gives you a collection for all the children (files and subprojects) of a project. It does not work recursively; only direct children are returned, so the user must manually implement recursion (see the description of the VSSItems collection for more information). IncludeDeleted says to return deleted children as well as nondeleted ones.
HRESULT Checkouts ([out,retval]IVSSCheckouts **ppICheckouts);
  • Read-only property.
  • Valid for files only (invalid for projects).
  • This gives you a collection of all the checkout records of a file. If the file is not checked out, of course, the resulting set will have no elements; if the file is checked out only once, there will be only one element. But in general, one file may have many checkouts at one time.

Item methods

Note   Many of the methods described here have a parameter called iFlags. The usage of this parameter is described in detail after the list of methods.

HRESULT Add ([in]BSTR Local, [in,defaultvalue(0)]BSTR Comment, [in,defaultvalue(0)]long iFlags, [out,retval]IVSSItem **ppIItem);
  • Valid for projects only (invalid for files).
  • Add the passed file or directory to the project that you are calling the method on. Local is the full local spec of a file or directory to Add; Comment is a comment. The function returns an IVSSItem pointer for the newly added file. iFlags can contain options from each of the following sets: DELTA, RECURS, USERO, KEEPCHECK, BINTYPE, and DEL.
HRESULT NewSubproject ([in]BSTR Name, [in,defaultvalue(0)]BSTR Comment, [out,retval]IVSSItem **ppIItem);
  • Valid for projects only (invalid for files).
  • Creates a Visual SourceSafe subproject in the project that you are calling the method on. Name is the name only (not the full spec!) of the project to create. The function returns an IVSSItem pointer for the newly created subproject.
HRESULT Destroy ();
  • Valid for files and projects.

  • Destroys (permanently removes) the item that you are calling this method on. After you call this method, the VSSItem object now refers to an file or project that no longer exists. You should Release() the VSSItem as soon as possible and should not call any other operations on it.

    Note: This is one of the few methods that can be called validly on a deleted item; whether the item was previously deleted or not, it is gone after this call.

HRESULT Get ([in,out,defaultvalue(0)]BSTR *Local, [in,defaultvalue(0)]long iFlags);
  • Valid for files and projects.
  • Gets a file or project. Local is the local file name or directory to Get it to. If Local is omitted (in Visual Basic) or set to NULL (in VC++), the function automatically determines the local spec. If Local is an empty string (" "), the file is placed in a unique file name in the temporary directory, and the full path and name of the file is placed in Local. iFlags can contain options from each of the following sets: RECURS, USERO, CMPMETHOD, TIMESTAMP, EOL, REPLACE, and FORCE.
HRESULT Checkout ([in,defaultvalue(0)]BSTR Comment, [in,defaultvalue(0)]BSTR Local, [in,defaultvalue(0)]long iFlags);
  • Valid for files and projects.
  • Checks out a file or project. Comment is the Checkout comment; Local is the local spec to retrieve the file to (it works just like Local in Get). iFlags can contain options from each of the following sets: GET, RECURS, USERO, CMPMETHOD, TIMESTAMP, EOL, REPLACE, FORCE, and CHKEXCLUSIVE.
HRESULT Checkin ([in,defaultvalue(0)]BSTR Comment, [in,defaultvalue(0)]BSTR Local, [in,defaultvalue(0)] long iFlags);
  • Valid for files and projects.
  • Checks in a checked-out file or project. Comment is the comment; Local is the local file name, with directory path, to check in. If Local is NULL, the function will check in the file from wherever the file is checked out to (which is what you almost always want). However, by setting this parameter you can check a file in from a different directory—or you can specify which, of multiple checkouts that the same user has made, to check in. iFlags can contain options from each of the following sets: RECURS, USERO, CMPMETHOD, TIMESTAMP, EOL, REPLACE, FORCE, KEEPCHECK, DEL, and UPD.
HRESULT UndoCheckout ([in,defaultvalue(0)]BSTR Local, [in,defaultvalue(0)]long iFlags);
  • Valid for files and projects.
  • Undoes a Checkout of a file or all files in a project. Local is the local file name or directory path to Undocheckout. Local works exactly as it does in Checkin. iFlags can contain options from each of the following sets: RECURS, USERO, CMPMETHOD, TIMESTAMP, EOL, REPLACE, FORCE, KEEPCHECK, and DEL.
HRESULT Share ([in]IVSSItem *pIItem, [in,defaultvalue(0)]BSTR Comment, [in,defaultvalue(0)]long iFlags);
  • Valid for files and projects.
  • Shares the specified item (file or project) into the current project. The comment is used only when sharing a project and as the creation comment for the new project. iFlags can contain options from each of the following sets: RECURS and GET.
HRESULT Move ([in]IVSSItem *pINewParent);
  • Valid for projects only (invalid for files).
  • Moves the project into the specified parent project.
HRESULT Label ([in]BSTR Label, [in,defaultvalue(0)]BSTR Comment);
  • Valid for files and projects.
  • Puts a label on the file or project. In general, this labels the current version of the file or project; however, if the VSSItem represents an old version*,* the label is applied to that old version.
HRESULT Version ([in,optional]VARIANT Version, [out,retval]IVSSItem **ppIItem);
  • Valid for files and projects.
  • The idea here is that you pass in information about a version and Visual SourceSafe returns a VSSItem that represents that particular version of a file or project. Version is a string that can be in one of three formats. If it is a valid date string (for example, 6/12/96), the version is retrieved based on the state of the file or project at that particular date. If it is a valid numerical string (for example, 12), it represents that particular numbered version of the file or project. If it is neither a date string or a number (for example, 2.00b), it is treated as a label.
HRESULT Versions ([in, defaultvalue(0)]long iFlags, [out,retval]IVSSVersions **ppIVersions);
  • Valid for files and projects.
  • Returns a collection of all the versions of a file or project. iFlags is only used when getting the history of a project*,* and can contain options from each of the following sets: RECURS and HISTIGNOREFILES.

The iFlags parameter

Each time there is an iFlags parameter, you can pass zero, which means "do all the default things, as specified by the system and/or the user's SS.INI file." The only time you actually pass any of the flags is when you want to explicitly override a particular behavior, to say "do this thing" or "don't do this thing."

As an example, for recursion, you can pass zero, meaning "either recurse or don't, depending on the user's settings." Or, you can pass VSSFLAG_RECURSNO, meaning "do not recurse, no matter what the user's settings are" or VSSFLAG_RECURSYES, meaning "do recurse, no matter what the user's settings are." In general, of course, the default (0) settings should be used unless there is some particular reason to override them.

These flags can be combined by using the Or operator in C++, or simply by adding them in Visual Basic or Visual C++. So you could pass (VSSFLAG_RECURSNO+VSSFLAG_USERROYES), explicitly setting those two settings, but leaving the others to their defaults. Of course, (VSSFLAG_RECURSNO+VSSFLAG_RECURSYES) is invalid.

The actual flags are described below.

VSSFLAG_USERROYES
VSSFLAG_USERRONO

Indicate whether the read-only flag should be used and assumed in the user's local director

VSSFLAG_TIMENOW
VSSFLAG_TIMEMOD
VSSFLAG_TIMEUPD

Indicate what date/time stamp Visual SourceSafe should give the local file: the current time (default), the last modification time, or the last check-in time.

VSSFLAG_EOLCR
VSSFLAG_EOLLF
VSSFLAG_EOLCRLF

Indicate what end-of-line character should be used: carriage-return, line-feed, or carriage-return/line-feed.

VSSFLAG_REPREPLACE
VSSFLAG_REPSKIP
VSSFLAG_REPMERGE
VSSFLAG_REPASK

Indicate what to do if a Get would replace an existing writable file on the user's local drive: replace the file, skip the file, or merge into the file. REPASK means that Visual SourceSafe calls your error handling callback function to find out whether it should replace the file or not.

VSSFLAG_CMPFULL
VSSFLAG_CMPCHECKSUM
VSSFLAG_CMPTIME

Indicate how Visual SourceSafe should determine if a file is out of date. When you Get a file, Visual SourceSafe does not physically copy it over if you already have the correct file. By default, a checksum is used for this comparison.

VSSFLAG_RECURSNO
VSSFLAG_RECURSYES

Indicate whether a command should be recursive, that is, whether it should act on an entire project tree.

VSSFLAG_FORCEDIRNO
VSSFLAG_FORCEDIRYES

When Visual SourceSafe recursively gets a project tree, it can put each subproject into its own working directory (FORCEDIRYES), or create a directory tree, ignoring the working directories of subprojects (FORCEDIRNO).

VSSFLAG_KEEPYES
VSSFLAG_KEEPNO

Indicate whether Visual SourceSafe should keep the file checked out after adding or checking it in.

VSSFLAG_DELYES
VSSFLAG_DELNO
VSSFLAG_DELNOREPLACE

Indicate whether the local file should be deleted after an Add, Checkin, or Undocheckout. The third value—replace the local copy with the Visual SourceSafe copy—is valid only on Undocheckout, where it is the default behavior in general.

VSSFLAG_BINBINARY
VSSFLAG_BINTEXT

Indicate whether a file is text or binary. If neither flag is passed, Visual SourceSafe automatically detects the file type at add-time.

VSSFLAG_DELTAYES
VSSFLAG_DELTANO

Indicate whether the file being added to Visual SourceSafe will store old versions of itself (Deltas) or not.

VSSFLAG_UPDUPDATE
VSSFLAG_UPDUNCH

Indicate whether a file being checked in that has not changed at all should be registered as a Checkin (creating a new version), or an Undocheckout. UPDASK means that Visual SourceSafe calls your error handling callback function to find out whether it should check the file in or not.

VSSFLAG_GETYES
VSSFLAG_GETNO

Indicate whether the file should be retrieved (gotten) on commands that do an automatic Get, such as Checkout and Share.

VSSFLAG_CHKEXCLUSIVE

Indicates whether a checkout should be exclusive. If you do not set this flag, the Checkout may be exclusive anyway due to the administrator's setting or the file type. But if you do set this flag, the Checkout will always be exclusive.

VSSFLAG_HISTIGNOREFILES

This flag is used in only one place—when you retrieve the Versions property of a project. The flag corresponds to the Ignore file checkins in the Visual SourceSafe Explorer project History dialog box, but in reverse. That is, if you do not set this flag, the History returns all the events in the life of the project and all its files. If you do set this flag, file histories are not scanned. Thus, you see only events in the life of the project (such as labels, file adds and deletes, and so on), and iterating through all the versions is much faster.

The VSSItems Object and IVSSItems

When you want to iterate through all the children (files and subprojects) in a project, you get a VSSItems from that project. The VSSItems object is a collection of all the children of that project. It supports the standard properties and methods of a collection object.

IVSSItems properties

HRESULT Count ([out,retval]long *piCount);
  • Read-only property.
  • The number of children under the project.

IVSSItems methods

HRESULT Item ([in]VARIANT sItem, [out,retval]IVSSItem **ppIItem);
  • Retrieves one particular item in the collection. The item may be specified as an integer, which represents a one-based index into the collection, or as a name.
HRESULT _NewEnum ([out,retval]IUnknown **ppIEnum);
  • Returns an enumerator (an IEnumVariant) that supports the standard Next, Skip, Reset, and Clone methods. This method is used explicitly by Visual C++ programmers; Visual Basic programmers do not call it directly, but use the for-each loop, which relies on the enumerator.

Digression About Handling Versions

Before diving into the details of the VSSVersion object, it is worthwhile to discuss how versions are treated in general in Visual SourceSafe OLE Automation.

Two different objects to represent an old version

A specific old version of a file is represented by two different objects: a VSSItem and a VSSVersion. These two objects give you different abilities.

This article has primarily discussed VSSItems that represent a file in general (with no reference to version): for instance, $/A/FU.HTML. However, a VSSItem can also be used to represent a specific version of a file: for instance, $/A/FU.HTML version 2. In both cases, the VSSItem gives you general properties of the object (such as its name) and methods (such as Get). So if you call Get on a VSSItem that represents $/A/FU.HTML in general, you get the latest version; but if you call Get on a VSSItem that specifically represents version 2 of that file, you get version 2. You should note that some methods and properties of the IVSSItem interface are not valid when acting on an old version. For instance, if you have a VSSItem that represents version 2 of the file, you cannot set the Deleted property on that object, or call the Checkin method. Such actions are only valid on the file in general, not on a specific version.

The VSSVersion gives you version-specific properties of a specific version, such as the version number and the date/time associated with that change. One of the properties of a VSSVersion is the VSSItem associated with it. So, if you have a VSSVersion that represents version 2 of $/A/FU.HTML and you want to Get that version, you first find a VSSItem from the VSSVersion, and then call the Get method on that VSSItem. Note that you cannot go the other way! That is, there is no way—given a VSSItem that represents an old version—to retrieve a VSSVersion for that version.

As a final note, projects are treated in the exact same way as files. Hence, version 12 of $/A is also represented by both a VSSItem and a VSSVersion object.

How you get an old version

Suppose you have a VSSItem that represents the file $/A/FU.HTML in general (not version specific). How do you get objects that represent specific versions? There are two different ways.

The first is the Version property. You retrieve the Version property on your general VSSItem, passing in a version identification string (representing a date, version number, or label), and you receive back a VSSItem representing that particular version of the same file.

The second way is with the Versions property, which returns a VSSVersions collection on the general VSSItem. You iterate through that collection to list all the versions of the file or project. For each version that you receive, you can get properties to display or a VSSItem to act on.

Both of these methods are described in more detail later in this article

The VSSVersion Object and IVSSVersion

When you want to iterate through all the versions of a file or project, you use the Versions property of that file or project to get a VSSVersion object. You use that object to iterate through a series of VSSVersion objects, each of which has enough properties to display version information to the user.**Using this object, therefore, you could create a display of the entire history of an object.

However, this object has no methods. If you want to Get an old version, you use the VSSItem property to get a VSSItem that represents that particular version*,* and then call the Get method on that item.

Version properties

HRESULT Username ([out,retval]BSTR *pUsername);
  • Read-only property.
  • This function gives you the name of the user who took the action for this version.
HRESULT VersionNumber ([out,retval]long *piVersion);
  • Read-only property.
  • This function gives you the version number for this version.
HRESULT Action ([out,retval]BSTR *pAction);
  • Read-only property.

  • This function gives you a string representing the action that caused this version, such as "Checked Into $/A".

    This is the key variable for understanding what this particular version means. It is also the variable that you would use to recreate the Visual SourceSafe history display in your application. Note, however, that this string does not always give you enough information. When you are enumerating the versions of a project, you may get the string above, "Checked Into $/A". How do you know which file was checked in? The only way to get this information is to use the VSSItem property of the Version object to retrieve a VSSItem that represents that version of that file*,* and then get the Spec or Name property of the VSSItem to find the file.

HRESULT Date ([out,retval]DATE *pDate);
  • Read-only property.
  • This function gives you the date/time that that this version was created.
HRESULT Comment ([out,retval]BSTR *pComment);
  • Read-only property.
  • This function gives you the comment left by the user who took the action for this version.
HRESULT Label ([out,retval]BSTR *pLabel);
  • Read-only property.
  • This function gives you the label associated with this version. Note that in Visual SourceSafe, any version of any file or project may be associated with a label. However, the most common scenario is that a new version entry in a project is created simply as a placeholder for the label.
HRESULT VSSItem ([out,retval]IVSSItem **ppIItem);
  • Read-only property.
  • This function gives you the VSSItem for this particular version. You can then call methods such as Get on that VSSItem.

The VSSVersions Object and IVSSVersions

The VSSVersions object is a collection of all the versions of a given file or project. However, it is a very odd collection object in a number of ways.

The Count property and Item method are not implemented in this collection. The only way to use this collection is to call the _NewEnum method (in C++), or use a for-each loop (in Visual Basic).

You cannot have more than one Versions enumerator open at once. This means that the Clone method is not implemented on the Versions enumerator. More importantly, it means that if you want to get the recursive history of a project, you should allow Visual SourceSafe to do it for you (by using the VSSFLAG_RECURSYES flag on the Versions property when you first get the collection), rather than doing the recursion manually.

There is one further important note: not all the versions that you get are actually versions of the object you started with.

If you are doing a history of $/A/FU.HTML, most of the versions you see will refer to that particular file. However, some of the versions you see will be labels that were applied to the project (not to the file). If you get back the label "2.00b," and then get a VSSItem for that version, you may be getting an item that represents FU.HTML (if the label was applied to the file), or an item that represents $/A (if the label was applied to the project).

Similarly, if you are doing a history of $/A, you will see all the checkins of FU.HTML. So, once again, although you started with one thing (the project), some of your versions refer to a different thing (the file).

So, here is a wrong way to use this interface. You want to find FU.HTML as it was on 10/27/93. You get a Versions collection and work your way through it until the date reaches that point; then you get a VSSItem from that version and call the Get method. You may retrieve exactly what you wanted; or, you may be very surprised to discover that you have retrieved the entire project!

The right way to do that sort of operation is to use the Version method, not the collection. When you ask for the version of FU.HTML with a particular date or label, call Version on FU.HTML, passing in that date or label, and then call Get on that*.* The Version method, unlike the Versions collection, will always return a version of the item you started with.

IVSSVersions methods

HRESULT _NewEnum ([out,retval]IUnknown **ppIEnum);

Returns an enumerator (an IEnumVariant) that supports the standard Next, Skip, and Reset methods (but notClone, since you can only have one such enumerator open at a time). This method is used explicitly by Visual C++ programmers; Visual Basic programmers do not call it directly, but use the for-each loop, which relies on the enumerator.

The VSSCheckout Object and IVSSCheckout

Each VSSCheckout on a file represents one checkout record for that file. Hence, if the file is checked out five times, you will iterate through five such records in the VSSCheckouts collection. Each record contains information about that particular checkout in the form of seven properties.

IVSSCheckout properties

HRESULT Username ([out,retval]BSTR *pUsername);
  • Read-only property.
  • The user who checked out the file.
HRESULT Date ([out,retval]DATE *pDate);
  • Read-only property.
  • The date/time of the file checkout.
HRESULT LocalSpec ([out,retval]BSTR *pLocal);
  • Read-only property.
  • The file name, with directory path, that the file was checked out to.
HRESULT Machine ([out,retval]BSTR *pMachine);
  • Read-only property.
  • The machine name that the file was checked out to.
HRESULT Project ([out,retval]BSTR *pProject);
  • Read-only property.
  • The project that the file was checked out from. This is relevant in the case of shared files. The file is actually checked out in all the projects that share it; however, this property tells you which project the user was working in when he checked the file out.
HRESULT Comment ([out,retval]BSTR *pComment);
  • Read-only property.
  • The checkout comment left by the user.
HRESULT VersionNumber ([out,retval]long *piVersion);
  • Read-only property.
  • The version number that the user checked out. Note that if this does not equal the current version number of the file, a merge will be necessary before the file can be checked in.

The VSSCheckouts Object and IVSSCheckouts

When you want very basic checkout information for a file, use the IsCheckedOut property. But when you want details, get a VSSCheckouts collection and use it to iterate all the checkouts on that file. You should note that the collection may potentially contain no items (the file is not checked out), one item (the file is checked out), or many items (the file is checked out multiple**times*)*, and that merges will automatically be done on check in).

The VSSCheckouts supports the standard properties and methods of a collection object.

IVSSCheckouts properties

HRESULT Count ([out,retval]long *piCount);
  • Read-only property.
  • The number of times that this file is currently checked out.

IVSSCheckouts methods

HRESULT Item ([in]VARIANT sItem, [out,retval]IVSSCheckout **ppICheckout);

Retrieves one particular checkout record. The item may be specified in one of two ways: either by using an integer (which represents a one-based index into the collection) or by naming a user name (which returns the checkout record, if any, for that particular user).

HRESULT _NewEnum ([out,retval]IUnknown **ppIEnum);

Returns an enumerator (an IEnumVariant) that supports the standard Next, Skip, Reset, and Clone methods. This method is used explicitly by Visual C++ programmers; Visual Basic programmers do not call it directly, but use the for-each loop, which relies on the enumerator.

Visual SourceSafe Events (the IVSSEvents Interface)

The section Hooking Up to Receive Visual SourceSafe Events describes the steps you go through to register your add-in with Visual SourceSafe. When the process ends, Visual SourceSafe has found your IVSSEvents interface and is ready to start calling you.

Visual SourceSafe exposes events for a few basic commands to Visual SourceSafe add-ins. All commands described by events are taking place on an individual file, so if a user checks out a project, a separate event will be generated for each file in the project.

The Before event indicates that the user is about to take an action. It is your opportunity to prevent the command from occurring by setting the *pbContinue variable to FALSE (0). As a rule of thumb, you should use the Before event only for the purpose of possibly preventing the command. You should definitely not take any actions that assume the command will occur, because it is possible that another event after you will actually prevent the command.

The After event indicates that the user just took an action. This is when it is appropriate for your program to do things that should be synchronized with the user action.

Note that there is no way for your add-in to tell from where the command is coming. For instance, if you discover that a file is about to be added to Visual SourceSafe, it may be coming from the Visual SourceSafe Explorer, from an integrated development environment (IDE), or even from a Visual SourceSafe add-in using the OLE Automation interface—including your own program! However, it is definitely not coming from the Visual SourceSafe command line because the Visual SourceSafe command line (in version 5.0) does not send events. This unfortunately means that it is possible for a command to happen without your add-in ever getting notified.

HRESULT BeforeAdd ([in]IVSSItem *piPrj, [in]BSTR Local, [out,retval]boolean *pbContinue);

The specified file (Local) is about to be added to the Visual SourceSafe project piPrj.

HRESULT AfterAdd ([in]IVSSItem *pIItem, [in]BSTR Local);

A new file has been added to Visual SourceSafe. *piItem is a VSSItem pointer for the newly created file.

HRESULT BeforeCheckout ([in]IVSSItem *pIItem, [in] BSTR Local, [out,retval]boolean *pbContinue);

The specified Visual SourceSafe file is about to be checked out to the specified local file name.

HRESULT AfterCheckout ([in]IVSSItem *pIItem, [in] BSTR Local);

The specified Visual SourceSafe file has been checked out to the specified local file name.

HRESULT BeforeCheckin ([in]IVSSItem *pIItem, [in] BSTR Local, [out,retval]boolean *pbContinue);

The specified Visual SourceSafe file is about to be checked in from the specified local file name.

HRESULT AfterCheckin ([in]IVSSItem *pIItem, [in] BSTR Local);

The specified Visual SourceSafe file has been checked in from the specified local file name.

HRESULT BeforeUndoCheckout ([in]IVSSItem *pIItem, [in] BSTR Local, [out,retval]boolean *pbContinue);

The specified Visual SourceSafe file is about to be unchecked out with the specified local file name.

HRESULT AfterUndoCheckout ([in]IVSSItem *pIItem, [in] BSTR Local);

The specified Visual SourceSafe file has been unchecked out with the specified local file name.

HRESULT BeforeRename ([in]IVSSItem *piItem, [in]BSTR NewName, [out,retval]boolean *pbContinue);

The specified file or project (*piItem ) is about to be renamed to the specified new name.

HRESULT AfterRename ([in]IVSSItem *pIItem, [in]BSTR OldName);

The specified file or project (*piItem ) has been renamed from the specified old name.

HRESULT BeforeBranch ([in]IVSSItem *piItem, [out,retval]boolean *pbContinue);

The specified file is about to be branched.

HRESULT AfterBranch ([in]IVSSItem *pIItem);

The specified file has been branched.

HRESULT BeforeEvent ([in]long iEvent, [in]IVSSItem *piItem, [in]BSTR Str, [in]VARIANT var, [out,retval]boolean *pbContinue);
HRESULT AfterEvent ([in]long iEvent, [in]IVSSItem *piItem, [in]BSTR Str, [in]VARIANT var);

These two events are included as part of the interface definition to allow for some expansion in the future without redefining the interface. If future events are added to the IVSSEvents interface, it will be done by calling BeforeEvent and AfterEvent, with the iEvent parameter designating the specific event, *piItem the file or project, and Str and var filling in any additional information required for the particular event. As of the writing of this spec, no iEvent codes are defined and these events are not used.

Error Handling

There are two kinds of errors that can occur during the running of a Visual SourceSafe command: errors and yes/no questions. Because the Visual SourceSafe OLE Automation calls have no user interface component, neither of these can be directly shown to the user. Errors may be returned to the caller as indications that a function call failed. Questions are answered by default; your calling application never even knows the question happened.

Errors

When you get an HRESULT back from a Visual SourceSafe method, you can use the standard OLE FAILED macro on that HRESULT to see if it indicates a failure. FAILED will tell you if an error occurred that was so severe that it prevented the method from completing.

The following is a list of all the errors that may be returned by Visual SourceSafe OLE Automation. Please note that your application can call the OLE function GetErrorInfo to get an IErrorInfo pointer and use that standard interface to get more information about any returned errors (such as a string to display to the user and on-line help information to display more details). The strings given here are the U.S. English versions and are listed so that you know the meaning of the errors. If you want to display an error, you should always use GetErrorInfo rather than hard-coding these strings.

Numerical values for these errors are in a file called SSAUTERR.H.

Error Code English String
ESS_CORRUPT File %s may be corrupt.
ESS_DT_BADDATESTR Invalid date string: %s
ESS_DT_INVALID Invalid time or date string.
ESS_NOMORE_HANDLES Too many file handles open.
ESS_FILE_ACCESSDENIED Access to file %s denied.
ESS_FILE_BADDRIVE Invalid drive: %s
ESS_FILE_BADHANDLE Invalid handle.
ESS_FILE_BADNAME Invalid file name: %s
ESS_FILE_BADPARAM Invalid access code (bad parameter).
ESS_FILE_BADPATH Invalid DOS path: %s
ESS_FILE_CURRENTDIR Folder %s is in use.
ESS_FILE_DISKFULL Disk full.
ESS_FILE_EXISTS File %s already exists.
ESS_FILE_LOCKED File %s is locked.
ESS_FILE_NOTFOUND File %s not found.
ESS_FILE_READ Error reading from file.
ESS_FILE_SHARE File %s is already open.
ESS_FILE_TOOMANY Too many file handles open.
ESS_FILE_VOLNOTSAME Cannot rename to another volume.
ESS_FILE_WRITE Error writing to file.
ESS_INI_BADBOOL Initialization variable %s must be set to Yes or No.
ESS_INI_BADLINE Invalid syntax on line %d of file %s.
ESS_INI_BADNUMBER Initialization variable %s set to invalid number.
ESS_INI_BADPATH Initialization variable %s set to invalid path.
ESS_INI_BADVALUE Initialization variable %s set to invalid value.
ESS_INI_NOSUCHVAR Cannot find initialization variable %s.
ESS_INI_NUMRANGE Initialization variable %s must be between %d and %d
ESS_INI_TOO_MANY_ENV Too many SS.INI environment strings.
ESS_LOCK_TIMEOUT Timeout locking file: %s
ESS_MEM_NOMEMORY Out of memory.
ESS_NO_TWEAK_CHKDOUT You cannot modify the properties of a file that is checked out.
ESS_NOMERGE_BIN_NODELTA You cannot perform a merge on a binary file or a file that stores latest version only.
ESS_NOMULTI_BINARY Cannot check out %s. It is binary and is already checked out.
ESS_NOMULTI_NODELTA %s stores only the latest version and is already checked out.
ESS_OS_NOT_EXE Error executing: %s
ESS_SS_ADDPRJASSOCFILE %s is a Visual SourceSafe configuration file and cannot be added.
ESS_SS_ADMIN_LOCKOUT The Visual SourceSafe database has been locked by the Administrator.
ESS_SS_BADRENAME Unable to rename %s to %s.
ESS_SS_CANT_FIND_SSINI Cannot find SS.INI file for user %s.
ESS_SS_CHECKED_OUT File %s is currently checked out by %s.
ESS_SS_CHECKED_OUT_YOU You currently have file %s checked out.
ESS_SS_CHECKOUT_OLD Cannot check out an old version of a file.
ESS_SS_CHKOUT_USER File %s is currently checked out by %s.
ESS_SS_CONFLICTS An automatic merge has occurred and there are conflicts. \nEdit %s to resolve them.
ESS_SS_DEL_ROOT Cannot delete the root project.
ESS_SS_DEL_SHARED A deleted link to %s already exists.
ESS_SS_FILE_NOTFOUND File %s not found.
ESS_SS_HISTOPEN A history operation is already in progress.
ESS_SS_INSUFRIGHTS You do not have access rights to %s.
ESS_SS_LATERCHKEDOUT A more recent version is checked out.
ESS_SS_LOCALRW A writable copy of %s already exists.
ESS_SS_MOVE_CHANGENAME Move does not change the name of a project.
ESS_SS_MOVE_NOPARENT Project %s does not exist.
ESS_SS_MOVE_ROOT Cannot move the root project.
ESS_SS_MUST_USE_VERS Cannot roll back to the most recent version of %s.
ESS_SS_NOCOMMANCESTOR Files have no common ancestor.
ESS_SS_NOCONFLICTS2 %s has been merged with no conflicts.
ESS_SS_NODOLLAR File %s is invalid. Files may not begin with $.
ESS_SS_NOT_CHKEDOUT File %s is not checked out.
ESS_SS_NOT_SHARED File %s is not shared by any other projects.
ESS_SS_NOTSEPARATED Files are not branched.
ESS_SS_OPEN_LOGGIN Unable to open user login file %s.
ESS_SS_PATHTOOLONG Path %s too long.
ESS_SS_RENAME_MOVE Rename does not move an item to another project.
ESS_SS_RENAME_ROOT Cannot rename the root project.
ESS_SS_ROLLBACK_NOTOLD Cannot rollback to the most recent version of %s.
ESS_SS_SHARE_ANCESTOR A project cannot be shared under a descendant.
ESS_SS_SHARED File %s is already shared by this project.
ESS_SSPEC_SYNTAX Invalid Visual SourceSafe syntax: %s.
ESS_UM_BAD_CHAR Bad user name syntax: %s.
ESS_UM_BAD_PASSWORD Invalid password.
ESS_UM_BADVERSION Incompatible database version.
ESS_UM_DEL_ADMIN Cannot delete the Admin user.
ESS_UM_PERM_DENIED Permission denied.
ESS_UM_RENAME_ADMIN Can not rename the Admin user.
ESS_UM_TOO_LONG User name too long.
ESS_UM_USER_EXISTS User %s already exists.
ESS_UM_USER_NOT_FOUND User %s not found.
ESS_URL_BADPATH The URL for project %s was not set properly.
ESS_VS_CHECKED_OUT File %s checked out.
ESS_VS_CHILD_NOT_FOUND Subproject or file not found.
ESS_VS_COLLISION Collision accessing database, please try again.
ESS_VS_EXCLUSIVE_CHECKED_OUT File %s is exclusively checked out.
ESS_VS_ITEMEXISTS An item with the name %s already exists.
ESS_VS_LONGNAME %s is an invalid %s name.
ESS_VS_MOVE_CYCLE You can not move a project under itself.
ESS_VS_NO_DELTA File %s does not retain old versions of itself.
ESS_VS_NOT_CHECKED_OUT File %s cannot be checked into this project.
ESS_VS_NOT_FOUND File or project not found.
ESS_VS_PARENT_NOT_FOUND Parent not found.
ESS_VS_VERS_NOT_FOUND Version not found.
ESS_VS_WANT_FILE This command only works on files.
ESS_VS_WANT_PRJ This command only works on projects.
ESS_URL_BUFOVERFLOW A link in %s was ignored because it was longer than Visual SourceSafe can understand.
ESS_URL_CANTCHECKHTML An error occurred while trying to check hyperlinks for %s.
ESS_SS_ADDINFAILED Error loading Visual SourceSafe add-in: %s.
ESS_CANCEL  
ESS_LOADSTRING_FAILED Error loading resource string.

Questions

The following questions can occur during the invocation of a Visual SourceSafe method. These questions will be answered automatically as indicated—your application will never know they happened. They are listed here, not because you can do anything about them, but so that you know what to expect when you call Visual SourceSafe.

As an example, consider the following scenario: the user deletes the FU.CPP file in a project, and then attempts to re-add the same file (same name and same contents) to the same project. In the Visual SourceSafe Explorer, when the user attempts to add the file, he will be asked if he wants simply to recover the previously deleted file. When you call the Add method in the Visual SourceSafe OLE Automation, the answer to that question (the first one listed in the table below) is automatically yes; that is, the deleted file is recovered.

Question Answer
A deleted copy of this %s file already exists in this project.

Do you want to recover the existing file?

Yes
Folder %s not found. Create? Yes
Have any conflicts in %s been properly resolved? Yes
File %s is currently checked out by %s. Proceed anyway? Yes
File %s was checked out to folder %s. Proceed in %s? Yes
File %s is checked out to project %s, and you are in %s.

Proceed anyway?

Yes
File %s is currently checked out by %s. Delete anyway? Yes
You currently have file %s checked out. Delete anyway? Yes
An item named %s was already deleted from this project.

Purge the old item and delete this one now?

Yes
This version of %s already has a label. Overwrite? Yes
The label %s is already used. Remove the old label? Yes
%s has been merged with no conflicts. Check in now? Yes
Redo the automatic merge? Yes
Delete local file: %s? Yes
%s is already checked out. Continue? Yes
File %s has been destroyed, and cannot be rebuilt. Continue anyway? Yes
Project $%s has been destroyed and cannot be rebuilt.

Continue anyway?

Yes
$%s was moved out of this project and cannot be rebuilt.

Continue anyway?

Yes
%s has changed. Undo check out and lose changes? Yes
A deleted file of the same name already exists in this Visual SourceSafe project.

Do you want to recover the deleted file instead of adding your local %s?

No
%s is writable. Replace? No
%s is checked out. Replace? No