5,666,979 members and growing! (15,416 online)
Email Password   helpLost your password?
General Programming » Programming Tips » General     Intermediate

VSS: protocol handler for Visual SourceSafe

By Victor Vogelpoel

This article describes how to hook up a protocol, in the example "vss:", to a custom handler to open a document from a Visual SourceSafe repository using automation.
VC6, C++, HTMLWindows, NT4, Win2KIE 6.0, VS6, Visual Studio, IE, Dev

Posted: 27 Jun 2001
Updated: 27 Jun 2001
Views: 101,711
Bookmarked: 69 times
Announcements
Loading...



Search    
Advanced Search
Sitemap
26 votes for this Article.
Popularity: 6.33 Rating: 4.47 out of 5
2 votes, 20.0%
1
0 votes, 0.0%
2
0 votes, 0.0%
3
2 votes, 20.0%
4
6 votes, 60.0%
5

Sample Image - vssprotocolhandler.gif

Introduction

Imagine that you could open a document in a Visual SourceSafe database with one mouse click.

No more opening the SourceSafe client, browsing to the correct project tree and choosing 'view latest version' from the document's context menu. In the screenshot above, you see three links to project documentation in a Word document; the links have the new VSS: protocol prefix. Clicking a VSS: link will fire up the handler presented below and open the designated document using shell association (.doc -> Word). In other words: "VSS://$/Projects/VSS ProtHandler/TD-VSSProtocolHandler.doc" will start Visual SourceSafe, find the TD-VSSProtocolHandler.doc in project "$/Projects/VSS ProtHandler" and attempt to open it using "shellexec". This is all done using Automation, you won't see the SourceSafe client at all!

You are of course familiar with the use of HTTP:, FTP: and other protocols. The VSS: protocol can be used in almost the same manner in a (intranet) webpage, on the Windows Run box, in a Word document or in a shortcut. All protocol requests are handled by URL.dll (a DLL installed by Internet Explorer). URL.dll will attempt to find a registration for the protocol and call the registered handler. For HTTP: it is most likely Internet Explorer, for VSS: it is VSSProtocolHandler.exe.

Please note that the user must have the SourceSafe client installed (to use its automation objects) and his/her machine must be able to reach the VSS database.

This article presents two principles:

  • Registering & hooking up the "VSS:" protocol to the handler VSSProtocolHandler.exe,
  • Handling the protocol request: letting the handler open the indicated document on the protocol line from the Visual SourceSafe database, using automation.

Registering & hooking the "VSS:" protocol

Actually, the "VSS:" protocol is similar to the "MAILTO:" protocol. Researching this protocol is how I found out how to hook the protocol! A few lines in the registry and the protocol is registered.

[HKEY_CLASSES_ROOT]
  [vss]
    (Default) = "URL:VSS Protocol"
    URL Protocol = ""
    [DefaultIcon]
      (Default) = "VSSProtocolHandler.exe"
    [shell]
      [open]
        [command]
          (Default) = "c:\whatever\VSSProtocolHandler.exe "%1""

The secret is the string entry URL Protocol with an empty string. Now URL.dll recognizes "VSS" as a protocol.

Handling the protocol request

When the hook is registered, URL.dll will call VSSProtocolHandler.exe whenever it encounters "VSS:xxxx". The argument must be parsed by the handler.

VSS://$/Projects/VSS ProtHandler/TD-VSSProtocolHandler.doc

will eventually yield the next command line:

c:\whatever\VSSProtocolHandler.exe 
    "VSS://$/Projects/VSS ProtHandler/TD-VSSProtocolHandler.doc"

Note that the protocol prefix is still present in the argument. The handler needs to parse it and check for the "VSS:" or "VSS://" prefix. The remaining part of the argument is used to look up the document in the SourceSafe database.

The source archive contains a full Visual C++ 6 project for the handler. It's plain Win32 & Automation (no MFC). Note that the project settings automatically register the "VSS:" protocol once the project has compiled successfully, just like a COM component project does.

The code was developed on Windows 2000 and only tested on Windows 2000.

The first part is parsing the argument:

//////////////////////////////////////////////////////

