65.9K
CodeProject 正在变化。 阅读更多。
Home

SSBrowser:一个示例 Visual SourceSafe 自动化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.59/5 (12投票s)

2004年6月4日

4分钟阅读

viewsIcon

95042

downloadIcon

3030

一个用于在您的应用程序中执行 Visual SourceSafe 操作的 VC++ 示例程序。

Sample Image - SSBrowser.jpg

引言

几年前,我有一个项目需要自动化我们团队的构建过程,其中涉及多个 SourceSafe 操作。我徒劳地寻找好的示例程序,但没有一个够全面;而且我找到的大多数指南和示例都是用 Visual Basic 写的。因此,这个示例项目可以说是 VC++ 代码如何与 SourceSafe 操作交互的存储库,具体包括:

  • 连接到 SourceSafe 数据库
  • 操作,例如 CheckIn、CheckOut、Undo Checkout、Label、添加项目、文件等。
  • 遍历 SourceSafe 存储库并将其显示在树形控件中
  • 获取文件历史记录列表并从中检索版本

我最近在 CodeProject 上看到了更多关于 SourceSafe 的 示例和文章,但我仍然会分享这篇文章。谁知道呢,也许它也能帮助其他人。

注意事项

提供的示例程序仅仅是为了演示您可以使用 SourceSafe 自动化完成什么。为了简洁起见,忽略了大量的错误检查。作者不对运行程序或使用示例程序中的代码造成的任何数据丢失负责。

关注点

作为一篇示例应用程序文章,我相信源代码本身就能说明问题。但我将重点介绍 Visual C++ 中 SourceSafe 自动化的几个方面。

与 Visual SourceSafe 交互

有两种方法可以与 VSS 交互。

1. 类型库 - 通过 SSAPI.DLL 导入

这是通过生成和使用 ssapi.tlhssapi.tli 来完成的。它的优点是已经声明了智能指针,并提供了错误处理的包装方法(抛出 COM 错误 HRESULT,而非 S_OK)。但是,此方法的可移植性较差,因为它需要 SSAPI.dll 的路径。例如:这可能在您的 stdafx.h 中。

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

// Please edit according to your system's VSS installation

//

#import "C:\Program Files\DevStudio\Vss\win32\ssapi.dll" no_namespace
///////////////////////////////////////////////////////////

创建 SS 数据库连接如下:

// Visual SourceSafe Database

IVSSDatabasePtr mp_vssDatabase;

mp_vssDatabase.CreateInstance(__uuidof(VSSDatabase);
mp_vssDatabase->Open(psz_VSSini, psz_User, psz_Password);

注意使用 ssauto.h 时的区别。

2. 原始 COM 接口 - Microsoft 提供的 ssauto.h

该文件可在 MSDN 网站上找到。该头文件仅包含原始 COM 接口类和定义的常量。如果您想使用智能指针,则需要自己定义它们。附带的示例项目正是这样做的。我在 stdafx.h 中进行了如下定义。

// Smart pointer typedef declarations

//

#include "ssauto.h"


extern "C" const GUID __declspec(selectany) LIBID_SourceSafeTypeLib =
    {0x783cd4e0,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) IID_IVSSItem =
    {0x783cd4e1,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) IID_IVSSVersions =
    {0x783cd4e7,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) IID_IVSSVersion =
    {0x783cd4e8,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) IID_IVSSItems =
    {0x783cd4e5,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) IID_IVSSCheckouts =
    {0x8903a770,0xf55f,0x11cf,{0x92,0x27,0x00,0xaa,0x00,0xa1,0xeb,0x95}};
extern "C" const GUID __declspec(selectany) IID_IVSSCheckout =
    {0x783cd4e6,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) IID_IVSSDatabase =
    {0x783cd4e2,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) CLSID_VSSItem =
    {0x783cd4e3,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) CLSID_VSSVersion =
    {0x783cd4ec,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) CLSID_VSSDatabase =
    {0x783cd4e4,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) IID_IVSSEvents =
    {0x783cd4e9,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) IID_IVSS =
    {0x783cd4eb,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};
extern "C" const GUID __declspec(selectany) IID_IVSSEventHandler =
    {0x783cd4ea,0x9d54,0x11cf,{0xb8,0xee,0x00,0x60,0x8c,0xc9,0xa7,0x1f}};


_COM_SMARTPTR_TYPEDEF(IVSSItem, IID_IVSSItem);
_COM_SMARTPTR_TYPEDEF(IVSSVersions, IID_IVSSVersions);
_COM_SMARTPTR_TYPEDEF(IVSSVersion, IID_IVSSVersion);
_COM_SMARTPTR_TYPEDEF(IVSSItems, IID_IVSSItems);
_COM_SMARTPTR_TYPEDEF(IVSSCheckouts, IID_IVSSCheckouts);
_COM_SMARTPTR_TYPEDEF(IVSSCheckout, IID_IVSSCheckout);
_COM_SMARTPTR_TYPEDEF(IVSSDatabase, IID_IVSSDatabase);
_COM_SMARTPTR_TYPEDEF(IVSSEvents,IID_IVSSEvents);
_COM_SMARTPTR_TYPEDEF(IVSS, IID_IVSS);
_COM_SMARTPTR_TYPEDEF(IVSSEventHandler, IID_IVSSEventHandler);

创建 SS 数据库连接如下:

// Visual SourceSafe Database

IVSSDatabasePtr mp_vssDatabase;

CLSIDFromProgID(L"SourceSafe", &clsid ));
CoGetClassObject( clsid, CLSCTX_ALL, NULL, IID_IClassFactory, (void**)&pClf );
pClf->CreateInstance( NULL, IID_IVSSDatabase, (void **) &mp_vssDatabase );

