/*++ © 1998 Seagate Software, Inc. All rights reserved. Module Name: Wsbdbsys.cpp Abstract: CWsbDbSys class. Author: Ron White [ronw] 1-Jul-1997 Revision History: --*/ #include "stdafx.h" #include "rsevents.h" #include "wsbdbsys.h" #include "wsbdbses.h" #include #include #define MAX_ATTACHED_DB 6 // Set by ESE/JET engine (suppose to be 7) #if !defined(BACKUP_TEST_TIMES) // Normal values #define DEFAULT_AUTOBACKUP_INTERVAL (3 * 60 * 60 * 1000) // 3 hours #define DEFAULT_AUTOBACKUP_IDLE_MINUTES 5 #define DEFAULT_AUTOBACKUP_COUNT_MIN 100 #define DEFAULT_AUTOBACKUP_LOG_COUNT 10 #else // Test values #define DEFAULT_AUTOBACKUP_INTERVAL (4 * 60 * 1000) // 4 minutes #define DEFAULT_AUTOBACKUP_IDLE_MINUTES 1 #define DEFAULT_AUTOBACKUP_COUNT_MIN 5 #define DEFAULT_AUTOBACKUP_LOG_COUNT 4 #endif #define DEFAULT_AUTOBACKUP_COUNT_MAX 500 // Local stuff // ATTACHED_DB_DATA holds information about currently attached DBs typedef struct { CWsbStringPtr Name; // Database name LONG LastOpen; // Sequence number of last open } ATTACHED_DB_DATA; // This static data manages a list of attached databases for this process. // (Future: If we want this list to be managed on a per instance basis, all of // this data should become class members and handled appropriately) static ATTACHED_DB_DATA Attached[MAX_ATTACHED_DB]; static LONG AttachedCount = 0; static CRITICAL_SECTION AttachedCritSect; static BOOL CritSectCreated = FALSE; static BOOL AttachedInit = FALSE; static SHORT AttachedCritSectUsers = 0; static CComCreator< CComObject > SessionFactory; // Local functions static HRESULT AddExtension(OLECHAR** pPath, OLECHAR* Ext); static HRESULT ClearDirectory(const OLECHAR* DirPath); static HRESULT CopyDirectory(const OLECHAR* DirSource, const OLECHAR* DirTarget); static HRESULT DirectoryHasFullBackup(const OLECHAR* DirPath); static HRESULT FileCount(const OLECHAR* DirPath, const OLECHAR* Pattern, ULONG* Count); static HRESULT RenameDirectory(const OLECHAR* OldDir, const OLECHAR* NewDir); // Non-member function initially called for autobackup thread static DWORD WsbDbSysStartAutoBackup( void* pVoid ) { return(((CWsbDbSys*) pVoid)->AutoBackup()); } HRESULT CWsbDbSys::AutoBackup( void ) /*++ Routine Description: Implements an auto-backup loop. Arguments: None. Return Value: Doesn't matter. --*/ { HRESULT hr = S_OK; try { ULONG SleepPeriod = DEFAULT_AUTOBACKUP_INTERVAL; BOOL exitLoop = FALSE; while (! exitLoop) { // Wait for termination event, if timeout occurs, check the sleep period criteria switch (WaitForSingleObject(m_terminateEvent, SleepPeriod)) { case WAIT_OBJECT_0: // Need to terminate WsbTrace(OLESTR("CWsbDbSys::AutoBackup: signaled to terminate\n")); exitLoop = TRUE; break; case WAIT_TIMEOUT: // Check if backup need to be performed WsbTrace(OLESTR("CWsbDbSys::AutoBackup awakened, ChangeCount = %ld\n"), m_ChangeCount); // Don't do a backup if there hasn't been much activity if (DEFAULT_AUTOBACKUP_COUNT_MIN < m_ChangeCount) { LONG DiffMinutes; FILETIME ftNow; LONGLONG NowMinutes; LONGLONG ThenMinutes; // Wait for an idle time GetSystemTimeAsFileTime(&ftNow); NowMinutes = WsbFTtoLL(ftNow) / WSB_FT_TICKS_PER_MINUTE; ThenMinutes = WsbFTtoLL(m_LastChange) / WSB_FT_TICKS_PER_MINUTE; DiffMinutes = static_cast(NowMinutes - ThenMinutes); WsbTrace(OLESTR("CWsbDbSys::AutoBackup idle minutes = %ld\n"), DiffMinutes); if (DEFAULT_AUTOBACKUP_IDLE_MINUTES < DiffMinutes || DEFAULT_AUTOBACKUP_COUNT_MAX < m_ChangeCount) { hr = Backup(NULL, 0); if (S_OK != hr) { // Just trace and go back to wait for the next round... WsbTrace(OLESTR("CWsbDbSys::AutoBackup: Backup failed, hr=<%ls>\n"), WsbHrAsString(hr)); } SleepPeriod = DEFAULT_AUTOBACKUP_INTERVAL;; } else { // Reduce the sleep time so we catch the next idle time ULONG SleepMinutes = SleepPeriod / (1000 * 60); if (SleepMinutes > (DEFAULT_AUTOBACKUP_IDLE_MINUTES * 2)) { SleepPeriod /= 2; } } } break; // end of timeout case case WAIT_FAILED: default: WsbTrace(OLESTR("CWsbDbSys::AutoBackup: WaitForSingleObject returned error %lu\n"), GetLastError()); exitLoop = TRUE; break; } // end of switch } // end of while } WsbCatch(hr); return(hr); } HRESULT CWsbDbSys::Backup( IN OLECHAR* path, IN ULONG flags ) /*++ Implements: IWsbDbSys::Backup --*/ { HRESULT hr = S_OK; char* backup_path = NULL; WsbTraceIn(OLESTR("CWsbDbSys::Backup"), OLESTR("path = <%ls>, flags = %lx"), path, flags); try { CWsbStringPtr BackupDir; JET_ERR jstat = JET_errSuccess; WsbAffirm(m_jet_initialized, WSB_E_NOT_INITIALIZED); // Set and save the backup path; make sure it exists if (NULL != path) { m_BackupPath = path; } CreateDirectory(m_BackupPath, NULL); // Start the automatic backup thread if requested if (flags & IDB_BACKUP_FLAG_AUTO) { // Don't start AutoBackup thread if it's already running if (0 == m_AutoThread) { DWORD threadId; // Create termination event for auto-backup thread WsbAffirmHandle(m_terminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL)); WsbAffirm((m_AutoThread = CreateThread(0, 0, WsbDbSysStartAutoBackup, (void*) this, 0, &threadId)) != 0, HRESULT_FROM_WIN32(GetLastError())); } // Do a full backup to a temporary directory } else if (flags & IDB_BACKUP_FLAG_FORCE_FULL) { BOOL UsedTempDir = FALSE; // Don't wipe out an existing backup -- if the normal backup // directory contains a full backup, do the full backup to // the .ful directory BackupDir = m_BackupPath; WsbAffirm(0 != (WCHAR *)BackupDir, E_OUTOFMEMORY); if (S_OK == DirectoryHasFullBackup(BackupDir)) { WsbAffirmHr(AddExtension(&BackupDir, L".ful")); UsedTempDir = TRUE; } // Make sure the directory exists (should check for errors?) CreateDirectory(BackupDir, NULL); // Make sure the directory is empty (the call to JetBackup will // fail if it's not) WsbAffirmHr(ClearDirectory(BackupDir)); // Convert to narrow char string for parameter WsbAffirmHr(wsb_db_jet_fix_path(BackupDir, NULL, &backup_path)); WsbTrace(OLESTR("CWsbDbSys::Backup: backup_path = <%hs>\n"), backup_path); // Do backup WsbAffirm(NULL != m_BackupEvent, WSB_E_IDB_WRONG_BACKUP_SETTINGS); DWORD status = WaitForSingleObject(m_BackupEvent, EVENT_WAIT_TIMEOUT); DWORD errWait; switch(status) { case WAIT_OBJECT_0: // Expected case - do Backup jstat = JetBackupInstance(m_jet_instance, backup_path, 0, NULL); if (! SetEvent(m_BackupEvent)) { // Don't abort, just trace error WsbTraceAlways(OLESTR("CWsbDbSys::Backup: SetEvent returned unexpected error %lu\n"), GetLastError()); } WsbAffirmHr(jet_error(jstat)); break; case WAIT_TIMEOUT: // Timeout - don't do backup WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object timed out after %lu ms\n"), EVENT_WAIT_TIMEOUT); WsbThrow(E_ABORT); break; case WAIT_FAILED: errWait = GetLastError(); WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned error %lu\n"), errWait); WsbThrow(HRESULT_FROM_WIN32(errWait)); break; default: WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned unexpected status %lu\n"), status); WsbThrow(E_UNEXPECTED); break; } // Full backup worked -- copy to real backup directory if (UsedTempDir) { try { WsbAffirmHr(ClearDirectory(m_BackupPath)); WsbAffirmHr(CopyDirectory(BackupDir, m_BackupPath)); WsbAffirmHr(ClearDirectory(BackupDir)); // Try to delete temporary directory (may fail) DeleteFile(BackupDir); BackupDir = m_BackupPath; } WsbCatch(hr); } WsbLogEvent(WSB_MESSAGE_IDB_BACKUP_FULL, 0, NULL, WsbAbbreviatePath(BackupDir, 120), NULL); m_ChangeCount = 0; WsbAffirmHr(hr); // Try an incremental backup } else { ULONG LogCount; BOOL TryFullBackup = FALSE; WsbAffirmHr(FileCount(m_BackupPath, L"*.log", &LogCount)); if (LogCount > DEFAULT_AUTOBACKUP_LOG_COUNT || S_FALSE == DirectoryHasFullBackup(m_BackupPath)) { // Do a full backup instead of the incremental if there // are already too many log files, or there's no full // backup in the backup directory (which means the incremental // wouldn't work anyway) TryFullBackup = TRUE; } else { WsbTrace(OLESTR("CWsbDbSys::Backup, trying incremental backup\n")); // Convert to narrow char string for parameter WsbAffirmHr(wsb_db_jet_fix_path(m_BackupPath, NULL, &backup_path)); WsbTrace(OLESTR("CWsbDbSys::Backup: backup_path = <%hs>\n"), backup_path); WsbAffirm(NULL != m_BackupEvent, WSB_E_IDB_WRONG_BACKUP_SETTINGS); DWORD status = WaitForSingleObject(m_BackupEvent, EVENT_WAIT_TIMEOUT); DWORD errWait; switch(status) { case WAIT_OBJECT_0: // Expected case - do Backup jstat = JetBackupInstance(m_jet_instance, backup_path, JET_bitBackupIncremental, NULL); if (! SetEvent(m_BackupEvent)) { // Don't abort, just trace error WsbTraceAlways(OLESTR("CWsbDbSys::Backup: SetEvent returned unexpected error %lu\n"), GetLastError()); } break; case WAIT_TIMEOUT: // Timeout - don't do backup WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object timed out after %lu ms\n"), EVENT_WAIT_TIMEOUT); WsbThrow(E_ABORT); break; case WAIT_FAILED: errWait = GetLastError(); WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned error %lu\n"), errWait); WsbThrow(HRESULT_FROM_WIN32(errWait)); break; default: WsbTraceAlways(OLESTR("CWsbDbSys::Backup, Wait for Single Object returned unexpected status %lu\n"), status); WsbThrow(E_UNEXPECTED); break; } // Check for an error. if (JET_errSuccess != jstat) { if (JET_errMissingFullBackup == jstat) { // Full backup need to be performed WsbLogEvent(WSB_MESSAGE_IDB_MISSING_FULL_BACKUP, 0, NULL, WsbAbbreviatePath(m_BackupPath, 120), NULL); } else { // Unknown error of incremental backup. Try a full backup anyway WsbLogEvent(WSB_MESSAGE_IDB_INCREMENTAL_BACKUP_FAILED, 0, NULL, WsbAbbreviatePath(m_BackupPath, 120), WsbLongAsString(jstat), NULL ); } TryFullBackup = TRUE; } else { // The incremental backup worked WsbLogEvent(WSB_MESSAGE_IDB_BACKUP_INCREMENTAL, 0, NULL, WsbAbbreviatePath(m_BackupPath, 120), NULL); m_ChangeCount = 0; } } // Try full backup? if (TryFullBackup) { WsbAffirmHr(Backup(NULL, IDB_BACKUP_FLAG_FORCE_FULL)); } } } WsbCatchAndDo(hr, WsbLogEvent(WSB_MESSAGE_IDB_BACKUP_FAILED, 0, NULL, WsbAbbreviatePath(m_BackupPath, 120), NULL); ); if (NULL != backup_path) { WsbFree(backup_path); } WsbTraceOut(OLESTR("CWsbDbSys::Backup"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CWsbDbSys::FinalConstruct( void ) /*++ Implements: CComObjectRoot::FinalConstruct --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CWsbDbSys::FinalConstruct"), OLESTR("") ); try { m_AutoThread = 0; m_terminateEvent = NULL; m_ChangeCount = 0; m_jet_initialized = FALSE; m_jet_instance = JET_instanceNil; m_BackupEvent = NULL; try { // Initialize critical sections (global resource, so init only for first user) if (AttachedCritSectUsers == 0) { WsbAffirmStatus(InitializeCriticalSectionAndSpinCount (&AttachedCritSect, 1000)); CritSectCreated = TRUE; } AttachedCritSectUsers++; } catch(DWORD status) { AttachedCritSectUsers--; WsbLogEvent(status, 0, NULL, NULL); switch (status) { case STATUS_NO_MEMORY: WsbThrow(E_OUTOFMEMORY); break; default: WsbThrow(E_UNEXPECTED); break; } } } WsbCatch(hr); WsbTraceOut(OLESTR("CWsbDbSys::FinalConstruct"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } void CWsbDbSys::FinalRelease( void ) /*++ Implements: CComObjectRoot::FinalRelease --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CWsbDbSys::FinalRelease"), OLESTR("")); try { // Make sure that Terminate was called if (m_jet_initialized == TRUE) { WsbAffirmHr(Terminate()); } } WsbCatch(hr); // Global resource, so delete only for last user AttachedCritSectUsers--; if ((AttachedCritSectUsers == 0) && CritSectCreated) { DeleteCriticalSection(&AttachedCritSect); } WsbTraceOut(OLESTR("CWsbDbSys::FinalRelease"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); } HRESULT CWsbDbSys::Init( IN OLECHAR* path, IN ULONG flags ) /*++ Implements: IWsbDbSys::Init --*/ { HRESULT hr = S_OK; char* log_path = NULL; static BOOL bFirstTime = TRUE; static int nInstance = 0; WsbTraceIn(OLESTR("CWsbDbSys::Init"), OLESTR("path = <%ls>"), path); try { CWsbStringPtr dir; JET_ERR jstat = JET_errSuccess; // Initialize the Jet engine just once per Jet instance WsbAffirm(!m_jet_initialized, E_FAIL); // Initialize backup event, unless Jet backup is not required for this isntance if (! (flags & IDB_SYS_INIT_FLAG_NO_BACKUP)) { // Event should already exist - it is created in the CRssJetWriter constructor WsbAffirmHandle(m_BackupEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, HSM_IDB_STATE_EVENT)); } // WsbDbSys represents one Jet instance. // However, some Jet initialization should be done only once per process, // before the first instance is being created. if (bFirstTime) { bFirstTime = FALSE; // Increase the default number of maximum Jet sesions for the process // TEMPORARY: Can this be set separately per instance? jstat = JetSetSystemParameter(0, 0, JET_paramCacheSizeMin , (IDB_MAX_NOF_SESSIONS*4), NULL); WsbTrace(OLESTR("CWsbDbSys::Init, JetSetSystemParameter(CacheSizeMax) = %ld\n"), jstat); WsbAffirmHr(jet_error(jstat)); jstat = JetSetSystemParameter(0, 0, JET_paramMaxSessions, IDB_MAX_NOF_SESSIONS, NULL); WsbTrace(OLESTR("CWsbDbSys::Init, JetSetSystemParameter(MaxSessions) = %ld\n"), jstat); WsbAffirmHr(jet_error(jstat)); // Tell Jet we are going to use multiple instances jstat = JetEnableMultiInstance(NULL, 0, NULL); WsbAffirmHr(jet_error(jstat)); } // Here start the per-instance initialization. // First step is creating the instance // Use a numeric counter as instance name - we care only that the name is unique WsbAssert(JET_instanceNil == m_jet_instance, E_FAIL); nInstance++; char szInstance[10]; sprintf(szInstance, "%d", nInstance); WsbTrace(OLESTR("CWsbDbSys::Init, Jet instance name = <%hs>\n"), szInstance); jstat = JetCreateInstance(&m_jet_instance, szInstance); WsbAffirmHr(jet_error(jstat)); // Set some per-instance parameters: // Create path for log directory (same path is also used for system files and temp files) WsbAffirm(NULL != path, E_INVALIDARG); m_InitPath = path; m_BackupPath = m_InitPath; WsbAffirmHr(AddExtension(&m_BackupPath, L".bak")); WsbTrace(OLESTR("CWsbDbSys::Init, BackupPath = <%ls>\n"), (WCHAR *)m_BackupPath); WsbAffirmHr(wsb_db_jet_fix_path(path, OLESTR(""), &log_path)); dir = log_path; // Convert to WCHAR // Make sure the directory exists. WsbTrace(OLESTR("CWsbDbSys::Init, Creating dir = <%ls>\n"), (WCHAR *)dir); if (! CreateDirectory(dir, NULL)) { DWORD status = GetLastError(); if ((status == ERROR_ALREADY_EXISTS) || (status == ERROR_FILE_EXISTS)) { status = NO_ERROR; } WsbAffirmNoError(status); } ULONG checkpointDepth; ULONG logFileSize = 128; // In kilobytes if (! (flags & IDB_SYS_INIT_FLAG_NO_LOGGING)) { WsbTrace(OLESTR("CWsbDbSys::Init, LogFilePath = <%hs>\n"), log_path); jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramLogFilePath, 0, log_path); WsbTrace(OLESTR("CWsbDbSys::Init, JetSetSystemParameter(LogFilePath) = %ld\n"), jstat); WsbAffirmHr(jet_error(jstat)); // Use circular logging for "limited" logging if (flags & IDB_SYS_INIT_FLAG_LIMITED_LOGGING) { logFileSize = 512; // Increase the log file size jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramCircularLog, 1, NULL); WsbAffirmHr(jet_error(jstat)); WsbTrace(OLESTR("CWsbDbSys::Init: set circular logging\n")); // Set the amount of logging allowed before a check point // to allow about 4 log files // (the check point depth is set in bytes.) checkpointDepth = 4 * logFileSize * 1024; jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramCheckpointDepthMax, checkpointDepth, NULL); WsbAffirmHr(jet_error(jstat)); WsbTrace(OLESTR("CWsbDbSys::Init: set CheckpointDepthMax = %ld\n"), checkpointDepth); } } else { jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramRecovery, 0, "off"); WsbAffirmHr(jet_error(jstat)); WsbTrace(OLESTR("CWsbDbSys::Init: set JET_paramRecovery to 0 (no logging)\n")); } // Set parameters for where to put auxiliary data WsbTrace(OLESTR("CWsbDbSys::Init, SystemPath = <%hs>\n"), log_path); jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramSystemPath, 0, log_path); WsbAffirmHr(jet_error(jstat)); // The next one, for some unknown reason, needs a file name at the end of the path WsbAffirmHr(dir.Append("\\temp.edb")); WsbAffirmHr(dir.CopyTo(&log_path)); WsbTrace(OLESTR("CWsbDbSys::Init, TempPath = <%hs>\n"), log_path); jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramTempPath, 0, log_path); WsbAffirmHr(jet_error(jstat)); if (! (flags & IDB_SYS_INIT_FLAG_NO_LOGGING)) { // Set the log file size (in KB). The minimum seems to be 128KB. jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramLogFileSize, logFileSize, NULL); WsbAffirmHr(jet_error(jstat)); WsbTrace(OLESTR("CWsbDbSys::Init: set logFileSize to %ld Kb\n"), logFileSize); } // Set parameter for deleting out-of-range log files. // These files may exist after a restore from a db backup without clearing the db directory first if (! (flags & IDB_SYS_INIT_FLAG_NO_BACKUP)) { jstat = JetSetSystemParameter(&m_jet_instance, 0, JET_paramDeleteOutOfRangeLogs, 1, NULL); WsbAffirmHr(jet_error(jstat)); WsbTrace(OLESTR("CWsbDbSys::Init: set delete out-of-range logs\n")); } // Initialize the DB instance jstat = JetInit(&m_jet_instance); hr = jet_error(jstat); // If this failed, report the error if (!SUCCEEDED(hr)) { if (flags & IDB_SYS_INIT_FLAG_SPECIAL_ERROR_MSG) { // Special message for FSA WsbLogEvent(WSB_E_IDB_DELETABLE_DATABASE_CORRUPT, 0, NULL, NULL); WsbThrow(WSB_E_RESOURCE_UNAVAILABLE); } else { WsbThrow(hr); } } WsbTrace(OLESTR("CWsbDbSys::Init: jet instance = %p\n"), (LONG_PTR)m_jet_instance); m_jet_initialized = TRUE; // Create a session for internal use of this instance WsbAffirmHr(NewSession(&m_pWsbDbSession)); WsbTrace(OLESTR("CWsbDbSys::Init, m_pWsbDbSession = %p\n"), (IWsbDbSession*)m_pWsbDbSession); } WsbCatchAndDo(hr, WsbLogEvent(WSB_MESSAGE_IDB_INIT_FAILED, 0, NULL, WsbAbbreviatePath(m_InitPath, 120), NULL); ); if (NULL != log_path) { WsbFree(log_path); } WsbTraceOut(OLESTR("CWsbDbSys::Init"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CWsbDbSys::Terminate( void ) /*++ Implements: IWsbDbSys::Terminate --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CWsbDbSys::Terminate"), OLESTR("")); try { // If wasn't initialized or alreday cleaned up - just get out if (m_jet_initialized == FALSE) { WsbTrace(OLESTR("CWsbDbSys::Terminate - this insatnce is not initialized")); WsbThrow(S_OK); } // Terminate the auto-backup thread if (m_AutoThread) { // Signal thread to terminate SetEvent(m_terminateEvent); // Wait for the thread, if it doesn't terminate gracefully - kill it switch (WaitForSingleObject(m_AutoThread, 20000)) { case WAIT_FAILED: { WsbTrace(OLESTR("CWsbDbSys::Terminate: WaitForSingleObject returned error %lu\n"), GetLastError()); } // fall through... case WAIT_TIMEOUT: { WsbTrace(OLESTR("CWsbDbSys::Terminate: force terminating of auto-backup thread.\n")); DWORD dwExitCode; if (GetExitCodeThread( m_AutoThread, &dwExitCode)) { if (dwExitCode == STILL_ACTIVE) { // thread still active if (!TerminateThread (m_AutoThread, 0)) { WsbTrace(OLESTR("CWsbDbSys::Terminate: TerminateThread returned error %lu\n"), GetLastError()); } } } else { WsbTrace(OLESTR("CWsbDbSys::Terminate: GetExitCodeThread returned error %lu\n"), GetLastError()); } break; } default: // Thread terminated gracefully break; } // Best effort done for terminating auto-backup thread CloseHandle(m_AutoThread); m_AutoThread = 0; } if (m_terminateEvent != NULL) { CloseHandle(m_terminateEvent); m_terminateEvent = NULL; } // Detach DBs before exiting so they don't automatically get // reattached the next time we start up if (m_pWsbDbSession) { JET_SESID sid; CComQIPtr pSessionPriv = m_pWsbDbSession; WsbAffirmPointer(pSessionPriv); WsbAffirmHr(pSessionPriv->GetJetId(&sid)); // Clean up the Attached data if (AttachedInit) { EnterCriticalSection(&AttachedCritSect); for (int i = 0; i < MAX_ATTACHED_DB; i++) { Attached[i].Name.Free(); Attached[i].LastOpen = 0; } JetDetachDatabase(sid, NULL); AttachedInit = FALSE; LeaveCriticalSection(&AttachedCritSect); } // Release the global session for this instance m_pWsbDbSession = 0; } // Terminate Jet JetTerm(m_jet_instance); m_jet_initialized = FALSE; m_jet_instance = JET_instanceNil; } WsbCatch(hr); if (m_BackupEvent) { CloseHandle(m_BackupEvent); m_BackupEvent = NULL; } WsbTraceOut(OLESTR("CWsbDbSys::Terminate"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return (hr); } HRESULT CWsbDbSys::NewSession( OUT IWsbDbSession** ppSession ) /*++ Implements: IWsbDbSys::NewSession --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CWsbDbSys::NewSession"), OLESTR("")); try { WsbAffirm(0 != ppSession, E_POINTER); WsbAffirmHr(SessionFactory.CreateInstance(NULL, IID_IWsbDbSession, (void**)ppSession)); CComQIPtr pSessionPriv = *ppSession; WsbAffirmPointer(pSessionPriv); WsbAffirmHr(pSessionPriv->Init(&m_jet_instance)); } WsbCatch(hr); WsbTraceOut(OLESTR("CWsbDbSys::NewSession"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CWsbDbSys::GetGlobalSession( OUT IWsbDbSession** ppSession ) /*++ Implements: IWsbDbSys::GetGlobalSession --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CWsbDbSys::GetGlobalSession"), OLESTR("")); // // If the Task Manager has been created, return the pointer. Otherwise, // fail. try { WsbAssert(0 != ppSession, E_POINTER); *ppSession = m_pWsbDbSession; WsbAffirm(m_pWsbDbSession != 0, E_FAIL); m_pWsbDbSession.p->AddRef(); } WsbCatch(hr); WsbTraceOut(OLESTR("CWsbDbSys::GetGlobalSession"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return (hr); } HRESULT CWsbDbSys::Restore( IN OLECHAR* fromPath, IN OLECHAR* toPath ) /*++ Implements: IWsbDbSys::Restore --*/ { HRESULT hr = S_OK; char* backup_path = NULL; char* restore_path = NULL; WsbTraceIn(OLESTR("CWsbDbSys::Restore"), OLESTR("fromPath = <%ls>, toPath = <%ls>"), fromPath, toPath); try { CWsbStringPtr dir; JET_ERR jstat; // This is only allowed before Init WsbAffirm(!m_jet_initialized, E_UNEXPECTED); WsbAffirm(NULL != fromPath, E_POINTER); WsbAffirm(NULL != toPath, E_POINTER); // Convert pathes WsbAffirmHr(wsb_db_jet_fix_path(fromPath, OLESTR(""), &backup_path)); WsbAffirmHr(wsb_db_jet_fix_path(toPath, OLESTR(""), &restore_path)); // Make sure the target directory exists. Should check for error. dir = restore_path; CreateDirectory(dir, NULL); jstat = JetRestoreInstance(m_jet_instance, backup_path, restore_path, NULL); WsbAffirmHr(jet_error(jstat)); } WsbCatch(hr); if (NULL != backup_path) { WsbFree(backup_path); } if (NULL != restore_path) { WsbFree(restore_path); } WsbTraceOut(OLESTR("CWsbDbSys::Restore"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CWsbDbSys::IncrementChangeCount( void ) /*++ Implements: IWsbDbSysPriv::IncrementChangeCount Routine Description: Increments the write count used by AutoBackup. Arguments: None. Return Value: S_OK --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CWsbDbSys::IncrementChangeCount"), OLESTR("count = %ld"), m_ChangeCount); try { m_ChangeCount++; GetSystemTimeAsFileTime(&m_LastChange); } WsbCatch(hr); WsbTraceOut(OLESTR("CWsbDbSys::IncrementChangeCount"), OLESTR("count = %ld"), m_ChangeCount); return(hr); } HRESULT CWsbDbSys::DbAttachedAdd( OLECHAR* name, BOOL attach) /*++ Implements: IWsbDbSysPriv::DbAttachedAdd Routine Description: Make sure DB is attached and update the last-used count. --*/ { HRESULT hr = S_OK; char* jet_name = NULL; WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedAdd"), OLESTR("name = %ls, attach = %ls"), name, WsbBoolAsString(attach)); try { int i; int i_empty = -1; int i_found = -1; LONG min_count = AttachedCount + 1; CWsbStringPtr match_name; WsbAssert(name, E_POINTER); // Make sure the list is initialized if (!AttachedInit) { WsbAffirmHr(DbAttachedInit()); } // Convert the name WsbAffirmHr(wsb_db_jet_fix_path(name, L"." IDB_DB_FILE_SUFFIX, &jet_name)); match_name = jet_name; // See if it's already in the list; look for an empty slot; find the // least-recently used DB EnterCriticalSection(&AttachedCritSect); for (i = 0; i < MAX_ATTACHED_DB; i++) { // Empty slot? if (!Attached[i].Name) { if (-1 == i_empty) { // Save the first one found i_empty = i; } } else { // Gather some data for later if (Attached[i].LastOpen < min_count) { min_count = Attached[i].LastOpen; } // Already in list? if (match_name.IsEqual(Attached[i].Name)) { i_found = i; } } } // Make sure the count isn't going to overflow if (LONG_MAX == AttachedCount + 1) { WsbAffirm(0 < min_count, E_FAIL); // Adjust counts down to avoid overflow for (i = 0; i < MAX_ATTACHED_DB; i++) { if (min_count <= Attached[i].LastOpen) { Attached[i].LastOpen -= min_count; } } AttachedCount -= min_count; } AttachedCount++; // If it's already in the list, update the info if (-1 != i_found) { WsbTrace(OLESTR("CWsbDbSys::DbAttachedAdd: i_found = %d\n"), i_found); Attached[i_found].LastOpen = AttachedCount; // If there's an empty slot, use it } else if (-1 != i_empty) { WsbTrace(OLESTR("CWsbDbSys::DbAttachedAdd: i_empty = %d\n"), i_empty); if (attach) { JET_ERR jstat; JET_SESID sid; WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED); CComQIPtr pSessionPriv = m_pWsbDbSession; WsbAffirmPointer(pSessionPriv); WsbAffirmHr(pSessionPriv->GetJetId(&sid)); jstat = JetAttachDatabase(sid, jet_name, 0); if (JET_errFileNotFound == jstat) { WsbThrow(STG_E_FILENOTFOUND); } else if (JET_wrnDatabaseAttached == jstat) { WsbTrace(OLESTR("CWsbDbSys::DbAttachedAdd: DB is already attached\n")); // No problem } else { WsbAffirmHr(jet_error(jstat)); } } Attached[i_empty].Name = match_name; Attached[i_empty].LastOpen = AttachedCount; // Try to detach the oldest DB first } else { WsbAffirmHr(DbAttachedEmptySlot()); WsbAffirmHr(DbAttachedAdd(name, attach)); } } WsbCatch(hr); if (jet_name) { WsbFree(jet_name); } LeaveCriticalSection(&AttachedCritSect); WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedAdd"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CWsbDbSys::DbAttachedEmptySlot( void) /*++ Implements: IWsbDbSysPriv::DbAttachedEmptySlot Routine Description: Force an empty slot in the attached list even if this means detaching a DB. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedEmptySlot"), OLESTR("")); // Don't worry about it if we're not initialized yet -- // all the slots are empty if (AttachedInit) { EnterCriticalSection(&AttachedCritSect); try { BOOL has_empty = FALSE; int i; int i_oldest; LONG oldest_count; // Find an empty slot or the oldest that is not currently open reloop: i_oldest = -1; oldest_count = AttachedCount; for (i = 0; i < MAX_ATTACHED_DB; i++) { if (!Attached[i].Name) { has_empty = TRUE; break; } else if (Attached[i].LastOpen < oldest_count) { i_oldest = i; oldest_count = Attached[i].LastOpen; } } // If there's no empty slot, try detaching the oldest WsbTrace(OLESTR("CWsbDbSys::DbAttachedEmptySlot: has_empty = %ls, i = %d, i_oldest = %d\n"), WsbBoolAsString(has_empty), i, i_oldest); if (!has_empty) { JET_ERR jstat; char* name; JET_SESID sid; WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED); CComQIPtr pSessionPriv = m_pWsbDbSession; WsbAffirmPointer(pSessionPriv); WsbAffirmHr(pSessionPriv->GetJetId(&sid)); WsbAffirm(-1 != i_oldest, WSB_E_IDB_TOO_MANY_DB); WsbAffirmHr(wsb_db_jet_fix_path(Attached[i_oldest].Name, L"." IDB_DB_FILE_SUFFIX, &name)); jstat = JetDetachDatabase(sid, name); WsbFree(name); WsbTrace(OLESTR("CWsbDbSys::DbAttachedEmptySlot: JetDetachDatabase = %ld\n"), (LONG)jstat); if (JET_errDatabaseInUse == jstat) { WsbTrace(OLESTR("CWsbDbSys::DbAttachedEmptySlot: DB in use; try again\n")); Attached[i_oldest].LastOpen = AttachedCount; goto reloop; } else if (JET_errDatabaseNotFound != jstat) { WsbAffirmHr(jet_error(jstat)); } Attached[i_oldest].Name.Free(); Attached[i_oldest].LastOpen = 0; } } WsbCatch(hr); LeaveCriticalSection(&AttachedCritSect); } WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedEmptySlot"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CWsbDbSys::DbAttachedInit( void) /*++ Implements: IWsbDbSysPriv::DbAttachedInit Routine Description: Initialize the attached-DB-list data. --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedInit"), OLESTR("")); EnterCriticalSection(&AttachedCritSect); try { if (!AttachedInit) { ULONG actual = 0; int i; JET_ERR jstat; JET_SESID sid; WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED); CComQIPtr pSessionPriv = m_pWsbDbSession; WsbAffirmPointer(pSessionPriv); WsbAffirmHr(pSessionPriv->GetJetId(&sid)); // Initialize data for (i = 0; i < MAX_ATTACHED_DB; i++) { Attached[i].Name.Free(); Attached[i].LastOpen = 0; } // Make sure there aren't pre-attached DBs jstat = JetDetachDatabase(sid, NULL); WsbTrace(OLESTR("CWsbDbSys::DbAttachedInit: JetDetachDatabase(NULL) = %ld\n"), (LONG)jstat); WsbAffirmHr(jet_error(jstat)); AttachedInit = TRUE; } } WsbCatch(hr); LeaveCriticalSection(&AttachedCritSect); WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedInit"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } HRESULT CWsbDbSys::DbAttachedRemove( OLECHAR* name) /*++ Implements: IWsbDbSysPriv::DbAttachedRemove Routine Description: Detach a DB (if attached). --*/ { HRESULT hr = S_FALSE; char* jet_name = NULL; WsbTraceIn(OLESTR("CWsbDbSys::DbAttachedRemove"), OLESTR("name = %ls"), name); try { int i; CWsbStringPtr match_name; WsbAssert(name, E_POINTER); WsbAffirm(AttachedInit, S_FALSE); // Convert the name WsbAffirmHr(wsb_db_jet_fix_path(name, L"." IDB_DB_FILE_SUFFIX, &jet_name)); match_name = jet_name; // See if it's in the list EnterCriticalSection(&AttachedCritSect); for (i = 0; i < MAX_ATTACHED_DB; i++) { if (Attached[i].Name) { if (match_name.IsEqual(Attached[i].Name)) { JET_ERR jstat; JET_SESID sid; WsbTrace(OLESTR("CWsbDbSys::DbAttachedRemove: found DB, index = %d\n"), i); WsbAffirm(m_pWsbDbSession, WSB_E_NOT_INITIALIZED); CComQIPtr pSessionPriv = m_pWsbDbSession; WsbAffirmPointer(pSessionPriv); WsbAffirmHr(pSessionPriv->GetJetId(&sid)); jstat = JetDetachDatabase(sid, jet_name); WsbTrace(OLESTR("CWsbDbSys::DbAttachedRemove: JetDetachDatabase = %ld\n"), (LONG)jstat); if (JET_errDatabaseNotFound != jstat) { WsbAffirmHr(jet_error(jstat)); hr = S_OK; } Attached[i].Name.Free(); Attached[i].LastOpen = 0; break; } } } } WsbCatch(hr); if (jet_name) { WsbFree(jet_name); } LeaveCriticalSection(&AttachedCritSect); WsbTraceOut(OLESTR("CWsbDbSys::DbAttachedRemove"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } // wsb_db_jet_check_error - check for a jet error; return S_OK for no error; // print error to trace otherwise HRESULT wsb_db_jet_check_error(LONG jstat, char *fileName, DWORD lineNo) { HRESULT hr = S_OK; if (jstat != JET_errSuccess) { WsbTrace(OLESTR("Jet error = %ld (%hs line %ld)\n"), jstat, fileName, lineNo); // Convert JET error to IDB error for some common values switch (jstat) { case JET_errDiskFull: case JET_errLogDiskFull: hr = WSB_E_IDB_DISK_FULL; break; case JET_errDatabaseNotFound: hr = WSB_E_IDB_FILE_NOT_FOUND; break; case JET_errDatabaseInconsistent: case JET_errPageNotInitialized: case JET_errReadVerifyFailure: case JET_errDatabaseCorrupted: case JET_errBadLogSignature: case JET_errBadDbSignature: case JET_errBadCheckpointSignature: case JET_errCheckpointCorrupt: case JET_errMissingPatchPage: case JET_errBadPatchPage: hr = WSB_E_IDB_DATABASE_CORRUPT; break; case JET_errWriteConflict: hr = WSB_E_IDB_UPDATE_CONFLICT; break; default: hr = WSB_E_IDB_IMP_ERROR; break; } // Log this error in the event log if (g_WsbLogLevel) { CWsbStringPtr str; WsbSetEventInfo(fileName, lineNo, VER_PRODUCTBUILD, RS_BUILD_VERSION); \ str = WsbLongAsString(jstat); if (WSB_E_IDB_IMP_ERROR != hr) { str.Prepend(" ("); str.Prepend(WsbHrAsString(hr)); str.Append(")"); } WsbTraceAndLogEvent(WSB_MESSAGE_IDB_ERROR, 0, NULL, static_cast(str), NULL); } } return(hr); } // wsb_db_jet_fix_path - convert database path name from OLESTR to char*, // change (or add) extension. // Returns HRESULT // // NOTE: OLECHAR* is passed in, but char* is returned HRESULT wsb_db_jet_fix_path(OLECHAR* path, OLECHAR* ext, char** new_path) { HRESULT hr = S_OK; try { CWsbStringPtr string; int tlen; WsbAssertPointer(path); WsbAssertPointer(new_path); // Add extension if given string = path; WsbAffirm(0 != (WCHAR *)string, E_OUTOFMEMORY); if (ext) { WsbAffirmHr(AddExtension(&string, ext)); } // Allocate char string tlen = (wcslen(string) + 1) * sizeof(OLECHAR); *new_path = (char*)WsbAlloc(tlen); WsbAffirm(*new_path, E_OUTOFMEMORY); // Convert from wide char to char if (wcstombs(*new_path, string, tlen) == (size_t)-1) { WsbFree(*new_path); *new_path = NULL; WsbThrow(WSB_E_STRING_CONVERSION); } } WsbCatch(hr); return(hr); } // Local functions // AddExtension - add (or replace) the file extension to the path. // If Ext is NULL, remove the existing extension. // // Return S_OK if no errors occurred. static HRESULT AddExtension(OLECHAR** pPath, OLECHAR* Ext) { HRESULT hr = S_OK; WsbTraceIn(OLESTR("AddExtension(wsbdbsys)"), OLESTR("Path = \"%ls\", Ext = \"%ls\""), WsbAbbreviatePath(*pPath, 120), Ext ); try { int elen; int len; OLECHAR* new_path; OLECHAR* pc; OLECHAR* pc2; int tlen; WsbAssertPointer(pPath); WsbAssertPointer(*pPath); // Allocate string and copy path len = wcslen(*pPath); if (Ext) { elen = wcslen(Ext); } else { elen = 0; } tlen = (len + elen + 1) * sizeof(OLECHAR); new_path = static_cast(WsbAlloc(tlen)); WsbAffirm(new_path, E_OUTOFMEMORY); wcscpy(new_path, *pPath); // Remove old extension (if there) pc = wcsrchr(new_path, L'.'); pc2 = wcsrchr(new_path, L'\\'); if (pc && (!pc2 || pc2 < pc)) { *pc = L'\0'; } // Add the new extension (if given) if (Ext) { wcscat(new_path, Ext); } // Return the new path WsbFree(*pPath); *pPath = new_path; } WsbCatch(hr); WsbTraceOut(OLESTR("AddExtension(wsbdbsys)"), OLESTR("hr =<%ls>, new path = \"%ls\""), WsbHrAsString(hr), WsbAbbreviatePath(*pPath, 120)); return(hr); } // ClearDirectory - delete all files in a directory // Return S_OK if no errors occurred. static HRESULT ClearDirectory(const OLECHAR* DirPath) { DWORD err; WIN32_FIND_DATA FindData; HANDLE hFind = 0; HRESULT hr = S_OK; int nDeleted = 0; int nSkipped = 0; CWsbStringPtr SearchPath; WsbTraceIn(OLESTR("ClearDirectory(wsbdbsys)"), OLESTR("Path = <%ls>"), WsbAbbreviatePath(DirPath, 120)); try { SearchPath = DirPath; SearchPath.Append("\\*"); hFind = FindFirstFile(SearchPath, &FindData); if (INVALID_HANDLE_VALUE == hFind) { hFind = 0; err = GetLastError(); WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindFirstFile(%ls) failed, error = %ld\n"), static_cast(SearchPath), err); WsbThrow(HRESULT_FROM_WIN32(err)); } while (TRUE) { if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN)) { nSkipped++; } else { CWsbStringPtr DeletePath; DeletePath = DirPath; DeletePath.Append("\\"); DeletePath.Append(FindData.cFileName); if (!DeleteFile(DeletePath)) { err = GetLastError(); WsbTrace(OLESTR("ClearDirectory(wsbdbsys): DeleteFile(%ls) failed, error = %ld\n"), static_cast(DeletePath), err); WsbThrow(HRESULT_FROM_WIN32(err)); } nDeleted++; } if (!FindNextFile(hFind, &FindData)) { err = GetLastError(); if (ERROR_NO_MORE_FILES == err) break; WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindNextFile failed, error = %ld\n"), err); WsbThrow(HRESULT_FROM_WIN32(err)); } } } WsbCatch(hr); if (0 != hFind) { FindClose(hFind); } WsbTraceOut(OLESTR("ClearDirectory(wsbdbsys)"), OLESTR("hr =<%ls>, # deleted = %d, # skipped = %d"), WsbHrAsString(hr), nDeleted, nSkipped); return(hr); } // CopyDirectory - copy files from one directory to another // Return S_OK if no errors occurred. static HRESULT CopyDirectory(const OLECHAR* DirSource, const OLECHAR* DirTarget) { DWORD err; WIN32_FIND_DATA FindData; HANDLE hFind = 0; HRESULT hr = S_OK; int nCopied = 0; int nSkipped = 0; CWsbStringPtr SearchPath; WsbTraceIn(OLESTR("CopyDirectory(wsbdbsys)"), OLESTR("OldPath = \"%ls\", NewPath = \"%ls\""), WsbQuickString(WsbAbbreviatePath(DirSource, 120)), WsbQuickString(WsbAbbreviatePath(DirTarget, 120))); try { SearchPath = DirSource; SearchPath.Append("\\*"); hFind = FindFirstFile(SearchPath, &FindData); if (INVALID_HANDLE_VALUE == hFind) { hFind = 0; err = GetLastError(); WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindFirstFile(%ls) failed, error = %ld\n"), static_cast(SearchPath), err); WsbThrow(HRESULT_FROM_WIN32(err)); } while (TRUE) { if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN)) { nSkipped++; } else { CWsbStringPtr NewPath; CWsbStringPtr OldPath; OldPath = DirSource; OldPath.Append("\\"); OldPath.Append(FindData.cFileName); NewPath = DirTarget; NewPath.Append("\\"); NewPath.Append(FindData.cFileName); if (!CopyFile(OldPath, NewPath, FALSE)) { err = GetLastError(); WsbTrace(OLESTR("ClearDirectory(wsbdbsys): CopyFile(%ls, %ls) failed, error = %ld\n"), static_cast(OldPath), static_cast(NewPath), err); WsbThrow(HRESULT_FROM_WIN32(err)); } nCopied++; } if (!FindNextFile(hFind, &FindData)) { err = GetLastError(); if (ERROR_NO_MORE_FILES == err) break; WsbTrace(OLESTR("ClearDirectory(wsbdbsys): FindNextFile failed, error = %ld\n"), err); WsbThrow(HRESULT_FROM_WIN32(err)); } } } WsbCatch(hr); if (0 != hFind) { FindClose(hFind); } WsbTraceOut(OLESTR("CopyDirectory(wsbdbsys)"), OLESTR("hr =<%ls>, copied = %ld, skipped = %ld"), WsbHrAsString(hr), nCopied, nSkipped); return(hr); } // DirectoryHasFullBackup - try to determine if the directory contains a full backup // Return // S_OK if it contains a full backup // S_FALSE if it doesn't // E_* on errors // // The technique use here is somewhat ad hoc since it expects the full backup // filename to end in IDB_DB_FILE_SUFFIX static HRESULT DirectoryHasFullBackup(const OLECHAR* DirPath) { HRESULT hr = S_OK; WsbTraceIn(OLESTR("DirectoryHasFullBackup(wsbdbsys)"), OLESTR("Path = <%ls>"), WsbAbbreviatePath(DirPath, 120)); try { ULONG Count; WsbAffirmHr(FileCount(DirPath, L"*." IDB_DB_FILE_SUFFIX, &Count)); if (0 == Count) { hr = S_FALSE; } } WsbCatch(hr); WsbTraceOut(OLESTR("DirectoryHasFullBackup(wsbdbsys)"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); } // FileCount - count all files in a directory matching a pattern. Skip // directories and hidden files. // Return S_OK if no errors occurred. static HRESULT FileCount(const OLECHAR* DirPath, const OLECHAR* Pattern, ULONG* Count) { DWORD err; WIN32_FIND_DATA FindData; HANDLE hFind = 0; HRESULT hr = S_OK; int lCount = 0; int nSkipped = 0; CWsbStringPtr SearchPath; WsbTraceIn(OLESTR("FileCount(wsbdbsys)"), OLESTR("Path = <%ls>"), WsbAbbreviatePath(DirPath, 120)); try { SearchPath = DirPath; SearchPath.Append("\\"); SearchPath.Append(Pattern); *Count = 0; hFind = FindFirstFile(SearchPath, &FindData); if (INVALID_HANDLE_VALUE == hFind) { hFind = 0; err = GetLastError(); if (ERROR_FILE_NOT_FOUND == err) WsbThrow(S_OK); WsbTrace(OLESTR("FileCount(wsbdbsys): FindFirstFile(%ls) failed, error = %ld\n"), static_cast(SearchPath), err); WsbThrow(HRESULT_FROM_WIN32(err)); } while (TRUE) { if (FindData.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN)) { nSkipped++; } else { lCount++; } if (!FindNextFile(hFind, &FindData)) { err = GetLastError(); if (ERROR_NO_MORE_FILES == err) break; WsbTrace(OLESTR("FileCount(wsbdbsys): FindNextFile failed, error = %ld\n"), err); WsbThrow(HRESULT_FROM_WIN32(err)); } } } WsbCatch(hr); if (0 != hFind) { FindClose(hFind); } if (S_OK == hr) { *Count = lCount; } WsbTraceOut(OLESTR("FileCount(wsbdbsys)"), OLESTR("hr =<%ls>, # skipped = %d, Count = %ld"), WsbHrAsString(hr), nSkipped, *Count); return(hr); } // RenameDirectory - rename a directory // Return S_OK if no errors occurred. static HRESULT RenameDirectory(const OLECHAR* OldDir, const OLECHAR* NewDir) { DWORD err; HRESULT hr = S_OK; WsbTraceIn(OLESTR("RenameDirectory(wsbdbsys)"), OLESTR("OldPath = \"%ls\", NewPath = \"%ls\""), WsbQuickString(WsbAbbreviatePath(OldDir, 120)), WsbQuickString(WsbAbbreviatePath(NewDir, 120))); try { if (!MoveFile(OldDir, NewDir)) { err = GetLastError(); WsbTrace(OLESTR("RenameDirectory(wsbdbsys): MoveFile failed, error = %ld\n"), err); WsbThrow(HRESULT_FROM_WIN32(err)); } } WsbCatch(hr); WsbTraceOut(OLESTR("RenameDirectory(wsbdbsys)"), OLESTR("hr =<%ls>"), WsbHrAsString(hr)); return(hr); }