// WinMain

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{

    char szArgument[1024] = {0};
    LPSTR pszVSSFile = NULL;

    // Use a copy of the argument

    StrNCpy(szArgument, lpCmdLine, 1023);

    // Remove the quote from the string...

    PathUnquoteSpaces(szArgument);

    char *pszTemp = szArgument;
    while (*pszTemp)
    {
        if (*pszTemp == '\\')
            *pszTemp = '/';
        pszTemp++;
    }

    if (strlen(szArgument) == 0)
    {
        ReportError(pszErrorNoArgumentSpecified);
        exit(1);
    }
    // Check if the vss protocol string is present

    else if (_strnicmp(szArgument, "vss://$/", 8) != 0)
    {
        // Check if the slashslash is missing... that's ok too

        if (_strnicmp(szArgument, "vss:$/", 6) != 0)
        {
            // Check for "/register".

            if (_strnicmp(szArgument, "/register", 9) == 0)
            {
                RegisterVSSProtocol(_strnicmp(szArgument, 
                    "/registerq", 10) == 0? true: false);
                exit(0);
            }
            else if (_strnicmp(szArgument, "/?", 2) == 0)
            {
                ShowHelp();
                exit(0);
            }
            else
            {
                ReportError(pszErrorProtocolInvalidArgument, 
                                        szArgument, pszAbout);
                exit(1);
            }
        }
        else
            pszVSSFile = szArgument + 4;    // Point past the protocol

    }
    else
        pszVSSFile = szArgument + 6;        // Point past the protocol



    if (strlen(pszVSSFile) == 0)
    {
        MessageBox(NULL, pszErrorNoVSSFileSpecified, 
                               pszAppName, MB_ICONERROR);
        return 1;
    }

Next is firing up COM, instantiating a Visual SourceSafe Object instance, finding the current SourceSafe database in the registry and calling ViewVSSFile() to get the intended file:

    // Init COM Libraries

    HRESULT hr;
    if (FAILED(hr=CoInitialize(NULL)))
    {
        ReportError(pszErrorFailedToInitCOM, hr);
        exit(1);
    }

    CLSID clsid;

    // Create SourceSafe Automation object.

    if(SUCCEEDED(hr=CLSIDFromProgID(L"SourceSafe", &clsid)))
    {
        IVSSDatabase *pVSSDBObject = NULL;
        if (SUCCEEDED(hr=CoCreateInstance(clsid, NULL, 
          CLSCTX_ALL, IID_IVSSDatabase, (void**)&pVSSDBObject)))
        {
            long    lErr = 0;
            HKEY    hKey;
            if ((lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                       _T("SOFTWARE\\Microsoft\\SourceSafe"),
                       0,
                       KEY_READ,
                       &hKey)) == ERROR_SUCCESS)
            {
                DWORD   dwType = REG_SZ;
                char    szVSSDatabasePath[512] = {0};
                DWORD   dwSize = 511;

                if ((lErr = RegQueryValueEx(hKey,
                            _T("Current Database"),
                            NULL,
                            &dwType,
                            (LPBYTE)szVSSDatabasePath,
                            &dwSize)) == ERROR_SUCCESS && dwType == REG_SZ)
                {

                    if (!ViewVSSFile(pVSSDBObject, 
                        szVSSDatabasePath, pszVSSFile))
                    {
                        // Opening the file from the

                        // current VSS database FAILED.

                        // Now try the other known databases.

                        // Exercise for the reader...



                    }
                }
                else
                    ReportError(pszErrorFindingVSSCurrentDatabase);

                RegCloseKey(hKey);
            }
            else
                ReportError(pszErrorOpeningVSSRegKey, lErr);

            pVSSDBObject->Release();
        }
        else
            ReportError(pszErrorCreatingVSSAutomationObject, hr);
    }
    else
        ReportError(pszErrorFindingVSSAutomationObject, hr);

    CoUninitialize();

ViewVSSFile() will open the designated database (srcsafe.ini), look up the designated item (file), test it if it is a file, which is not marked 'Deleted', and performs a 'Get' command on it, resulting in a file in the Temporary directory. This file is executed using ShellExecuteW (UNICODE version because the arguments (widestrings) are UNICODE and I was lazy at the time).

