/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : LevelEdit * * * * $Archive:: /Commando/Code/Tools/LevelEdit/VSSClass.cpp $* * * * Author:: Patrick Smith * * * * $Modtime:: 1/29/02 3:16p $* * * * $Revision:: 13 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "stdafx.h" #include "vssclass.h" #include "utils.h" #include "filemgr.h" #include "oleauto.h" #include "afxdisp.h" #include "rawfile.h" typedef struct tagVSS_ERROR { HRESULT hresult; LPCTSTR description; } VSS_ERROR; VSS_ERROR _ErrorArray[] = { { -2147164975, "You cannot change the Admin name or access privileges" }, { -2147166139, "Invalid DOS path: < >" }, { -2147166373, "File < > is currently checked out by < >" }, { -2147166374, "You currently have file < > checked out" }, { -2147166386, "Cannot delete the root project" }, { -2147166391, "You do not have access rights to < >" }, { -2147166398, "Cannot move the root project" }, { -2147166404, "File < > is not checked out" }, { -2147166405, "File < > is not shared by any other projects" }, { -2147166411, "Cannot Rename the root project" }, { -2147166417, "A project cannot be shared under a descendant." }, { -2147166418, "File < > is already shared by this project" }, { -2147166424, "The SourceSafe database has been locked by the Administrator." }, { -2147166519, "Invalid password" }, { -2147166521, "Cannot delete the Admin user" }, { -2147166522, "Permission denied " }, { -2147166525, "User ""< >"" already exists" }, { -2147166526, "User ""< >"" not found " }, { -2147166572, "An item with the name < > already exists" }, { -2147166574, "You can not move a project under itself" }, { -2147166583, "Version not found" }, { -2147167977, "The SourceSafe database path < > does not exist. Please select another database." }, { -2147210887, "This command only works on projects." }, { -2147210888, "This command only works on files." }, { -2147352566, "Invalid access code (bad parameter)*" } }; /////////////////////////////////////////////////////////////////// // // Sys_String_To_ANSI // LPTSTR Sys_String_To_ANSI (BSTR bstr) { int len = ::SysStringLen (bstr); TCHAR *ansi_string = new TCHAR[len + 1]; ASSERT(ansi_string != NULL); // Convert the BSTR to ANSI using the ANSI code page ::WideCharToMultiByte (CP_ACP, 0, bstr, len, ansi_string, len + 1, NULL, NULL); // Ensure the new string is NULL terminated ansi_string[len] = 0; // Return a pointer to the ANSI string return ansi_string; } /////////////////////////////////////////////////////////////////// // // Alloc_Sys_String // BSTR Alloc_Sys_String (LPCTSTR string) { BSTR sys_string = NULL; if (string != NULL) { // Copy the ascii format string to a wide-character string WCHAR *pwide_string = new WCHAR [::lstrlen (string) + 1]; WCHAR *ptemp_string = pwide_string; while (*string) { *ptemp_string++ = (WCHAR)*string++; } *ptemp_string = WCHAR('\0'); // Allocate a system-string (COM-safe) from the wide-character string. sys_string = ::SysAllocString (pwide_string); // Free the wide-character string we allocated earlier delete [] pwide_string; pwide_string = NULL; } // Return the system string return sys_string; } /////////////////////////////////////////////////////////////////// // // IDispatch_Get_Property // bool IDispatch_Get_Property ( LPDISPATCH pdispatch, DISPID dispid, VARIANT *presult ) { // Assume failure bool retval = false; // Params OK? ASSERT (pdispatch != NULL); if (pdispatch != NULL) { // Get this property w/o passing any args DISPPARAMS no_args = {NULL, NULL, 0, 0}; retval = SUCCEEDED (pdispatch->Invoke (dispid, IID_NULL, NULL, DISPATCH_PROPERTYGET, &no_args, presult, NULL, NULL)); } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // IDispatch_Get_Property // bool IDispatch_Get_Property ( LPDISPATCH pdispatch, DISPID dispid, VARIANT &var_arg, VARIANT *presult ) { // Assume failure bool retval = false; // Params OK? ASSERT (pdispatch != NULL); if (pdispatch != NULL) { // Fill in the dispatch-params structure with our single param DISPPARAMS args = {NULL, NULL, 0, 0}; args.rgvarg = &var_arg; args.cArgs = 1; // Get this property an pass in the single arguement EXCEPINFO excep_info = { 0 }; retval = SUCCEEDED (pdispatch->Invoke (dispid, IID_NULL, NULL, DISPATCH_PROPERTYGET, &args, presult, &excep_info, NULL)); } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Get_VSS_Interface // void VSSClass::Get_VSS_Interface (void) { if (m_pIVSSDatabase == NULL) { // Attempt to create an instance of the VSS Database interface LPUNKNOWN punknown = NULL; HRESULT hresult = ::CoCreateInstance (CLSID_VSSDatabase, NULL, CLSCTX_INPROC_SERVER, IID_IVSSDatabase, (LPVOID *)&punknown); if (SUCCEEDED (hresult)) { // Get a pointer to the 'newest' interface we know about punknown->QueryInterface (IID_IVSSDatabase, (LPVOID *)&m_pIVSSDatabase); // Release our hold on the original IUnknown interface COM_RELEASE (punknown); } } return ; } /////////////////////////////////////////////////////////////////// // // Open_Database // bool VSSClass::Open_Database ( LPCTSTR ini_filename, LPCTSTR username, LPCTSTR password ) { // Make sure we have an interface to work with Get_VSS_Interface (); // Assume failure bool retval = false; m_bReadOnly = TRUE; ASSERT (m_pIVSSDatabase != NULL); if (m_pIVSSDatabase != NULL) { // Convert the string params to COM-safe system strings BSTR bstr_filename = ::Alloc_Sys_String (ini_filename); BSTR bstr_username = ::Alloc_Sys_String (username != NULL ? username : ""); BSTR bstr_password = ::Alloc_Sys_String (password != NULL ? password : ""); // Ask VSS to open this database retval = SUCCEEDED (m_pIVSSDatabase->Open (bstr_filename, bstr_username, bstr_password)); if (retval) { // // Determine if the user is read-only // IVSSUser *user = NULL; if (SUCCEEDED (m_pIVSSDatabase->get_User (bstr_username, &user))) { VARIANT_BOOL readonly = 0; user->get_ReadOnly (&readonly); m_bReadOnly = (readonly != 0); COM_RELEASE (user); } // // Set the working directory // /*IVSSItem *root_item = Get_VSS_Item ("$/"); if (root_item != NULL) { LPCTSTR local_path = ::Get_File_Mgr ()->Get_Base_Path (); BSTR bstr_local_path = ::Alloc_Sys_String (local_path); HRESULT result = root_item->put_LocalSpec (bstr_local_path); ASSERT (SUCCEEDED (result)); ::SysFreeString (bstr_local_path); COM_RELEASE (root_item); }*/ } // Free the system strings we allocated earlier ::SysFreeString (bstr_filename); ::SysFreeString (bstr_username); ::SysFreeString (bstr_password); } // Free our inteface pointer if the function wasn't successfull if (retval == false) { COM_RELEASE (m_pIVSSDatabase); } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Get_VSS_Item_From_Local_Path // IVSSItem * VSSClass::Get_VSS_Item_From_Local_Path (LPCTSTR local_filename) { // Assume failure IVSSItem *pitem = NULL; ASSERT (m_pIVSSDatabase != NULL); ASSERT (local_filename != NULL); // State OK? if ((m_pIVSSDatabase != NULL) && (local_filename != NULL)) { // Lookup the relative path TCHAR vss_path[MAX_PATH]; CString rel_path = ::Get_File_Mgr ()->Make_Relative_Path (local_filename); ::wsprintf (vss_path, "$/%s", (LPCTSTR)rel_path); // Now, lookup the VSS item pitem = Get_VSS_Item (vss_path); } // Return a pointer to the IVSSItem interface return pitem; } /////////////////////////////////////////////////////////////////// // // Get_VSS_Item // IVSSItem * VSSClass::Get_VSS_Item (LPCTSTR vss_path) { // Assume failure IVSSItem *pitem = NULL; ASSERT (m_pIVSSDatabase != NULL); ASSERT (vss_path != NULL); // State OK? if ((m_pIVSSDatabase != NULL) && (vss_path != NULL)) { // Convert the ascii string to a COM-safe BSTR BSTR bstr_vss_path = ::Alloc_Sys_String (vss_path); /*VARIANT var_arg; var_arg.vt = VT_BSTR; var_arg.bstrVal = bstr_vss_path; VARIANT result = { 0 }; // Use the IDispatch interface to get the VSSItem property bool success = ::IDispatch_Get_Property (m_pIVSSDatabase, 0x00000006, var_arg, &result); */ HRESULT result = m_pIVSSDatabase->get_VSSItem (bstr_vss_path, 0, &pitem); if (FAILED (result)) { pitem = NULL; } // Was the property-get successful? /*if (success) { // The IVSSItem pointer should be passed back in the result pitem = (IVSSItem *)result.ppdispVal; }*/ // Free the system string we allocated earlier ::SysFreeString (bstr_vss_path); } // Return a pointer to the IVSSItem interface return pitem; } /////////////////////////////////////////////////////////////////// // // Does_File_Exist // bool VSSClass::Does_File_Exist (LPCTSTR local_filename) { // Assume failure bool retval = false; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); // If we successfully got the VSS item, then it exists in the DB if (pitem != NULL) { retval = true; // Release our hold on the item COM_RELEASE (pitem); } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Get_Type // VSSItemType VSSClass::Get_Type (LPCTSTR local_filename) { VSSItemType type = VSSITEM_FILE; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); ASSERT (pitem != NULL); if (pitem != NULL) { int temp = 0; pitem->get_Type (&temp); type = (VSSItemType)temp; // Release our hold on the item COM_RELEASE (pitem); } // Return the type (either project or file) return type; } /////////////////////////////////////////////////////////////////// // // Get // bool VSSClass::Get (LPCTSTR local_filename) { // Assume failure bool retval = false; // // If this 'file' is a directory, then get files recursively, otherwise // simply 'get' the desired file. // if (Get_Type (local_filename) == VSSITEM_PROJECT) { retval = Get_Subproject (local_filename); } else { // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); ASSERT (pitem != NULL); if (pitem != NULL) { // Do the actual file 'get' from VSS. retval = Get (local_filename, pitem); COM_RELEASE (pitem); } } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Get // bool VSSClass::Get ( LPCTSTR local_filename, IVSSItem *pitem ) { // Assume failure bool retval = false; // Params OK? ASSERT (pitem != NULL); if (pitem != NULL) { if (Get_File_Status (local_filename, NULL, 0, pitem) != VSSFILE_CHECKEDOUT_ME) { // Convert the ascii string to a COM-safe BSTR BSTR bstr_filename = ::Alloc_Sys_String (local_filename); // // Get the file // VARIANT_BOOL is_different = 0; HRESULT hresult = pitem->get_IsDifferent (bstr_filename, &is_different); if (!SUCCEEDED (hresult) || (is_different != 0)) { retval = SUCCEEDED (pitem->Get (&bstr_filename, VSSFLAG_TIMEMOD | VSSFLAG_REPREPLACE)); } // Free the system string we allocated earlier ::SysFreeString (bstr_filename); } else { retval = true; } // // Only do the 'get' if the vss-version and local versions are different. // /*VARIANT_BOOL is_different = 0; HRESULT hresult = pitem->get_IsDifferent (bstr_filename, &is_different); if (!SUCCEEDED (hresult) || (is_different != 0)) { // Get the last mod time of the version in VSS SYSTEMTIME vss_file_time = { 0 }; if (Get_File_Date (pitem, vss_file_time)) { // Get the last mod time of the local version FILETIME local_file_time = { 0 }; Get_File_Time (local_filename, NULL, NULL, &local_file_time); // // Should this file be overwritten? // CTime vss_time (vss_file_time); CTime local_time (local_file_time); if (vss_time > local_time) { retval = SUCCEEDED (pitem->Get (&bstr_filename, VSSFLAG_TIMEMOD | VSSFLAG_REPREPLACE)); } } }*/ } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Get_Subproject // bool VSSClass::Get_Subproject (LPCTSTR local_filename) { // Assume failure bool retval = false; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); // Did we succesfully get the VSS item? ASSERT (pitem != NULL); if (pitem != NULL) { Get_Recursive (local_filename, pitem); retval = true; // Convert the ascii string to a COM-safe BSTR //BSTR bstr_filename = ::Alloc_Sys_String (::Get_File_Mgr ()->Make_Full_Path (local_filename)); // Ask the VSS interface to recursively 'get' this file for us //retval = SUCCEEDED (pitem->Get (&bstr_filename, VSSFLAG_TIMEMOD | VSSFLAG_RECURSYES)); // Free the system string we allocated earlier //::SysFreeString (bstr_filename); // Release our hold on the item COM_RELEASE (pitem); } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Check_Out // bool VSSClass::Check_Out (LPCTSTR local_filename, bool get_locally) { // Assume failure bool retval = false; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); // Did we succesfully get the VSS item? ASSERT (pitem != NULL); if (pitem != NULL) { // Convert the ascii string to a COM-safe BSTR BSTR bstr_filename = ::Alloc_Sys_String (local_filename); // Ask the VSS interface to 'checkout' this file for us retval = SUCCEEDED (pitem->Checkout (NULL, bstr_filename, (get_locally ? VSSFLAG_TIMEMOD : VSSFLAG_GETNO) | VSSFLAG_CHKEXCLUSIVENO)); // Free the system string we allocated earlier ::SysFreeString (bstr_filename); // Release our hold on the item COM_RELEASE (pitem); } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Check_In // bool VSSClass::Check_In ( LPCTSTR local_filename, LPCTSTR comment ) { // Assume failure bool retval = false; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); // Did we succesfully get the VSS item? ASSERT (pitem != NULL); if (pitem != NULL) { // Convert the ascii string to a COM-safe BSTR BSTR bstr_filename = ::Alloc_Sys_String (local_filename); BSTR bstr_comment = (comment != NULL) ? ::Alloc_Sys_String (comment) : NULL; // Ask the VSS interface to 'Checkin' this file for us retval = SUCCEEDED (pitem->Checkin (bstr_comment, bstr_filename, VSSFLAG_CMPCHKSUM | VSSFLAG_RECURSNO | VSSFLAG_UPDUPDATE)); // Free the system strings we allocated earlier ::SysFreeString (bstr_filename); if (bstr_comment) { ::SysFreeString (bstr_filename); } // Release our hold on the item COM_RELEASE (pitem); } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Add_File // bool VSSClass::Add_File ( LPCTSTR local_filename, LPCTSTR comment ) { // Assume failure bool retval = false; if (Build_Tree (local_filename)) { TCHAR parent_dir[MAX_PATH]; ::lstrcpy (parent_dir, ::Strip_Filename_From_Path (local_filename)); // Get a pointer to this item's parent inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (parent_dir); // Did we succesfully get the VSS item? ASSERT (pitem != NULL); if (pitem != NULL) { // Convert the ascii string to a COM-safe BSTR BSTR bstr_filename = ::Alloc_Sys_String (local_filename); BSTR bstr_comment = (comment != NULL) ? ::Alloc_Sys_String (comment) : NULL; // Ask the VSS interface to add this file for us IVSSItem *pnew_item = NULL; retval = SUCCEEDED (pitem->Add (bstr_filename, bstr_comment, 0, &pnew_item)); // Free the system strings we allocated earlier ::SysFreeString (bstr_filename); if (bstr_comment) { ::SysFreeString (bstr_filename); } // Release our hold on these items COM_RELEASE (pitem); COM_RELEASE (pnew_item); } } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Build_Tree // bool VSSClass::Build_Tree (LPCTSTR local_filename) { // Assume failure bool retval = true; // Create a relative path to use CString rel_path = ::Get_File_Mgr ()->Make_Relative_Path (local_filename); // Generate a list of folders from the path CString *pfolder_list = NULL; int count = ::Build_List_From_String (::Strip_Filename_From_Path (rel_path), "\\", &pfolder_list); // Loop through all the folders in the path and make sure they // exist in VSS CString vss_path = "$"; CString parent = "$"; for (int ifolder = 0; (ifolder < count) && retval; ifolder ++) { // See if this path exists in the VSS database vss_path += CString ("\\") + pfolder_list[ifolder]; IVSSItem *pitem = Get_VSS_Item (vss_path); if (pitem == NULL) { // Get a pointer to the parent so we can create a new folder pitem = Get_VSS_Item (parent + CString ("\\")); if (pitem != NULL) { // Convert the ascii string to a COM-safe BSTR BSTR bstr_foldername = ::Alloc_Sys_String (pfolder_list[ifolder]); // Ask the VSS interface to add this folder for us IVSSItem *pfolder = NULL; retval &= (bool)SUCCEEDED (pitem->NewSubproject (bstr_foldername, NULL, &pfolder)); // Free any memory we allocated ::SysFreeString (bstr_foldername); COM_RELEASE (pfolder); } } COM_RELEASE (pitem); parent += CString ("\\") + pfolder_list[ifolder]; } // Free the list SAFE_DELETE_ARRAY (pfolder_list); // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Undo_Check_Out // bool VSSClass::Undo_Check_Out (LPCTSTR local_filename) { // Assume failure bool retval = false; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); // Did we succesfully get the VSS item? ASSERT (pitem != NULL); if (pitem != NULL) { // Convert the ascii string to a COM-safe BSTR BSTR bstr_filename = ::Alloc_Sys_String (local_filename); // Ask the VSS interface to undo our 'checkout' on this file for us retval = SUCCEEDED (pitem->UndoCheckout (bstr_filename, 0)); // Free the system string we allocated earlier ::SysFreeString (bstr_filename); // Release our hold on the item COM_RELEASE (pitem); } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Destroy // bool VSSClass::Destroy (LPCTSTR local_filename) { // Assume failure bool retval = false; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); // Did we succesfully get the VSS item? ASSERT (pitem != NULL); if (pitem != NULL) { // Ask the VSS interface to completely destroy this file for us retval = SUCCEEDED (pitem->Destroy ()); // Release our hold on the item COM_RELEASE (pitem); } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Get_File_Version // long VSSClass::Get_File_Version (LPCTSTR local_filename) { long version = 0L; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); // Did we succesfully get the VSS item? ASSERT (pitem != NULL); if (pitem != NULL) { // Get the version number for this item VARIANT result = { 0 }; ::IDispatch_Get_Property (pitem, 0x00000008, &result); // Pass the result back to the caller version = result.lVal; // Release our hold on the item COM_RELEASE (pitem); } // Return the version ID return version; } /////////////////////////////////////////////////////////////////// // // Is_File_Different // bool VSSClass::Is_File_Different (LPCTSTR local_filename) { bool different = false; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = Get_VSS_Item_From_Local_Path (local_filename); // Did we succesfully get the VSS item? ASSERT (pitem != NULL); if (pitem != NULL) { // Convert the ansi string to a system string BSTR bstr_filename = ::Alloc_Sys_String (local_filename); VARIANT var_arg = { 0 }; var_arg.vt = VT_BSTR; var_arg.bstrVal = bstr_filename; // Ask VSS if the local and checked-in copy are different VARIANT result = { 0 }; ::IDispatch_Get_Property (pitem, 0x00000010, var_arg, &result); // Convert the COM-bool to a C++ bool different = (result.boolVal == 0 ? false : true); // Free the system strings we allocated earlier ::SysFreeString (bstr_filename); // Release our hold on the item COM_RELEASE (pitem); } // Return the true/false result code return different; } /////////////////////////////////////////////////////////////////// // // Get_File_Date // bool VSSClass::Get_File_Date ( //LPCTSTR local_filename, IVSSItem *pitem, SYSTEMTIME &system_time ) { // Params OK? ASSERT (pitem != NULL); if (pitem != NULL) { // Determine which version number we want the date for. long current_ver_num = 0; pitem->get_VersionNumber (¤t_ver_num); // // Get the 'versions' list for this item // IVSSVersions *pversions = NULL; HRESULT hresult = pitem->get_Versions (VSSFLAG_HISTIGNOREFILES, &pversions); if (SUCCEEDED (hresult) && (pversions != NULL)) { // // Get the version enumerator for this item // IUnknown *penum_variant = NULL; HRESULT hresult = pversions->_NewEnum (&penum_variant); if (SUCCEEDED (hresult)) { // // Convert the IUnknown version enumerator to a standard // VARIANT enumerator. // IEnumVARIANT *pversion_enum = NULL; hresult = penum_variant->QueryInterface (IID_IEnumVARIANT, (LPVOID *)&pversion_enum); if (SUCCEEDED (hresult) && (pversion_enum != NULL)) { // // Loop through all the versions until we've found the right one. // bool found = false; VARIANT version_info = { 0 }; while ((pversion_enum->Next (1, &version_info, NULL) == S_OK) && !found) { IVSSVersionOld *pversion = (IVSSVersionOld *)version_info.pdispVal; if (pversion != NULL) { // Get this version's number long ver_num = 0L; pversion->get_VersionNumber (&ver_num); // Is this the version we were looking for? if (ver_num == current_ver_num) { // Get the date from the version DATE file_date = { 0 }; pversion->get_Date (&file_date); // Convert the OLE date to a SYSTEMTIME structure COleDateTime date_obj (file_date); date_obj.GetAsSystemTime (system_time); // Success! found = true; } COM_RELEASE (pversion); } } COM_RELEASE (pversion_enum); } // Get the date of this file /*if (::IDispatch_Get_Property (pcheckout, 0x00000004, &result)) { COleDateTime date_obj (result.dateVal); SYSTEMTIME system_time; date_obj.GetAsSystemTime (system_time); LPTSTR username = ::Sys_String_To_ANSI (result.bstrVal); // Copy the username back to the buffer the caller supplied ::lstrcpyn (checked_out_username, username, buffer_size); checked_out_username[buffer_size-1] = 0; // Free the temporary buffer we used to hold the username delete [] username; username = NULL; }*/ COM_RELEASE (penum_variant); } // Release our hold on the interface COM_RELEASE (pversions); } } // Return the current file status return true; } /////////////////////////////////////////////////////////////////// // // Get_File_Status // VSSFileStatus VSSClass::Get_File_Status ( LPCTSTR local_filename, LPTSTR checked_out_username, DWORD buffer_size, IVSSItem * item_to_use ) { // Assume not checked out VSSFileStatus status = VSSFILE_NOTCHECKEDOUT; // Get a pointer to this 'item' inside the VSS database. IVSSItem *pitem = item_to_use; if (pitem == NULL) { pitem = Get_VSS_Item_From_Local_Path (local_filename); } // Did we succesfully get the VSS item? ASSERT (pitem != NULL); if (pitem != NULL) { // Get the current check-out status for this item VARIANT result = { 0 }; ::IDispatch_Get_Property (pitem, 0x0000000e, &result); // Pass the result back to the caller status = (VSSFileStatus)result.lVal; // Should we lookup the name of user who has this file checked out? if ((status != VSSFILE_NOTCHECKEDOUT) && (checked_out_username != NULL)) { // Get a pointer to the 'checkouts' interface for this item bool success = ::IDispatch_Get_Property (pitem, 0x0000000f, &result); IVSSCheckouts *pcheckouts = (IVSSCheckouts *)result.ppdispVal; if (success && (pcheckouts != NULL)) { // Get the count of 'checkouts' on this file (should be 1 for our purposes) if (::IDispatch_Get_Property (pcheckouts, 0x00000001, &result)) { int count = result.lVal; if (count > 0) { VARIANT var_test = { 0 }; var_test.vt = VT_I4; var_test.lVal = 1; VARIANT var_arg = { 0 }; var_arg.vt = VT_VARIANT | VT_BYREF; var_arg.pvarVal = &var_test; // Attempt to lookup an interface that represents the first 'checkout'. // Remember, VSS supports multiple checkouts per file. if (::IDispatch_Get_Property (pcheckouts, 00000000, var_arg, &result)) { IVSSCheckout *pcheckout = (IVSSCheckout *)result.ppdispVal; // Get the username of this 'checkout' if (::IDispatch_Get_Property (pcheckout, 0x00000001, &result)) { LPTSTR username = ::Sys_String_To_ANSI (result.bstrVal); // Copy the username back to the buffer the caller supplied ::lstrcpyn (checked_out_username, username, buffer_size); checked_out_username[buffer_size-1] = 0; // Free the temporary buffer we used to hold the username delete [] username; username = NULL; } // Release our hold on the checkout interface COM_RELEASE (pcheckout); } } } // Release our hold on the interface COM_RELEASE (pcheckouts); } } // Release our hold on the item if (item_to_use == NULL) { COM_RELEASE (pitem); } } // Return the current file status return status; } /////////////////////////////////////////////////////////////////// // // Get_Error_Description // LPCTSTR VSSClass::Get_Error_Description (HRESULT hresult) { LPCTSTR description = NULL; // Loop through all the VSS specific errors that we know about int error_count = sizeof (_ErrorArray) / sizeof (VSS_ERROR); for (int error = 0; (error < error_count) && (description == NULL); error ++) { // Is this the error we are looking for? if (_ErrorArray[error].hresult == hresult) { description = _ErrorArray[error].description; } } // Return a string containing the error description. return description; } /////////////////////////////////////////////////////////////////// // // Retry_Check_Out // bool VSSClass::Retry_Check_Out ( LPCTSTR local_filename, int iattempts, int idelay ) { // Assume failure bool retval = false; // Try for up to 'iattempts' times to check-out the file for (int iattempt = 0; (iattempt < iattempts) && !retval; iattempt ++) { // Attempt to checkout the file, if unsucccessful, then wait for a second retval = Check_Out (local_filename); if (retval == false) { ::Sleep (idelay); } } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Retry_Check_In // bool VSSClass::Retry_Check_In ( LPCTSTR local_filename, int iattempts, int idelay ) { // Assume failure bool retval = false; // Try for up to 'iattempts' times to check-in the file for (int iattempt = 0; (iattempt < iattempts) && !retval; iattempt ++) { // Attempt to checkin the file, if unsucccessful, then wait for a second retval = Check_In (local_filename); if (retval == false) { ::Sleep (idelay); } } // Return the true/false result code return retval; } /////////////////////////////////////////////////////////////////// // // Get_Recursive // void VSSClass::Get_Recursive ( LPCTSTR local_path, IVSSItem *pparent ) { // Get a pointer to the 'IVSSItems' interface for this item VARIANT result = { 0 }; //bool success = ::IDispatch_Get_Property (pparent, 0x00000009, &result); IVSSItems *pitems = NULL; pparent->get_Items (0, &pitems); //IVSSItems *pitems = (IVSSItems *)result.ppdispVal; if (pitems != NULL) { // Get the sub-item count if (::IDispatch_Get_Property (pitems, 0x00000001, &result)) { int count = result.lVal; for (int index = 0; index < count; index ++) { VARIANT var_index = { 0 }; var_index.vt = VT_I4; var_index.lVal = index + 1; VARIANT var_arg = { 0 }; var_arg.vt = VT_VARIANT | VT_BYREF; var_arg.pvarVal = &var_index; // Get the subitem at this index if (::IDispatch_Get_Property (pitems, 0x00000000, var_arg, &result)) { IVSSItem *psub_item = (IVSSItem *)result.ppdispVal; if (psub_item != NULL) { // Get the sub-item's name TCHAR sub_item_name[MAX_PATH] = { 0 }; if (::IDispatch_Get_Property (psub_item, 0x00000006, &result)) { char *ansi_name = ::Sys_String_To_ANSI (result.bstrVal); ::lstrcpy (sub_item_name, ansi_name); SAFE_DELETE (ansi_name); } // Build a filename from the path and the subitem name TCHAR sub_item_path[MAX_PATH]; ::lstrcpy (sub_item_path, local_path); if (sub_item_path[::lstrlen (sub_item_path)-1] != '\\') { ::lstrcat (sub_item_path, "\\"); } ::lstrcat (sub_item_path, sub_item_name); // Get the sub-item's type VSSItemType sub_item_type = VSSITEM_PROJECT; if (::IDispatch_Get_Property (psub_item, 0x00000004, &result)) { sub_item_type = (VSSItemType)result.lVal; } // Recursively 'get' items for this subitem if (sub_item_type == VSSITEM_PROJECT) { Get_Recursive (sub_item_path, psub_item); } else { Get (sub_item_path, psub_item); } // Release our hold on this subitem's interface COM_RELEASE (psub_item); } } } } // Release our hold on the interface COM_RELEASE (pitems); } return ; } /////////////////////////////////////////////////////////////////// // // Check_Out_Ex // /////////////////////////////////////////////////////////////////// bool VSSClass::Check_Out_Ex (LPCTSTR local_filename, HWND parent_wnd) { bool retval = false; // // Determine the checkout status of the file // CString user_name; VSSFileStatus status = Get_File_Status (local_filename, user_name.GetBufferSetLength (64), 64); if (status == VSSFILE_NOTCHECKEDOUT) { // // Check out the file // retval = Retry_Check_Out (local_filename, 10, 250); if (retval == false) { CString message; message.Format ("Unable to check out %s.", local_filename); ::MessageBox (parent_wnd, message, "VSS Error", MB_ICONERROR | MB_OK); } } else if (status == VSSFILE_CHECKEDOUT) { // // Let the user know who has the file checked out. // CString message; message.Format ("User %s already has this file checked out.", user_name); ::MessageBox (parent_wnd, message, "Checkout Error", MB_ICONEXCLAMATION | MB_OK); } else if (status == VSSFILE_CHECKEDOUT_ME) { retval = true; } return retval; } /////////////////////////////////////////////////////////////////// // // Check_In_Ex // /////////////////////////////////////////////////////////////////// bool VSSClass::Check_In_Ex (LPCTSTR local_filename, HWND parent_wnd) { bool retval = false; // // Make sure the file is checked out to the current user // if (Get_File_Status (local_filename) == VSSFILE_CHECKEDOUT_ME) { // // Check in the file // retval = Retry_Check_In (local_filename, 10, 250); if (retval == false) { CString message; message.Format ("Unable to check in file: %s.", local_filename); ::MessageBox (parent_wnd, message, "VSS Error", MB_ICONERROR | MB_OK); } } else if (Does_File_Exist (local_filename) == false) { // // If the file did not exist, then add it to the database. // retval = Add_File (local_filename); } return retval; } /////////////////////////////////////////////////////////////////// // // Get_File_Status // /////////////////////////////////////////////////////////////////// AssetDatabaseClass::FILE_STATUS VSSClass::Get_File_Status (LPCTSTR local_filename, StringClass *checked_out_user_name) { AssetDatabaseClass::FILE_STATUS retval = AssetDatabaseClass::UNKNOWN; LPTSTR username = NULL; DWORD buffer_size = 0; if (checked_out_user_name != NULL) { username = checked_out_user_name->Get_Buffer (64); buffer_size = 64; } // // Lookup the vss status of this file // VSSFileStatus vss_status = Get_File_Status (local_filename, username, buffer_size); // // Map the return value to a generic // if (vss_status == VSSFILE_CHECKEDOUT_ME) { retval = AssetDatabaseClass::CHECKED_OUT_TO_ME; } else if (vss_status == VSSFILE_NOTCHECKEDOUT) { retval = AssetDatabaseClass::NOT_CHECKED_OUT; } else if (vss_status == VSSFILE_CHECKEDOUT) { retval = AssetDatabaseClass::CHECKED_OUT; } return retval; } /////////////////////////////////////////////////////////////////// // // Get_File // /////////////////////////////////////////////////////////////////// FileClass * VSSClass::Get_File (LPCTSTR local_filename) { // // Do this to get around an oddity of the class when you pass in // the filename to the constructor // RawFileClass *file_obj = new RawFileClass; file_obj->Set_Name (local_filename); return file_obj; }