|
|
//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1993-1994
//
// File: ibrfstg.c
//
// This files contains the IBriefcaseStg interface.
//
// History:
// 02-02-94 ScottH Converted from iface.c
//
//---------------------------------------------------------------------------
#include "brfprv.h" // common headers
#undef LODWORD // (because they are redefined by configmg.h)
#undef HIDWORD
#include <brfcasep.h>
#include "recact.h"
#include "res.h"
#include <help.h>
// FEATURE - BobDay - We need some mechanism of determining dock state
//---------------------------------------------------------------------------
// BriefStg Class
//---------------------------------------------------------------------------
// An IBriefcaseStg interface instance is created for each
// folder the caller (the Shell) binds to, where the folder
// is known to be inside a briefcase storage. A briefcase
// storage is the overall storage area (the database) that
// starts at a given folder (called the "briefcase root")
// and extends onwards and below in the file-system.
//
// Internally, the briefcase storage holds the path to the
// folder that this instance is bound to, and it holds a
// cached briefcase structure (CBS), which itself holds a
// reference to the briefcase root.
//
typedef struct _BriefStg { IBriefcaseStg bs; UINT cRef; // reference count
CBS * pcbs; // cached briefcase info
TCHAR szFolder[MAX_PATH]; // canonical path
HBRFCASEITER hbrfcaseiter; // handle to iterate briefcases
DWORD dwFlags; // BSTG_* flags
} BriefStg, * PBRIEFSTG;
// Flags for BriefStg
#define BSTG_SYNCFOLDER 0x00000001 // This folder has a sync copy
//---------------------------------------------------------------------------
// Supporting private code
//---------------------------------------------------------------------------
#ifdef DEBUG
/*----------------------------------------------------------
Purpose: Dump all the cache tables Returns: -- Cond: -- */ void PUBLIC DumpTables() { Atom_DumpAll(); CBS_DumpAll(); CRL_DumpAll(); } #endif
/*----------------------------------------------------------
Purpose: Initialize the cache tables Returns: -- Cond: -- */ BOOL PRIVATE InitCacheTables() { ASSERT(Sync_IsEngineLoaded()); DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Initialize cache tables")); ) if (!CBS_Init()) goto Init_Fail; if (!CRL_Init()) goto Init_Fail; return TRUE; Init_Fail: CRL_Term(); CBS_Term(NULL); return FALSE; }
/*----------------------------------------------------------
Purpose: Terminate the cache tables Returns: -- Cond: -- */ void PUBLIC TermCacheTables(void) { ASSERT(Sync_IsEngineLoaded()); DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Terminate cache tables")); ) CRL_Term(); CBS_Term(NULL); }
/*----------------------------------------------------------
Purpose: Returns TRUE if the path (a folder) has a sync copy.
Returns: see above Cond: -- */ BOOL PRIVATE HasFolderSyncCopy( HBRFCASE hbrf, LPCTSTR pszPath) { ASSERT(pszPath); ASSERT(PathIsDirectory(pszPath)); return (S_OK == Sync_IsTwin(hbrf, pszPath, SF_ISFOLDER) || IsSubfolderTwin(hbrf, pszPath)); }
/*----------------------------------------------------------
Purpose: Open a folder that belongs to a briefcase storage. The pszPath parameter is a folder, which is not necessarily the briefcase root.
Returns: NOERROR on success Cond: -- */ HRESULT PRIVATE OpenBriefcaseStorage( LPCTSTR pszPath, CBS ** ppcbs, HWND hwndOwner) { HRESULT hres; UINT uLocality; int atomBrf; TCHAR szBrfPath[MAX_PATH]; TCHAR szBrfCanon[MAX_PATH]; ASSERT(pszPath); ASSERT(ppcbs); DBG_ENTER_SZ(TEXT("OpenBriefcaseStorage"), pszPath); DEBUG_CODE( DEBUG_BREAK(BF_ONOPEN); ) // Get the root folder of the briefcase storage
// Get strictly up to the briefcase portion of path
//
uLocality = PathGetLocality(pszPath, szBrfPath); if (PL_FALSE == uLocality) { // The only time we get here is if the caller had a legitimate
// reason to believe this folder was a briefcase storage,
// but no database exists (yet). Just continue on as normal,
// the database will get created later.
BrfPathCanonicalize(pszPath, szBrfCanon); } else { BrfPathCanonicalize(szBrfPath, szBrfCanon); } // Add this path to the atom list and add it to the
// cached briefcase structure table.
// (Reference count decrement happens in CloseBriefcase)
//
atomBrf = Atom_Add(szBrfCanon); if (atomBrf != ATOM_ERR) { hres = CBS_Add(ppcbs, atomBrf, hwndOwner); } else { *ppcbs = NULL; hres = ResultFromScode(E_OUTOFMEMORY); } DEBUG_CODE( DumpTables(); ) DBG_EXIT_HRES(TEXT("OpenBriefcaseStorage"), hres); return hres; }
/*----------------------------------------------------------
Purpose: Close a briefcase.
Returns: NOERROR on success Cond: -- */ HRESULT PRIVATE CloseBriefcaseStorage( LPCTSTR pszPath) { int atomBrf; TCHAR szBrfPath[MAX_PATH]; TCHAR szBrfCanon[MAX_PATH]; UINT uLocality; ASSERT(pszPath); ASSERT(*pszPath); // Should not be an emptry string
DBG_ENTER_SZ(TEXT("CloseBriefcaseStorage"), pszPath); DEBUG_CODE( DEBUG_BREAK(BF_ONCLOSE); ) DEBUG_CODE( DumpTables(); ) // Save the briefcase and remove it from the cache
//
// Get the root folder of the briefcase storage
// Get strictly up to the briefcase portion of path
//
uLocality = PathGetLocality(pszPath, szBrfPath); if (PL_FALSE == uLocality) { // The only time we get here is for a briefcase storage that
// has no database yet. Just continue on as normal,
// the database will get created very soon now.
BrfPathCanonicalize(pszPath, szBrfCanon); } else { BrfPathCanonicalize(szBrfPath, szBrfCanon); } atomBrf = Atom_Find(szBrfCanon); ASSERT(atomBrf != ATOM_ERR); CBS_Delete(atomBrf, NULL); Atom_Delete(atomBrf); // for the Add in OpenBriefcaseStorage
DBG_EXIT_HRES(TEXT("CloseBriefcaseStorage"), NOERROR); return NOERROR; }
// Confirm button flags
#define CBF_YES 0x0001
#define CBF_NO 0x0002
#define CBF_TOALL 0x0004
#define CBF_CANCEL 0x0008
/*----------------------------------------------------------
Purpose: Checks to see if the given file/folder already exists in the given directory. Prompts the user to confirm replacing if this is true.
Returns: TRUE if path exists confirm flag settings Cond: -- */ BOOL PRIVATE DoesPathAlreadyExist( CBS * pcbs, LPCTSTR pszPathOld, LPCTSTR pszPathNew, LPUINT puConfirmFlags, // CBF_*
UINT uFlags, // SF_ISFOLDER or SF_ISFILE
HWND hwndOwner, BOOL bMultiDrop) { BOOL bRet; BOOL bIsTwin; ASSERT(puConfirmFlags); // Retain settings of *puConfirmFlags coming in
bIsTwin = (S_OK == Sync_IsTwin(pcbs->hbrf, pszPathOld, uFlags)); if (bIsTwin) uFlags |= SF_ISTWIN; else uFlags |= SF_ISNOTTWIN; bRet = (FALSE != PathExists(pszPathOld)); // Does the path already exist?
if (!bRet) { // No; remove it from the database if it is in there so we
// don't add duplicates.
Sync_Split(pcbs->hbrf, pszPathOld, 1, hwndOwner, uFlags | SF_QUIET | SF_NOCONFIRM); } else { // Yes; has a "to all" previously been specified by the user?
if (IsFlagSet(*puConfirmFlags, CBF_TOALL)) { // Yes; keep flags as they are
// (CBF_YES and CBF_NO flags are mutually exclusive)
ASSERT(IsFlagSet(*puConfirmFlags, CBF_YES) && IsFlagClear(*puConfirmFlags, CBF_NO | CBF_CANCEL) || IsFlagSet(*puConfirmFlags, CBF_NO) && IsFlagClear(*puConfirmFlags, CBF_YES | CBF_CANCEL)); } else { // No; prompt the user
UINT uFlagsCRF = bMultiDrop ? CRF_MULTI : CRF_DEFAULT; int id = ConfirmReplace_DoModal(hwndOwner, pszPathOld, pszPathNew, uFlagsCRF); *puConfirmFlags = 0; if (GetKeyState(VK_SHIFT) < 0) SetFlag(*puConfirmFlags, CBF_TOALL); if (IDYES == id) SetFlag(*puConfirmFlags, CBF_YES); else if (IDNO == id) SetFlag(*puConfirmFlags, CBF_NO); else if (IDC_YESTOALL == id) SetFlag(*puConfirmFlags, CBF_YES | CBF_TOALL); else { ASSERT(IDCANCEL == id); SetFlag(*puConfirmFlags, CBF_CANCEL); } } // Has the user chosen to replace the file?
if (IsFlagSet(*puConfirmFlags, CBF_YES)) { // Yes; is this an existing twin?
if (bIsTwin) { // Yes; delete it from the database before we continue
Sync_Split(pcbs->hbrf, pszPathOld, 1, hwndOwner, SF_QUIET | SF_NOCONFIRM); } // Some merge-handlers need the unwanted file to be deleted
// first because they cannot tell the difference between
// a newly added file (that is replacing an existing file)
// and a one-way merge.
if (!PathIsDirectory(pszPathOld)) DeleteFile(pszPathOld); } } return bRet; }
/*----------------------------------------------------------
Purpose: Add the folder twin to the database, using the default *.* wildcard settings.
Returns: standard result Cond: -- */ HRESULT PRIVATE AddDefaultFolderTwin( HWND hwndOwner, HBRFCASE hbrf, HDPA hdpa, // Return: twin handle in array
LPCTSTR pszPathFrom, // Source path
LPCTSTR pszPathTo) // Target path
{ HRESULT hres; int iTwin; // First make sure we can add another handle to hdpa (set to zero for now)
if (DPA_ERR == (iTwin = DPA_InsertPtr(hdpa, DPA_APPEND, (LPVOID)NULL))) { hres = E_OUTOFMEMORY; } else { NEWFOLDERTWIN nft; TWINRESULT tr; HFOLDERTWIN hft; RETRY_BEGIN(FALSE) { ZeroInit(&nft, NEWFOLDERTWIN); nft.ulSize = sizeof(nft); nft.pcszFolder1 = pszPathFrom; nft.pcszFolder2 = pszPathTo; nft.pcszName = c_szAllFiles; nft.dwAttributes = OBJECT_TWIN_ATTRIBUTES; nft.dwFlags = NFT_FL_SUBTREE; // Add the twin
tr = Sync_AddFolder(hbrf, &nft, &hft); hres = HRESULT_FROM_TR(tr); if (FAILED(hres)) { DWORD dwError = GetLastError(); int id; extern SETbl const c_rgseInfo[4]; // Unavailable disk?
if (ERROR_INVALID_DATA == dwError || ERROR_ACCESS_DENIED == dwError) { // Yes
hres = E_TR_UNAVAILABLE_VOLUME; } id = SEMsgBox(hwndOwner, IDS_CAP_INFO, hres, c_rgseInfo, ARRAYSIZE(c_rgseInfo)); if (IDRETRY == id) { // Try the operation again
RETRY_SET(); } } } RETRY_END() if (FAILED(hres)) { DPA_DeletePtr(hdpa, iTwin); } else { // Success
ASSERT(DPA_ERR != iTwin); ASSERT(NULL != hft); DPA_SetPtr(hdpa, iTwin, hft); } } return hres; }
/*----------------------------------------------------------
Purpose: Create a twin relationship between a folder and another folder.
Returns: standard hresult handles to created twins in hdpa confirm flag settings Cond: -- */ HRESULT PRIVATE CreateTwinOfFolder( CBS * pcbs, LPTSTR pszPath, // Dragged folder path
LPCTSTR pszDir, // Location to place twin
HDPA hdpaTwin, // array of twin handles
UINT uFlags, // AOF_*
PUINT puConfirmFlags, // CBF_*
HWND hwndOwner, BOOL bMultiDrop) // TRUE: more than 1 file/folder was dropped
{ HRESULT hres; TCHAR szPathB[MAX_PATH]; LPTSTR pszFile; ASSERT(pszPath); ASSERT(pszDir); pszFile = PathFindFileName(pszPath); // Will the path name be too long?
if (PathsTooLong(pszDir, pszFile)) { // Yes; bail
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_ADDFOLDER_TOOLONG), MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_ERROR, pszFile); hres = E_FAIL; } // Did the user drag another briefcase root into this briefcase?
else if (PathIsBriefcase(pszPath)) { // Yes; we don't allow nested briefcases! Tell the user.
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_CANTADDBRIEFCASE), MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_WARNING); hres = E_FAIL; } else { // No; check for an existing folder in the target folder.
BOOL bExists; PathCombine(szPathB, pszDir, pszFile); bExists = DoesPathAlreadyExist(pcbs, szPathB, pszPath, puConfirmFlags, SF_ISFOLDER, hwndOwner, bMultiDrop); if (!bExists || IsFlagSet(*puConfirmFlags, CBF_YES)) { ASSERT(IsFlagClear(*puConfirmFlags, CBF_NO) && IsFlagClear(*puConfirmFlags, CBF_CANCEL)); // Show 'Add Folder' dialog?
if (IsFlagSet(uFlags, AOF_FILTERPROMPT)) { // Yes
hres = Info_DoModal(hwndOwner, pszPath, szPathB, hdpaTwin, pcbs); } else { // No; just default to *.*
hres = AddDefaultFolderTwin(hwndOwner, pcbs->hbrf, hdpaTwin, pszPath, szPathB); } } else if (IsFlagSet(*puConfirmFlags, CBF_NO)) { // The user said NO
ASSERT(IsFlagClear(*puConfirmFlags, CBF_YES) && IsFlagClear(*puConfirmFlags, CBF_CANCEL)); hres = NOERROR; } else { ASSERT(IsFlagSet(*puConfirmFlags, CBF_CANCEL)); ASSERT(IsFlagClear(*puConfirmFlags, CBF_YES) && IsFlagClear(*puConfirmFlags, CBF_NO)); hres = E_ABORT; } } return hres; }
/*----------------------------------------------------------
Purpose: Create a twin of a file.
Returns: standard result twin handle in hdpa Cond: -- */ HRESULT PRIVATE CreateTwinOfFile( CBS * pcbs, LPCTSTR pszPath, // ptr to path to twin
LPCTSTR pszTargetDir, // ptr to dest dir
HDPA hdpa, // Return: twin handle in array
UINT uFlags, // AOF_*
PUINT puConfirmFlags, // CBF_*
HWND hwndOwner, BOOL bMultiDrop) // TRUE: more than 1 file/folder was dropped
{ HRESULT hres; int iTwin; TCHAR szPath[MAX_PATH]; LPCTSTR pszFile; HTWINFAMILY htfam = NULL; ASSERT(pszPath); ASSERT(pszTargetDir); pszFile = PathFindFileName(pszPath); // Will the path name be too long?
if (PathsTooLong(pszTargetDir, pszFile)) { // Yes; bail
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_ADDFILE_TOOLONG), MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_ERROR, pszFile); iTwin = DPA_ERR; hres = E_FAIL; } // First make sure we can add another handle to hdpa (set to zero for now)
else if (DPA_ERR == (iTwin = DPA_InsertPtr(hdpa, DPA_APPEND, (LPVOID)NULL))) { hres = E_OUTOFMEMORY; } else { BOOL bExists; // Confirm the replace if a file with the same name already exists.
//
PathCombine(szPath, pszTargetDir, pszFile); bExists = DoesPathAlreadyExist(pcbs, szPath, pszPath, puConfirmFlags, SF_ISFILE, hwndOwner, bMultiDrop); if (!bExists || IsFlagSet(*puConfirmFlags, CBF_YES)) { NEWOBJECTTWIN not; TWINRESULT tr; DECLAREHOURGLASS; ASSERT(IsFlagClear(*puConfirmFlags, CBF_NO) && IsFlagClear(*puConfirmFlags, CBF_CANCEL)); lstrcpy(szPath, pszPath); PathRemoveFileSpec(szPath); // User has either opted to continue adding this object to the
// database, or it does not exist in the destination folder.
RETRY_BEGIN(FALSE) { ZeroInit(¬, NEWOBJECTTWIN); not.ulSize = sizeof(NEWOBJECTTWIN); not.pcszFolder1 = szPath; not.pcszFolder2 = pszTargetDir; not.pcszName = pszFile; SetHourglass(); Sync_Dump(¬, NEWOBJECTTWIN); tr = Sync_AddObject(pcbs->hbrf, ¬, &htfam); ResetHourglass(); hres = HRESULT_FROM_TR(tr); if (FAILED(hres)) { DWORD dwError = GetLastError(); // Unavailable disk?
if (ERROR_INVALID_DATA == dwError || ERROR_ACCESS_DENIED == dwError) { // Yes; ask user to retry/cancel
int id = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_ADDFILE_UNAVAIL_VOL), MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_RETRYCANCEL | MB_ICONWARNING); // Set specific error value
hres = E_TR_UNAVAILABLE_VOLUME; if (IDRETRY == id) { RETRY_SET(); // Try again
} } } } RETRY_END() } else if (IsFlagSet(*puConfirmFlags, CBF_NO)) { // The user said NO
ASSERT(IsFlagClear(*puConfirmFlags, CBF_YES) && IsFlagClear(*puConfirmFlags, CBF_CANCEL)); DPA_DeletePtr(hdpa, iTwin); hres = NOERROR; } else { ASSERT(IsFlagSet(*puConfirmFlags, CBF_CANCEL)); ASSERT(IsFlagClear(*puConfirmFlags, CBF_YES) && IsFlagClear(*puConfirmFlags, CBF_NO)); hres = E_ABORT; } } if (FAILED(hres)) { if (DPA_ERR != iTwin) { DPA_DeletePtr(hdpa, iTwin); } } else { // Success
ASSERT(DPA_ERR != iTwin); if (htfam) DPA_SetPtr(hdpa, iTwin, htfam); } return hres; }
/*----------------------------------------------------------
Purpose: Deletes the new twins Returns: -- Cond: -- */ void PRIVATE DeleteNewTwins( CBS * pcbs, HDPA hdpa) { int iItem; int cItems; ASSERT(pcbs); ASSERT(hdpa); cItems = DPA_GetPtrCount(hdpa); for (iItem = 0; iItem < cItems; iItem++) { HTWIN htwin = DPA_FastGetPtr(hdpa, iItem); if (htwin) Sync_DeleteTwin(htwin); } }
/*----------------------------------------------------------
Purpose: Releases the twin handles Returns: -- Cond: -- */ void PRIVATE ReleaseNewTwins( HDPA hdpa) { int i; int cItems; ASSERT(hdpa); cItems = DPA_GetPtrCount(hdpa); for (i = 0; i < cItems; i++) { HTWIN htwin = DPA_FastGetPtr(hdpa, i); if (htwin) Sync_ReleaseTwin(htwin); } }
/*----------------------------------------------------------
Purpose: Returns the count of nodes that do not have FS_COND_UNAVAILABLE.
Returns: see above Cond: -- */ UINT PRIVATE CountAvailableNodes( PRECITEM pri) { UINT ucNodes = 0; PRECNODE prn; for (prn = pri->prnFirst; prn; prn = prn->prnNext) { if (FS_COND_UNAVAILABLE != prn->fsCurrent.fscond) { ucNodes++; } } return ucNodes; }
/*----------------------------------------------------------
Purpose: Returns the count of nodes that require some sort of action.
Returns: see above Cond: -- */ UINT PRIVATE CountActionItem( PRECLIST prl) { UINT uc = 0; PRECITEM pri; for (pri = prl->priFirst; pri; pri = pri->priNext) { if (RIA_NOTHING != pri->riaction) { uc++; } } return uc; }
/*----------------------------------------------------------
Purpose: Update the twins in the list
Returns: Cond: -- */ HRESULT PRIVATE MassageReclist( CBS * pcbs, PRECLIST prl, LPCTSTR pszInsideDir, BOOL bCopyIn, HWND hwndOwner) { HRESULT hres = NOERROR; PRECITEM pri; BOOL bWarnUser = TRUE; PRECNODE prnInside; PRECNODE prnOutside; // Make sure the direction of the reconciliation coincides
// with the direction of the user's action.
for (pri = prl->priFirst; pri; pri = pri->priNext) { if (RIA_NOTHING != pri->riaction) { UINT cAvailableNodes = CountAvailableNodes(pri); // Is this a wierd multi-edged case (not including
// Sneakernet)?
if (2 < cAvailableNodes) { // Should never get here, but better safe than sorry
ASSERT(0); } else { // No; get the pair of nodes that we just added to the
// database.
hres = Sync_GetNodePair(pri, Atom_GetName(pcbs->atomBrf), pszInsideDir, &prnInside, &prnOutside); if (SUCCEEDED(hres)) { ASSERT(prnInside); ASSERT(prnOutside); if (bCopyIn) { switch (prnOutside->rnstate) { case RNS_UNAVAILABLE: case RNS_DOES_NOT_EXIST: case RNS_DELETED: break; // leave alone
default: // Force the update to be a copy into the briefcase.
pri->riaction = RIA_COPY; prnInside->rnaction = RNA_COPY_TO_ME; prnOutside->rnaction = RNA_COPY_FROM_ME; TRACE_MSG(TF_GENERAL, TEXT("Massaging reclist")); break; } } else { switch (prnInside->rnstate) { case RNS_UNAVAILABLE: case RNS_DOES_NOT_EXIST: case RNS_DELETED: break; // leave alone
default: // Force the update to be a copy out of the briefcase.
pri->riaction = RIA_COPY; prnInside->rnaction = RNA_COPY_FROM_ME; prnOutside->rnaction = RNA_COPY_TO_ME; TRACE_MSG(TF_GENERAL, TEXT("Massaging reclist")); break; } } } else break; // Error
} } } return hres; }
/*----------------------------------------------------------
Purpose: Check for more than 2 available nodes in each recitem. Remove the associated twin if we find such a case, to prevent multiple sync copies.
Returns: S_OK if everything looks ok S_FALSE if there were multiple sync copies introduced Cond: -- */ HRESULT PRIVATE VerifyTwins( CBS * pcbs, PRECLIST prl, LPCTSTR pszTargetDir, HWND hwndOwner) { HRESULT hres = NOERROR; PRECITEM pri; BOOL bWarnUser = TRUE; BOOL bWarnUserFolder = TRUE; TCHAR szPath[MAX_PATH]; // Look thru the reclist and pick out recitems that have more than
// 2 recnodes that are currently available.
// Scenarios when this can happen:
//
// 1) Foo.txt --> BC
// Foo.txt --> BC\Orphan Folder
//
// Expected result: delete BC\Orphan Folder\Foo.txt twin
//
// 2) Foo.txt --> BC\Orphan Folder
// Orphan Folder --> BC
//
// Expected result: delete BC\Orphan Folder twin
//
// 3) Foo.txt --> BC\Orphan Folder
// Foo.txt --> BC
//
// Expected result: delete BC\Foo.txt twin
//
for (pri = prl->priFirst; pri; pri = pri->priNext) { UINT cAvailableNodes = CountAvailableNodes(pri); PRECNODE prn; // Are there more than 2 available nodes?
if (2 < cAvailableNodes && *pri->pcszName) { BOOL bLookForFolders = TRUE; // FIRST: Look for object twins that are not in folder twins.
for (prn = pri->prnFirst; prn; prn = prn->prnNext) { // Is this file here because the file was dragged in?
if (IsSzEqual(pszTargetDir, prn->pcszFolder)) { // Yes; warn the user
if (bWarnUser) { MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_ADDFILE_TOOMANY), MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_WARNING, pri->pcszName); if (0 > GetKeyState(VK_SHIFT)) { bWarnUser = FALSE; } } // Try to remove the object twin
PathCombine(szPath, prn->pcszFolder, pri->pcszName); hres = Sync_Split(pcbs->hbrf, szPath, 1, hwndOwner, SF_QUIET | SF_NOCONFIRM); TRACE_MSG(TF_GENERAL, TEXT("Deleted object twin for %s"), szPath); ASSERT(FAILED(hres) || S_OK == hres); bLookForFolders = FALSE; break; } } if (bLookForFolders) { // SECOND: Look for object twins that exist because of folder
// twins.
for (prn = pri->prnFirst; prn; prn = prn->prnNext) { lstrcpy(szPath, prn->pcszFolder); PathRemoveFileSpec(szPath); // Is this file here because it is in a folder that was
// dragged in?
if (IsSzEqual(pszTargetDir, szPath)) { // Yes; warn the user
if (bWarnUserFolder && bWarnUser) { MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_ADDFOLDER_TOOMANY), MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_WARNING, PathFindFileName(prn->pcszFolder)); // Hack: to prevent showing this messagebox for
// every file in this folder, set this flag
bWarnUserFolder = FALSE; if (0 > GetKeyState(VK_SHIFT)) { bWarnUser = FALSE; } } // Remove the folder twin
hres = Sync_Split(pcbs->hbrf, prn->pcszFolder, 1, hwndOwner, SF_ISFOLDER | SF_QUIET | SF_NOCONFIRM); TRACE_MSG(TF_GENERAL, TEXT("Deleted folder twin for %s"), prn->pcszFolder); ASSERT(FAILED(hres) || !bWarnUserFolder || S_OK == hres); break; } } } hres = S_FALSE; } } return hres; } #define STATE_VERIFY 0
#define STATE_UPDATE 1
#define STATE_STOP 2
/*----------------------------------------------------------
Purpose: This function updates the new files. Unlike the general update function, this strictly updates file pairs. All other incidental nodes are set to RNA_NOTHING.
In addition, to be safe, we force the update to always perform a copy into the briefcase. This function releases the twin handles when it is finished. Returns: standard result Cond: -- */ HRESULT PRIVATE UpdateNewTwins( CBS * pcbs, LPCTSTR pszInsideDir, LPCTSTR pszTargetDir, BOOL bCopyIn, HDPA hdpa, HWND hwndOwner) { HRESULT hres = E_FAIL; int iItem; int cItems; ASSERT(pcbs); ASSERT(hdpa); cItems = DPA_GetPtrCount(hdpa); if (cItems > 0) { HTWINLIST htl; PRECLIST prl; TWINRESULT tr; tr = Sync_CreateTwinList(pcbs->hbrf, &htl); if (TR_SUCCESS != tr) { hres = HRESULT_FROM_TR(tr); } else { HWND hwndProgress; UINT nState = STATE_VERIFY; DEBUG_CODE( UINT nCount = 0; ) // State progression is simple:
// STATE_VERIFY --> STATE_UPDATE --> STATE_STOP
// Any questions?
hwndProgress = UpdBar_Show(hwndOwner, UB_CHECKING, DELAY_UPDBAR); for (iItem = 0; iItem < cItems; iItem++) { HTWIN htwin = DPA_FastGetPtr(hdpa, iItem); if (htwin) Sync_AddToTwinList(htl, htwin); } do { ASSERT(STATE_VERIFY == nState || STATE_UPDATE == nState); ASSERT(2 > nCount++); // Sanity check for infinite loop
// Create the reclist
hres = Sync_CreateRecListEx(htl, UpdBar_GetAbortEvt(hwndProgress), &prl); DEBUG_CODE( Sync_DumpRecList(GET_TR(hres), prl, TEXT("Adding new twins")); ) if (SUCCEEDED(hres)) { ASSERT(prl); switch (nState) { case STATE_VERIFY: hres = VerifyTwins(pcbs, prl, pszTargetDir, hwndOwner); if (S_FALSE == hres) nState = STATE_UPDATE; else if (S_OK == hres) goto Update; else nState = STATE_STOP; break; case STATE_UPDATE: // After recreating the reclist, is there anything
// that needs updating?
if (0 < CountActionItems(prl)) { // Yes
Update: UpdBar_SetAvi(hwndProgress, UB_UPDATEAVI); hres = MassageReclist(pcbs, prl, pszInsideDir, bCopyIn, hwndOwner); if (SUCCEEDED(hres)) { // Update these files
hres = Sync_ReconcileRecList(prl, Atom_GetName(pcbs->atomBrf), hwndProgress, RF_ONADD); } } nState = STATE_STOP; break; default: ASSERT(0); break; } Sync_DestroyRecList(prl); } } while (SUCCEEDED(hres) && STATE_UPDATE == nState); Sync_DestroyTwinList(htl); UpdBar_Kill(hwndProgress); } } return hres; } // FEATURE - BobDay - WinNT docking state determination code goes here.
/*----------------------------------------------------------
Purpose: Return TRUE if the machine is docked
Returns: See above. Cond: -- */ BOOL PRIVATE IsMachineDocked(void) { return TRUE; }
//---------------------------------------------------------------------------
// IBriefcaseStg member functions
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: IBriefcaseStg::Release
Returns: new reference count Cond: -- */ STDMETHODIMP_(UINT) BriefStg_Release( LPBRIEFCASESTG pstg) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); DBG_ENTER(TEXT("BriefStg_Release")); if (--this->cRef) { DBG_EXIT_UL(TEXT("BriefStg_Release"), this->cRef); return this->cRef; // Return decremented reference count
} if (this->pcbs) { // Release this briefcase storage instance
CloseBriefcaseStorage(this->szFolder); } if (this->hbrfcaseiter) { Sync_FindClose(this->hbrfcaseiter); } GFree(this); ENTEREXCLUSIVE(); { DecBriefSemaphore(); if (IsLastBriefSemaphore()) { CommitIniFile(); DEBUG_CODE( DumpTables(); ) TermCacheTables(); } } LEAVEEXCLUSIVE(); DBG_EXIT_UL(TEXT("BriefStg_Release"), 0); return 0; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::AddRef
Returns: new reference count Cond: -- */ STDMETHODIMP_(UINT) BriefStg_AddRef( LPBRIEFCASESTG pstg) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); UINT cRef; DBG_ENTER(TEXT("BriefStg_AddRef")); cRef = ++this->cRef; DBG_EXIT_UL(TEXT("BriefStg_AddRef"), cRef); return cRef; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::QueryInterface
Returns: standard Cond: -- */ STDMETHODIMP BriefStg_QueryInterface( LPBRIEFCASESTG pstg, REFIID riid, LPVOID * ppvOut) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres; DBG_ENTER_RIID(TEXT("BriefStg_QueryInterface"), riid); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IBriefcaseStg)) { // We use the bs field as our IUnknown as well
*ppvOut = &this->bs; this->cRef++; hres = NOERROR; } else { *ppvOut = NULL; hres = ResultFromScode(E_NOINTERFACE); } DBG_EXIT_HRES(TEXT("BriefStg_QueryInterface"), hres); return hres; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::Initialize
Called to initialize a briefcase storage instance. The pszFolder indicates the folder we are binding to, which is in the briefcase storage (somewhere). Returns: standard Cond: -- */ STDMETHODIMP BriefStg_Initialize( LPBRIEFCASESTG pstg, LPCTSTR pszPath, HWND hwndOwner) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres = ResultFromScode(E_FAIL); DBG_ENTER_SZ(TEXT("BriefStg_Initialize"), pszPath); ASSERT(pszPath); // Only initialize once per interface instance
//
if (pszPath && NULL == this->pcbs) { BOOL bCancel = FALSE; RETRY_BEGIN(FALSE) { // Unavailable disk?
if (!PathExists(pszPath)) { // Yes; ask user to retry/cancel
int id = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_OPEN_UNAVAIL_VOL), MAKEINTRESOURCE(IDS_CAP_OPEN), NULL, MB_RETRYCANCEL | MB_ICONWARNING); if (IDRETRY == id) RETRY_SET(); // Try again
else bCancel = TRUE; } } RETRY_END() if (!bCancel) { BrfPathCanonicalize(pszPath, this->szFolder); if (PathExists(this->szFolder) && !PathIsDirectory(this->szFolder)) { // (Store this as a path to a folder)
PathRemoveFileSpec(this->szFolder); } // Open the briefcase storage for this folder
//
hres = OpenBriefcaseStorage(this->szFolder, &this->pcbs, hwndOwner); if (SUCCEEDED(hres)) { // Is this folder a sync folder?
if (HasFolderSyncCopy(this->pcbs->hbrf, this->szFolder)) { // Yes
SetFlag(this->dwFlags, BSTG_SYNCFOLDER); } else { // No (or error, in which case we default to no)
ClearFlag(this->dwFlags, BSTG_SYNCFOLDER); } } } } hres = MapToOfficialHresult(hres); DBG_EXIT_HRES(TEXT("BriefStg_Initialize"), hres); return hres; }
/*----------------------------------------------------------
Purpose: Add an object or objects to the briefcase storage. This function does the real work for BriefStg_AddObject.
Returns: standard result NOERROR if the object(s) were added S_FALSE if the object(s) should be handled by the caller Cond: -- */ HRESULT PRIVATE BriefStg_AddObjectPrivate( LPBRIEFCASESTG pstg, LPDATAOBJECT pdtobj, LPCTSTR pszFolderEx, // optional (may be NULL)
UINT uFlags, // One of AOF_*
HWND hwndOwner) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres; LPTSTR pszList; LPTSTR psz; UINT i; UINT cFiles; TCHAR szCanon[MAX_PATH]; HDPA hdpa; LPCTSTR pszTarget; BOOL bMultiFiles; static SETbl const c_rgseAdd[] = { { E_OUTOFMEMORY, IDS_OOM_ADD, MB_ERROR }, { E_TR_OUT_OF_MEMORY, IDS_OOM_ADD, MB_ERROR }, }; ASSERT(pdtobj); // Verify that the folder of this briefcase storage is actually inside
// a briefcase. (szCanon is used as a dummy here.)
ASSERT( !PathExists(this->szFolder) || PL_FALSE != PathGetLocality(this->szFolder, szCanon) ); // Get list of files to add
hres = DataObj_QueryFileList(pdtobj, &pszList, &cFiles); if (SUCCEEDED(hres)) { // Grab the mutex to delay any further calculation in any
// Briefcase views' secondary threads until we're done
// processing here.
Delay_Own(); // Does the caller want to create sync copies of objects that are
// already in the briefcase to some other folder? (Sneakernet)
if (NULL != pszFolderEx) { // Yes
pszTarget = pszFolderEx; } else { // No
pszTarget = this->szFolder; // Are the entities already in this briefcase?
//
// Based on the success return value of DataObj_QueryFileList,
// we can tell if the entities are already within a briefcase.
// Because of the nature of the shell, we assume the file
// list contains entities which all exist in the same folder,
// so we consider it an "all or nothing" sort of indicator.
// If the entities are indeed in a briefcase, we compare the
// roots of the source and destination briefcases, and BLOCK
// the addition if they are the same.
//
if (S_OK == hres) { // They are in *a* briefcase. Which one?
DataObj_QueryBriefPath(pdtobj, szCanon); if (IsSzEqual(szCanon, Atom_GetName(this->pcbs->atomBrf))) { // This same one! Don't do anything.
// display message box
hres = ResultFromScode(E_FAIL); goto Error1; } } } bMultiFiles = (1 < cFiles); // Create the temporary DPA list
if (NULL == (hdpa = DPA_Create(cFiles))) { hres = ResultFromScode(E_OUTOFMEMORY); } else { UINT uConfirmFlags = 0; // Add all the objects to the briefcase storage
for (i = 0, psz = pszList; i < cFiles; i++) { // Get file/folder name that was dropped
BrfPathCanonicalize(psz, szCanon); if (PathIsDirectory(szCanon)) { hres = CreateTwinOfFolder(this->pcbs, szCanon, pszTarget, hdpa, uFlags, &uConfirmFlags, hwndOwner, bMultiFiles); } else { hres = CreateTwinOfFile(this->pcbs, szCanon, pszTarget, hdpa, uFlags, &uConfirmFlags, hwndOwner, bMultiFiles); } if (FAILED(hres)) { // An error occurred while attempting to add a twin
break; } DataObj_NextFile(psz); // Set psz to next file in list
} if (FAILED(hres)) { // Delete the twins that were added.
DeleteNewTwins(this->pcbs, hdpa); } else { // Update these new twins
hres = UpdateNewTwins(this->pcbs, this->szFolder, pszTarget, (NULL == pszFolderEx), hdpa, hwndOwner); } ReleaseNewTwins(hdpa); DPA_Destroy(hdpa); } Error1: DataObj_FreeList(pszList); Delay_Release(); } if (FAILED(hres)) { SEMsgBox(hwndOwner, IDS_CAP_ADD, hres, c_rgseAdd, ARRAYSIZE(c_rgseAdd)); } return hres; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::AddObject
Add an object to the briefcase storage. Returns: standard hresult Cond: -- */ STDMETHODIMP BriefStg_AddObject( LPBRIEFCASESTG pstg, LPDATAOBJECT pdtobj, LPCTSTR pszFolderEx, // optional
UINT uFlags, HWND hwndOwner) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres = NOERROR; LPCTSTR pszFolder; UINT ids; DEBUG_CODE( TCHAR szDbg[MAX_PATH]; ) DBG_ENTER_DTOBJ(TEXT("BriefStg_AddObject"), pdtobj, szDbg); ASSERT(pdtobj); ASSERT(this->pcbs); // Is this sneakernet?
// Is this folder a sync folder?
if (pszFolderEx) { // Yes; is the source a sync folder already?
if (HasFolderSyncCopy(this->pcbs->hbrf, pszFolderEx)) { // Yes; don't allow other sync copies into (or out of) it
ids = IDS_ERR_ADD_SYNCFOLDER; pszFolder = PathFindFileName(pszFolderEx); hres = E_FAIL; } // Is the source folder a sync folder already?
else if (IsFlagSet(this->dwFlags, BSTG_SYNCFOLDER)) { // Yes; don't allow other sync copies into (or out of) it
ids = IDS_ERR_ADD_SYNCFOLDER_SRC; pszFolder = PathFindFileName(this->szFolder); hres = E_FAIL; } } else if (IsFlagSet(this->dwFlags, BSTG_SYNCFOLDER)) { // Yes; don't allow other sync copies into (or out of) it
ids = IDS_ERR_ADD_SYNCFOLDER; pszFolder = PathFindFileName(this->szFolder); hres = E_FAIL; } if (SUCCEEDED(hres)) { hres = BriefStg_AddObjectPrivate(pstg, pdtobj, pszFolderEx, uFlags, hwndOwner); } else { MsgBox(hwndOwner, MAKEINTRESOURCE(ids), MAKEINTRESOURCE(IDS_CAP_ADD), NULL, MB_WARNING, pszFolder); } DEBUG_CODE( DumpTables(); ) hres = MapToOfficialHresult(hres); DBG_EXIT_HRES(TEXT("BriefStg_AddObject"), hres); return hres; }
/*----------------------------------------------------------
Purpose: Removes an object or objects from the briefcase storage.
Returns: standard hresult Cond: -- */ HRESULT PRIVATE ReleaseObject( CBS * pcbs, LPDATAOBJECT pdtobj, HWND hwndOwner) { HRESULT hres; LPTSTR pszList; UINT cFiles; ASSERT(pdtobj); hres = DataObj_QueryFileList(pdtobj, &pszList, &cFiles); if (SUCCEEDED(hres)) { RETRY_BEGIN(FALSE) { hres = Sync_Split(pcbs->hbrf, pszList, cFiles, hwndOwner, 0); // Unavailable disk?
if (E_TR_UNAVAILABLE_VOLUME == hres) { // Yes; ask user to retry/cancel
int id = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_UNAVAIL_VOL), MAKEINTRESOURCE(IDS_CAP_Split), NULL, MB_RETRYCANCEL | MB_ICONWARNING); if (IDRETRY == id) RETRY_SET(); // Try again
} } RETRY_END() DataObj_FreeList(pszList); } return hres; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::ReleaseObject
Release an object from the briefcase storage. Returns: standard hresult Cond: -- */ STDMETHODIMP BriefStg_ReleaseObject( LPBRIEFCASESTG pstg, LPDATAOBJECT pdtobj, HWND hwndOwner) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres; DEBUG_CODE( TCHAR szDbg[MAX_PATH]; ) DBG_ENTER_DTOBJ(TEXT("BriefStg_ReleaseObject"), pdtobj, szDbg); ASSERT(pdtobj); ASSERT(this->pcbs); hres = ReleaseObject(this->pcbs, pdtobj, hwndOwner); DEBUG_CODE( DumpTables(); ) hres = MapToOfficialHresult(hres); DBG_EXIT_HRES(TEXT("BriefStg_ReleaseObject"), hres); return hres; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::UpdateObject
Update an object in the briefcase storage. Returns: standard hresult Cond: -- */ STDMETHODIMP BriefStg_UpdateObject( LPBRIEFCASESTG pstg, LPDATAOBJECT pdtobj, HWND hwndOwner) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres; TCHAR szPath[MAX_PATH]; DEBUG_CODE( TCHAR szDbg[MAX_PATH]; )
DBG_ENTER_DTOBJ(TEXT("BriefStg_UpdateObject"), pdtobj, szDbg);
ASSERT(pdtobj); ASSERT(this->pcbs);
// Determine whether this is an Update Selection or Update All.
hres = DataObj_QueryPath(pdtobj, szPath); if (SUCCEEDED(hres)) { // Is this a briefcase root?
if (PathIsBriefcase(szPath)) { // Yes; do an Update All
hres = Upd_DoModal(hwndOwner, this->pcbs, NULL, 0, UF_ALL); } else { // No; do an Update Selection
LPTSTR pszList; UINT cFiles; hres = DataObj_QueryFileList(pdtobj, &pszList, &cFiles); if (SUCCEEDED(hres)) { hres = Upd_DoModal(hwndOwner, this->pcbs, pszList, cFiles, UF_SELECTION); DataObj_FreeList(pszList); } } } DEBUG_CODE( DumpTables(); ) hres = MapToOfficialHresult(hres); DBG_EXIT_HRES(TEXT("BriefStg_UpdateObject"), hres); return hres; }
/*----------------------------------------------------------
Purpose: Update a briefcase based on events
Returns: standard hresult Cond: -- */ HRESULT PRIVATE BriefStg_UpdateOnEvent( LPBRIEFCASESTG pstg, UINT uEvent, HWND hwndOwner) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres = NOERROR; DBG_ENTER(TEXT("BriefStg_UpdateOnEvent")); switch (uEvent) { case UOE_CONFIGCHANGED: case UOE_QUERYCHANGECONFIG: // Is the machine docked?
if (IsMachineDocked()) { // Yes; does the user want to update?
TCHAR sz[MAX_PATH]; int ids = (UOE_CONFIGCHANGED == uEvent) ? IDS_MSG_UpdateOnDock : IDS_MSG_UpdateBeforeUndock; LPCTSTR pszBrf = Atom_GetName(this->pcbs->atomBrf); int id = MsgBox(hwndOwner, MAKEINTRESOURCE(ids), MAKEINTRESOURCE(IDS_CAP_UPDATE), LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_UPDATE_DOCK)), MB_QUESTION, PathGetDisplayName(pszBrf, sz)); if (IDYES == id) { // Yes; do an Update All
hres = Upd_DoModal(hwndOwner, this->pcbs, NULL, 0, UF_ALL); } } break; default: hres = ResultFromScode(E_INVALIDARG); break; } DEBUG_CODE( DumpTables(); ) hres = MapToOfficialHresult(hres); DBG_EXIT_HRES(TEXT("BriefStg_UpdateOnEvent"), hres); return hres; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::Notify
Marks the path dirty in the briefcase storage cache. (The path may not exist in the cache, in which case this function does nothing.) Returns: S_OK to force a refresh S_FALSE to not force a refresh Cond: -- */ STDMETHODIMP BriefStg_Notify( LPBRIEFCASESTG pstg, LPCTSTR pszPath, // may be NULL
LONG lEvent, // one of NOE_ flags
UINT * puFlags, // returned NF_ flags
HWND hwndOwner) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres = ResultFromScode(E_OUTOFMEMORY); TCHAR szCanon[MAX_PATH]; int atom; DBG_ENTER_SZ(TEXT("BriefStg_Notify"), pszPath); ASSERT(this->pcbs); ASSERT(puFlags); DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Received event %lx for %s"), lEvent, Dbg_SafeStr(pszPath)); ) *puFlags = 0; // Dirty the entire cache?
if (NOE_DIRTYALL == lEvent) { // Yes
TRACE_MSG(TF_GENERAL, TEXT("Marking everything")); CRL_DirtyAll(this->pcbs->atomBrf); Sync_ClearBriefcaseCache(this->pcbs->hbrf); hres = NOERROR; } else if (pszPath && 0 < lEvent) { // No
BrfPathCanonicalize(pszPath, szCanon); atom = Atom_Add(szCanon); if (ATOM_ERR != atom) { int atomCab = Atom_Add(this->szFolder); if (ATOM_ERR != atomCab) { // There are two actions we must determine: what gets marked dirty?
// and does this specific window get forcibly refreshed?
BOOL bRefresh; BOOL bMarked; bMarked = CRL_Dirty(atom, atomCab, lEvent, &bRefresh); hres = NOERROR; if (bMarked) { SetFlag(*puFlags, NF_ITEMMARKED); } #ifdef DEBUG
if (bMarked && bRefresh) { TRACE_MSG(TF_GENERAL, TEXT("Marked and forcing refresh of window on %s"), (LPTSTR)this->szFolder); } else if (bMarked) { TRACE_MSG(TF_GENERAL, TEXT("Marked")); } #endif
Atom_Delete(atomCab); } Atom_Delete(atom); } } DBG_EXIT_HRES(TEXT("BriefStg_Notify"), hres); return hres; }
/*----------------------------------------------------------
Purpose: Gets special info (status and origin) of a path. Returns: -- Cond: -- */ HRESULT PRIVATE BriefStg_GetSpecialInfoOf( PBRIEFSTG this, LPCTSTR pszName, UINT uFlag, LPTSTR pszBuf, int cchBuf) { HRESULT hres = E_OUTOFMEMORY; TCHAR szPath[MAX_PATH]; TCHAR szCanon[MAX_PATH]; int atom; ASSERT(this); ASSERT(pszName); ASSERT(pszBuf); ASSERT(this->pcbs); *pszBuf = TEXT('\0'); // Would the path be too long if combined?
if (PathsTooLong(this->szFolder, pszName)) { // Yes
hres = E_FAIL; } else { PathCombine(szPath, this->szFolder, pszName); BrfPathCanonicalize(szPath, szCanon); atom = Atom_Add(szCanon); if (ATOM_ERR != atom) { CRL * pcrl; // The first CRL_Get call will get the reclist from the cache
// or get a fresh reclist if the dirty bit is set. If the cache
// item doesn't exist, add it. We add orphans to the cache too
// but they have no reclist.
// Does the cached item already exist?
hres = CRL_Get(atom, &pcrl); if (FAILED(hres)) { // No; add it
hres = CRL_Add(this->pcbs, atom); if (SUCCEEDED(hres)) { // Do another 'get' to offset the CRL_Delete at the end of
// this function. This will leave this new reclist in the
// cache upon exit. (We don't want to create a new reclist
// everytime this functin is called.) It will all get
// cleaned up when the CBS is freed.
//
hres = CRL_Get(atom, &pcrl); } } ASSERT(FAILED(hres) || pcrl); // Do we have a cache reclist entry to work with?
if (pcrl) { // Yes
if (GEI_ORIGIN == uFlag) { lstrcpyn(pszBuf, Atom_GetName(pcrl->atomOutside), cchBuf); PathRemoveFileSpec(pszBuf); } else { ASSERT(GEI_STATUS == uFlag); SzFromIDS(pcrl->idsStatus, pszBuf, cchBuf); } CRL_Delete(atom); // Decrement count
} Atom_Delete(atom); } } return hres; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::GetExtraInfo
Returns: standard hresult Cond: -- */ STDMETHODIMP BriefStg_GetExtraInfo( LPBRIEFCASESTG pstg, LPCTSTR pszName, UINT uInfo, WPARAM wParam, LPARAM lParam) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres; DBG_ENTER_SZ(TEXT("BriefStg_GetExtraInfo"), pszName); ASSERT(this->pcbs); switch (uInfo) { case GEI_ORIGIN: case GEI_STATUS: { LPTSTR pszBuf = (LPTSTR)lParam; int cchBuf = (int)wParam; ASSERT(pszName); ASSERT(pszBuf); hres = BriefStg_GetSpecialInfoOf(this, pszName, uInfo, pszBuf, cchBuf); } break; case GEI_DELAYHANDLE: { HANDLE * phMutex = (HANDLE *)lParam; ASSERT(phMutex); *phMutex = g_hMutexDelay; hres = NOERROR; } break; case GEI_ROOT: { LPTSTR pszBuf = (LPTSTR)lParam; int cchBuf = (int)wParam; ASSERT(pszBuf); lstrcpyn(pszBuf, Atom_GetName(this->pcbs->atomBrf), cchBuf); #ifdef DEBUG
if (IsFlagSet(g_uDumpFlags, DF_PATHS)) { TRACE_MSG(TF_ALWAYS, TEXT("Root is \"%s\""), pszBuf); } #endif
hres = NOERROR; } break; case GEI_DATABASENAME: { LPTSTR pszBuf = (LPTSTR)lParam; int cchBuf = (int)wParam; LPCTSTR pszDBName; ASSERT(pszBuf); if (IsFlagSet(this->pcbs->uFlags, CBSF_LFNDRIVE)) pszDBName = g_szDBName; else pszDBName = g_szDBNameShort; lstrcpyn(pszBuf, pszDBName, cchBuf); hres = NOERROR; } break; default: hres = E_INVALIDARG; break; } DBG_EXIT_HRES(TEXT("BriefStg_GetExtraInfo"), hres); return hres; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::FindFirst
Returns the location of the root of the first briefcase storage in the system. Returns: S_OK if a briefcase was found S_FALSE to end enumeration Cond: -- */ STDMETHODIMP BriefStg_FindFirst( LPBRIEFCASESTG pstg, LPTSTR pszName, int cchMaxName) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres; TWINRESULT tr; BRFCASEINFO bcinfo; DBG_ENTER(TEXT("BriefStg_FindFirst")); ASSERT(pszName); bcinfo.ulSize = sizeof(bcinfo); tr = Sync_FindFirst(&this->hbrfcaseiter, &bcinfo); switch (tr) { case TR_OUT_OF_MEMORY: hres = ResultFromScode(E_OUTOFMEMORY); break; case TR_SUCCESS: hres = ResultFromScode(S_OK); lstrcpyn(pszName, bcinfo.rgchDatabasePath, cchMaxName); break; case TR_NO_MORE: hres = ResultFromScode(S_FALSE); break; default: hres = ResultFromScode(E_FAIL); break; } DBG_EXIT_HRES(TEXT("BriefStg_FindFirst"), hres); return hres; }
/*----------------------------------------------------------
Purpose: IBriefcaseStg::FindNext
Returns the location of the root of the next briefcase storage in the system. Returns: S_OK if a briefcase was found S_FALSE to end enumeration Cond: -- */ STDMETHODIMP BriefStg_FindNext( LPBRIEFCASESTG pstg, LPTSTR pszName, int cchMaxName) { PBRIEFSTG this = IToClass(BriefStg, bs, pstg); HRESULT hres; TWINRESULT tr; BRFCASEINFO bcinfo; DBG_ENTER(TEXT("BriefStg_FindNext")); ASSERT(pszName); bcinfo.ulSize = sizeof(bcinfo); tr = Sync_FindNext(this->hbrfcaseiter, &bcinfo); switch (tr) { case TR_OUT_OF_MEMORY: hres = ResultFromScode(E_OUTOFMEMORY); break; case TR_SUCCESS: hres = ResultFromScode(S_OK); lstrcpyn(pszName, bcinfo.rgchDatabasePath, cchMaxName); break; case TR_NO_MORE: hres = ResultFromScode(S_FALSE); break; default: hres = ResultFromScode(E_FAIL); break; } DBG_EXIT_HRES(TEXT("BriefStg_FindNext"), hres); return hres; }
//---------------------------------------------------------------------------
// BriefStg class : Vtables
//---------------------------------------------------------------------------
IBriefcaseStgVtbl c_BriefStg_BSVtbl = { BriefStg_QueryInterface, BriefStg_AddRef, BriefStg_Release, BriefStg_Initialize, BriefStg_AddObject, BriefStg_ReleaseObject, BriefStg_UpdateObject, BriefStg_UpdateOnEvent, BriefStg_GetExtraInfo, BriefStg_Notify, BriefStg_FindFirst, BriefStg_FindNext, };
/*----------------------------------------------------------
Purpose: This function is called back from within IClassFactory::CreateInstance() of the default class factory object, which is created by SHCreateClassObject.
Returns: standard Cond: -- */ HRESULT CALLBACK BriefStg_CreateInstance( LPUNKNOWN punkOuter, // Should be NULL for us
REFIID riid, LPVOID * ppvOut) { HRESULT hres = E_FAIL; PBRIEFSTG this; DBG_ENTER_RIID(TEXT("BriefStg_CreateInstance"), riid); // Briefcase storage does not support aggregation.
//
if (punkOuter) { hres = CLASS_E_NOAGGREGATION; *ppvOut = NULL; goto Leave; } this = GAlloc(sizeof(*this)); if (!this) { hres = E_OUTOFMEMORY; *ppvOut = NULL; goto Leave; } this->bs.lpVtbl = &c_BriefStg_BSVtbl; this->cRef = 1; this->pcbs = NULL; this->dwFlags = 0; // Load the engine if it hasn't already been loaded
// (this only returns FALSE if something went wrong)
if (Sync_QueryVTable()) { ENTEREXCLUSIVE(); { // The decrement is in BriefStg_Release()
IncBriefSemaphore(); if (IsFirstBriefSemaphore()) { ProcessIniFile(); // Load settings first
// Initialize cache
if (InitCacheTables()) hres = NOERROR; else hres = E_OUTOFMEMORY; } else { hres = NOERROR; } } LEAVEEXCLUSIVE(); } if (SUCCEEDED(hres)) { // Note that the Release member will free the object, if
// QueryInterface failed.
//
hres = this->bs.lpVtbl->QueryInterface(&this->bs, riid, ppvOut); this->bs.lpVtbl->Release(&this->bs); } else { *ppvOut = NULL; } Leave: DBG_EXIT_HRES(TEXT("BriefStg_CreateInstance"), hres); return hres; // S_OK or E_NOINTERFACE
}
|