bool ViewVSSFile(IVSSDatabase *pVSSDBObject, 
       char *pszVSSDatabasePath, char *pszVSSFile)
{
  bool    bViewSucceeded = false;
  HRESULT hr = S_OK;
  // Note: pszDatabasePath is a BUFFER with sufficient space...


  PathRemoveBackslash(pszVSSDatabasePath);
  strcat(pszVSSDatabasePath, "\\srcsafe.ini");
  CComBSTR bsVSSIniFile(pszVSSDatabasePath);

  // Open the sourcesafe database under the current

  // logged on username and (cached) password

  // by entering empty strings.

  if(SUCCEEDED(hr=pVSSDBObject->Open(bsVSSIniFile, L"", L"")))
  {
    IVSSItem *pIVSSItem = NULL;
    // bsVSSFile("$/Courses/Cursus evaluatie.doc");

    CComBSTR bsVSSFile(pszVSSFile);

    //MessageBox(NULL, "DATABASE IS OPEN!", 

                           pszAppName, MB_ICONINFORMATION);
    if(SUCCEEDED(hr=pVSSDBObject->get_VSSItem(bsVSSFile, 
                                                0, &pIVSSItem)))
    {
      int nItemType = -1;
      // Test if the VSS item is a file and not a project.

      if (SUCCEEDED(hr = pIVSSItem->get_Type(&nItemType)))
      {
        if (nItemType == VSSITEM_FILE)
        {
          VARIANT_BOOL vbDeleted;
          if (SUCCEEDED(hr = 
            pIVSSItem->get_Deleted(&vbDeleted)))
          {
            if (vbDeleted == VARIANT_FALSE)
            {
              // Get the filename from

              // the specified SourceSafe filepath.

              char *pszTargetFileName= 
                  StrRChrA(pszVSSFile, NULL, '/');
              pszTargetFileName++;

              char szTempFile[_MAX_PATH] = {0};

              if (GetTempPath(_MAX_PATH, szTempFile) > 0)
              {
                StrCat(szTempFile, pszTargetFileName);

                CComBSTR bsLocal(szTempFile);

                if (SUCCEEDED(hr=pIVSSItem->Get(&bsLocal, 
                                         VSSFLAG_REPREPLACE))) 
                // VSSFLAG_USERROYES | VSSFLAG_REPREPLACE)))

                {
                  // bsLocal now contains a path

                  // to a file in the temp folder,

                  // which is the extracted file.

                  int hInst = (int)ShellExecuteW(NULL, L"open", 
                    (BSTR)bsLocal, NULL, NULL, SW_SHOWMAXIMIZED);
                  if (hInst <= 32)
                    ReportError(pszErrorOpeningTempDocument, 
                               hInst, szTempFile, pszVSSFile);
                  else
                    bViewSucceeded = true;
                }
                else
                  ReportError(pszErrorGettingVSSDocument, hr, 
                    pszVSSFile, pszVSSDatabasePath, szTempFile);
              }
              else
                ReportError(pszErrorCantDetermineTempDir);
            }
            else
              ReportError(pszErrorVSSFileMarkedDeleted, pszVSSFile);
          }
          else
            ReportError(pszErrorRetrievingVSSFileDeletedState, hr);
        }
        else
          ReportError(pszErrorVSSItemIsNotAFile, pszVSSFile);
      }
      else
        ReportError(pszErrorRetrievingVSSFileType, hr);

      pIVSSItem->Release();
    }
    else
      ReportError(pszErrorFindingVSSDocument, 
              hr, pszVSSFile, pszVSSDatabasePath);
  }
  else
    ReportError(pszErrorOpeningVSSDatabase, hr, pszVSSDatabasePath);

  return bViewSucceeded;
}

There is some extra code to handle registration of the protocol and handle error messaging; they're pretty straight forward.

Demo executable

The demo archive contains the (release version) executable of the project. Extract it somewhere and run it through the Windows Run box (Start | Run) using argument "/register", e.g.: "C:\path\VSSProtocolHandler.exe /register". The handler will register the "VSS:" protocol and set itself up to be the handler. You are now ready to use the protocol. You may want to hardcode your SourceSafe username and password in VSS Open command (pVSSDBObject->Open(bsVSSIniFile, L"", L"")) instead of the empty strings. Empty strings make SourceSafe use the current Windows username and cached password. You must also setup an argument line ("VSS://$/path_to_existing_file_in_VSS_database") in the project's Settings "Debug" tab before debugging.

Further

This project was a 'proof-of-concept', and was never intended to be an idiot proof, user ready application. I won't be doing any more development on it, but I still have some ideas that you might find interesting to implement:

  • Better "logging on" code for the SourceSafe database. The current code assumes that you are using your Windows logon name to log on into the SourceSafe database. It also assumes that you have logged on to the database at least once; the logon/password is cached by Windows and is provided automatically in this case.
  • If one uses more than one SourceSafe database, and the document cannot be found in the current SourceSafe database, then the handler could open up the other databases until the file is found (or not). SourceSafe stores all used databases in the registry, so this shouldn't be hard to find.
    • Search for srcsafe.ini in the directory where ssapi.dll is located.
    • Search for srcsafe.ini in each directory of the path to ssapi.dll. In other words, if ssapi.dll is located in C:\Folder1\Folder2\Folder3\SSAPI.DLL, then Folder3, Folder2, Folder1 and C:\ are searched (in that order).
    • Search for srcsafe.ini in the location indicated by the named value "API Current Database" in the registry key HKEY_LOCAL_MACHINE\Software\Microsoft\SourceSafe.
    • Search for srcsafe.ini in the location indicated by the named value "SCCProviderPath" in the registry key HKEY_LOCAL_MACHINE\Software\Microsoft\SourceSafe.