mp_vssDatabase->Open((CComBSTR)psz_VSSini, 
  (CComBSTR)psz_User, (CComBSTR)psz_Password);

注意使用 ssapi.dll 时的区别。

遍历 Visual SourceSafe 数据库

示例中有一个树形控件 (CSSTreeCtrl),它提供了导航数据库的良好示例。该树加载速度很快,因为在初始化树时,它仅填充从根到默认项目(通过 VSSDatabase::get_CurrentProject 方法获取)的路径直接连接的节点。其他节点将在用户实际通过单击树控件图形打开它们时才加载。

下面的 h_InsertPath 方法展示了如何递归地将部分结构加载到树形控件中。deepestNodeFound 项稍后可用于选择/展开树。

/*****************************************************************
h_InsertPath - Insert a tree structure that represents
               the specified project path.

PARAMETERS:
    IVSSItemPtr vss_item - the sourcesafe item where to start traversing 
    HTREEITEM h_parent - the tree node where to insert 
                         the new tree structure (NULL for tree root)
    CString & rstr_traverseToPath - the path to the project to traverse
    HTREEITEM & rh_deepestNodeFound - (output) the deepest 
                                      node found after traversing the path

RETURNS:
    HTREEITEM - the tree item attached into the specified parent
******************************************************************/
HTREEITEM CSSTreeCtrl::h_InsertPath(IVSSItemPtr vss_item, HTREEITEM h_parent, 
    CString & rstr_traverseToPath, HTREEITEM & rh_deepestNodeFound)
{
    HTREEITEM h_item = NULL;
    try
    {
        CComBSTR str_name;
        v_TestHr(vss_item->get_Name(&str_name));
        CString str_Filename = static_cast<LPCTSTR>(str_name);
        if (h_parent == NULL)
        {
            str_Filename = gsz_root;
        }

        //Add the item into the tree node

        TV_INSERTSTRUCT tvis;
        ZeroMemory(&tvis, sizeof(TV_INSERTSTRUCT));
        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());

        IVSSItemsPtr vss_items;
        int i_type;
        long l_count, l_checkOut;
        vss_item->get_Type(&i_type);
        vss_item->get_IsCheckedOut(&l_checkOut);
        if (i_type == VSSITEM_FILE)
        {
            // Set image according to check out state

            if (l_checkOut == VSSFILE_NOTCHECKEDOUT)
            {
                tvis.item.iImage = ITEM_FILE_NORMAL_IMAGE;
                tvis.item.iSelectedImage = ITEM_FILE_NORMAL_IMAGE;
            }
            else
            {
                tvis.item.iImage = ITEM_FILE_CHECKOUT_IMAGE;
                tvis.item.iSelectedImage = ITEM_FILE_CHECKOUT_IMAGE;
            }
        }
        else if (i_type == VSSITEM_PROJECT)
        {
            vss_item->get_Items(_variant_t(false), &vss_items);
            tvis.item.iImage = ITEM_PROJECT_NORMAL_IMAGE;
            tvis.item.iSelectedImage = ITEM_PROJECT_SELECTED_IMAGE;
            vss_items->get_Count(&l_count);
            // Add (+) sign to indicate the node has child

            tvis.item.cChildren = (l_count > 0) ? 1:0;
        }
        h_item = InsertItem(&tvis);

        // If this node is within the path, consider it as the deepest node

        CString str_currentPath = str_ItemToPath(h_item);
        ATLTRACE(_T("%s\n"),str_currentPath);
        if (rstr_traverseToPath == str_currentPath)
        {
            rh_deepestNodeFound = h_item;
        }

        // If this node is not within the path, we're done

        if (h_parent && rstr_traverseToPath.Find(str_currentPath) != 0)
        {
            return h_item;
        }
        // Otherwise, recursively populate the node path

        if (i_type == VSSITEM_PROJECT)
        {
            for (long i = 0; i < l_count; i++)
            {
                IVSSItemPtr vss_ChildItem;
                vss_items->get_Item(_variant_t(i+1L), &vss_ChildItem);
                h_InsertPath(vss_ChildItem, h_item, 
                       rstr_traverseToPath, rh_deepestNodeFound);
            }
        }
    }
    catch (_com_error &e)
    {
        ATLTRACE(_T("COM error: %s\n"), e.ErrorMessage());
    }

    return h_item;
}