By the way: MS Office applications do not recognize the "VSS:" protocol as a hyperlink and underline it automatically. You will have to use the "Insert Hyperlink" (CTRL+K) command to enter a Visual SourceSafe hyperlink.

An MSDN article describes the "Note:" protocol, which works in the same manner as the "VSS" protocol, but is hooked to notepad.exe. However, this example doesn't work, because notepad.exe receives "Note://file.ext" as argument, but can't handle the prefix "Note://" and fails to open the document.

References

  • Q167134 - HOWTO Open Visual SourceSafe to a Specific Project
  • Q169928 - HOWTO Open a SourceSafe Database with OLE Automation
  • Q175758 - HOWTO Trap Visual SourceSafe OLE Errors
  • Q176350 - HOWTO Open Visual SourceSafe to a Specific Database
  • Q201431 - HOWTO Write Automation for Visual SourceSafe 5_0-6_0
  • Microsoft Visual SourceSafe OLE Automation (Ken Felder, October 1995)
  • ssauterr - SourceSafe Automation errors
  • ssauto - Visual C++ Header File for Visual SourceSafe
  • Visual SourceSafe Frequently Asked Questions
  • vsstree - VSS OLE Sample Code Written in Visual C

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Victor Vogelpoel


Victor is consulting in The Netherlands.

His interests include Windows and web application development using .NET technologies and even some Apache/PHP/MySQL...
Occupation: Web Developer
Location: Netherlands Netherlands

Other popular Programming Tips articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 24 of 24 (Total in Forum: 24) (Refresh)FirstPrevNext
Generalgetting errormemberkishore_kd0:47 19 Aug '08  
GeneralRe: getting errormemberVictor Vogelpoel2:33 19 Aug '08  
GeneralWhen I insert hyperlink in word document as instructed with the VSS://$/projects/test.doc - doesn't open the documentmemberVasanth.vk6:35 29 Oct '04  
GeneralRe: When I insert hyperlink in word document as instructed with the VSS://$/projects/test.doc - doesn't open the documentmemberVictor Vogelpoel8:26 29 Oct '04  
GeneralRe: When I insert hyperlink in word document as instructed with the VSS://$/projects/test.doc - doesn't open the documentmemberVasanth.vk17:34 1 Nov '04  
Generalssauterr.hmemberdubh15:46 16 Aug '03  
GeneralRe: ssauterr.hmemberAshutosh R. Bhatikar22:16 20 Aug '03  
GeneralRe: ssauterr.hsussdubh@dubh.org6:32 21 Aug '03  
GeneralGreat stuffmemberAnthony_Yio20:13 27 Nov '02  
GeneralChanging Comment of a VSSVersion or VSSItemsussblair wagner9:17 30 Sep '02  
GeneralRe: Changing Comment of a VSSVersion or VSSItemmemberVictor Vogelpoel21:08 30 Sep '02  
GeneralRe: Changing Comment of a VSSVersion or VSSItemmemberChristian Ernst Rysgaard18:51 3 Feb '03  
GeneralRe: Changing Comment of a VSSVersion or VSSItemmemberPeter ritchie12:15 18 Feb '03  
GeneralUsing VSS file compare externally?sussFref19:27 11 Sep '02  
GeneralRe: Using VSS file compare externally?memberVictor Vogelpoel21:54 11 Sep '02  
Generalhow to use VSS ssapi.dll in ASPsussDuong Chi Nghia17:54 15 Jul '02  
GeneralRe: how to use VSS ssapi.dll in ASPmemberVictor Vogelpoel22:09 15 Jul '02  
GeneralRe: how to use VSS ssapi.dll in ASPsussSenKumarkathiresan23:05 25 Jan '03  
GeneralRe: how to use VSS ssapi.dll in ASPmemberVictor Vogelpoel13:14 26 Jan '03  
Generaljust one questionmemberUltras4:43 23 Jan '02  
GeneralRe: just one questionmemberVictorV5:14 23 Jan '02  
GeneralExtension ideamemberVictor Vogelpoel22:23 10 Jul '01  
GeneralVSSmemberAnonymous17:34 3 Jul '01  
GeneralthanksmemberAnonymous2:27 30 Jun '01  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 27 Jun 2001
Editor: Smitha Vijayan
Copyright 2001 by Victor Vogelpoel
Everything else Copyright © CodeProject, 1999-2008
Web17 | Advertise on the Code Project