有关其余代码,请参阅示例项目中的 CSSTreeCtrl 源代码。

检索 SourceSafe 项目历史记录

项目的历史记录可通过 IVSSVersions 访问,它提供了一个接口到 IEnum,我们可以从中迭代每个历史记录项。

void v_DumpItemHistory(IVSSItemPtr vss_item)
{
    int i_type = 0;
    IVSSVersionsPtr versions;

    vss_item->get_Type(&i_type);
    // Get the IVSSVersions object of the item

    if (vss_item->get_Versions((i_type == VSSITEM_PROJECT)? 
             VSSFLAG_HISTIGNOREFILES:0, &versions) == S_OK)
    {
        CComPtr<IENUMVARIANT> p_versionsEnum;
        CComPtr<IUNKNOWN> spunk;

        versions->_NewEnum(&spunk);
        spunk->QueryInterface(IID_IEnumVARIANT, (LPVOID*)&p_versionsEnum);

        ULONG l_fetched;
        long l_value;
        CString str_display;
        CComBSTR bstr_text;
        DATE date;

        // We use this object to point to each version

        _variant_t object;

        // Now iterate through all the versions

        while (p_versionsEnum->Next(1, &object, &l_fetched) == S_OK)
        {
            try
            {
                // Treat the object as IVSSVersion

                IVSSVersionPtr p_version(object);

                // ...where we can get the version number

                p_version->get_VersionNumber(&l_value);
                // the user who did the changes

                p_version->get_Username(&bstr_text);
                ATLTRACE(_T("Version %d by %s"), l_value, bstr_text); 
                // the date it was checked in

                p_version->get_Date(&date);
                // the action done

                p_version->get_Action(&bstr_text);
                ATLTRACE(_T(" On %s %s\n"), 
                             str_FormatDateTime(date), bstr_text);
            }
            catch (_com_error & e)
            {
                ATLTRACE(_T("%s\n"), e.ErrorMessage());
            }
        }
    }
}

更全面的示例在示例项目的 CMainDialog::OnLoadHistory() 中。.

下载项目的旧版本

只要有版本号,您就可以检索旧版本。项目的旧版本也表示为 VSSItem 对象,您可以调用 Get() 方法将其下载到工作目录。请注意,您无法检出旧版本。

IVSSItemPtr vss_oldItem;
if (vss_item->get_Version(_variant_t(l_oldVersionNumber), 
                                         &vss_oldItem) == S_OK)
{
    // Download to the working directory

    CComBSTR bstr_localSpec;
    vss_item->get_LocalSpec(&bstr_localSpec);
    vss_oldItem->Get(&bstr_localSpec, VSSFLAG_REPREPLACE);
}

示例应用程序

我重点介绍示例应用程序本身,因为它也是一个很好的示例,说明了:

  • WTL 应用程序
  • WTL 中控件的子类化
  • 消息反射
  • 调整对话框大小

其他资源

其他关于 SourceSafe 的 CodeProject 文章。

以下是我在“挣扎”期间使用的参考资料链接:

© . All rights reserved.