|
|
//=======================================================================
//
// Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
//
// File: History.CPP
// Author: Charles Ma, 10/13/2000
//
// Revision History:
//
//
//
// Description:
//
// Class to handle history log
//
//=======================================================================
#include "iuengine.h"
#include <iucommon.h>
#include <fileutil.h>
#include <StringUtil.h>
#include <shlwapi.h> // for PathAppend() API
#include "history.h"
const TCHAR C_V3_LOG_FILE[] = _T("wuhistv3.log"); const TCHAR C_LOG_FILE[] = _T("iuhist.xml"); const TCHAR C_LOG_FILE_CORP[] = _T("iuhist_catalog.xml"); const TCHAR C_LOG_FILE_CORP_ADMIN[] = _T("iuhist_catalogAdmin.xml"); const OLECHAR C_IU_CORP_SITE[] = L"IU_CORP_SITE"; const OLECHAR C_HISTORICALSPEED[] = L"GetHistoricalSpeed";
//
// we use a global mutex name to let all clients, including services
// on terminal servers gain exclusive access for updating history on disk
//
#if defined(UNICODE) || defined(_UNICODE)
const TCHAR C_MUTEX_NAME[] = _T("Global\\6D7495AB-399E-4768-89CC-9444202E8412"); #else
const TCHAR C_MUTEX_NAME[] = _T("6D7495AB-399E-4768-89CC-9444202E8412"); #endif
#define CanSaveHistory (NULL != m_hMutex)
#define ReturnFailedAllocSetHrMsg(x) {if (NULL == (x)) {hr = E_OUTOFMEMORY; LOG_ErrorMsg(hr); return hr;}}
CIUHistory::CIUHistory() : m_pszDownloadBasePath(NULL), m_bstrCurrentClientName(NULL) { LOG_Block("CIUHisotry::CIUHistory()");
m_pxmlExisting = new CXmlItems(TRUE); m_pxmlDownload = new CXmlItems(FALSE); m_pxmlInstall = new CXmlItems(FALSE);
m_hMutex = CreateMutex( NULL, // no security descriptor
FALSE, // mutex object not owned, yet
C_MUTEX_NAME ); if (NULL == m_hMutex) { DWORD dwErr = GetLastError(); LOG_ErrorMsg(dwErr); m_ErrorCode = HRESULT_FROM_WIN32(GetLastError()); } else { LOG_Out(_T("Mutex created okay")); m_ErrorCode = S_OK; }
m_fSavePending = FALSE; }
CIUHistory::~CIUHistory() { if (m_fSavePending) { SaveHistoryToDisk(); }
if (CanSaveHistory) { CloseHandle(m_hMutex); }
if (NULL != m_pxmlExisting) { delete m_pxmlExisting; }
if (NULL != m_pxmlDownload) { delete m_pxmlDownload; }
if (NULL != m_pxmlInstall) { delete m_pxmlInstall; }
SafeHeapFree(m_pszDownloadBasePath); SysFreeString(m_bstrCurrentClientName); }
// ------------------------------------------------------------------
//
// public function SetDownloadBasePath()
// this function should be called before AddHistoryItemDownloadStatus()
// for corporate case to set the download path that the user has input,
// so that we know where to save the history log.
//
// ------------------------------------------------------------------
HRESULT CIUHistory::SetDownloadBasePath(LPCTSTR pszDownloadedBasePath) { LOG_Block("SetDownloadBasePath()");
if (NULL != pszDownloadedBasePath) { HRESULT hr = S_OK; if (NULL != m_pszDownloadBasePath) { //
// most likely user called SetDownloadBasePath() at least twice
// within the same instance of this class
//
SafeHeapFree(m_pszDownloadBasePath); }
m_pszDownloadBasePath = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH * sizeof (TCHAR)); if (NULL == m_pszDownloadBasePath) { LOG_ErrorMsg(E_OUTOFMEMORY); return E_OUTOFMEMORY; }
hr = StringCchCopyEx(m_pszDownloadBasePath, MAX_PATH, pszDownloadedBasePath, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { SafeHeapFree(m_pszDownloadBasePath); LOG_ErrorMsg(hr); return hr; } BSTR bstrCorpSite = SysAllocString(C_IU_CORP_SITE); SetClientName(bstrCorpSite); SafeSysFreeString(bstrCorpSite); } return S_OK; }
// ------------------------------------------------------------------
//
// public function AddHistoryItemDownloadStatus()
// this function should be called when you want to record the
// download status of this item. A new history item will be
// added to the history file
//
// ------------------------------------------------------------------
HRESULT CIUHistory::AddHistoryItemDownloadStatus( CXmlCatalog* pCatalog, HANDLE_NODE hCatalogItem, // a handle points to node in catalog
_HISTORY_STATUS enDownloadStatus, LPCTSTR lpcszDownloadedTo, LPCTSTR lpcszClient, DWORD dwErrorCode /*= 0*/ ) { LOG_Block("AddHistoryItemDownloadStatus()");
HRESULT hr = S_OK;
HANDLE_NODE hDownloadItem = HANDLE_NODE_INVALID;
ReturnFailedAllocSetHrMsg(m_pxmlDownload);
if (NULL == lpcszClient || _T('\0') == lpcszClient[0]) { hr = E_INVALIDARG; LOG_ErrorMsg(hr); return hr; }
if (!CanSaveHistory) { return m_ErrorCode; }
BSTR bstrDownloadedTo = NULL; BSTR bstrClient = T2BSTR(lpcszClient); BSTR bstrDownloadStatus = GetBSTRStatus(enDownloadStatus);
//
// append a new node
//
hr = m_pxmlDownload->AddItem(pCatalog, hCatalogItem, &hDownloadItem); if (SUCCEEDED(hr)) { m_pxmlDownload->AddTimeStamp(hDownloadItem); if (0 != dwErrorCode) { m_pxmlDownload->AddDownloadStatus(hDownloadItem, bstrDownloadStatus, dwErrorCode); } else { m_pxmlDownload->AddDownloadStatus(hDownloadItem, bstrDownloadStatus); }
bstrDownloadedTo = T2BSTR(lpcszDownloadedTo); m_pxmlDownload->AddDownloadPath(hDownloadItem, bstrDownloadedTo); m_pxmlDownload->AddClientInfo(hDownloadItem, bstrClient); m_pxmlDownload->CloseItem(hDownloadItem);
m_fSavePending = TRUE; }
SetClientName(bstrClient); SysFreeString(bstrDownloadedTo); SysFreeString(bstrClient); SysFreeString(bstrDownloadStatus); return hr; }
// ------------------------------------------------------------------
//
// public function AddHistoryItemInstallStatus()
// this function should be called when you want to record the
// install status of this item. This function will go to the
// existing history tree and find the first item that matches
// the identity of hCatalogItem, and assume that one as
// the one you want to modify the install status
//
//
// return:
// HRESULT - S_OK if succeeded
// - E_HANDLE if can't find hCatalogItem from
// the current history log tree
// - or other HRESULT error
//
// ------------------------------------------------------------------
HRESULT CIUHistory::AddHistoryItemInstallStatus( CXmlCatalog* pCatalog, HANDLE_NODE hCatalogItem, // a handle points to node in catalog
_HISTORY_STATUS enInstallStatus, LPCTSTR lpcszClient, BOOL fNeedsReboot, DWORD dwErrorCode /*= 0*/ ) { LOG_Block("AddHistoryItemInstallStatus()");
HRESULT hr = S_OK;
HANDLE_NODE hInstallItem = HANDLE_NODE_INVALID;
ReturnFailedAllocSetHrMsg(m_pxmlInstall);
if (!CanSaveHistory) { return m_ErrorCode; }
BSTR bstrClient = NULL; BSTR bstrInstallStatus = GetBSTRStatus(enInstallStatus); //
// append a new node
//
hr = m_pxmlInstall->AddItem(pCatalog, hCatalogItem, &hInstallItem); if (SUCCEEDED(hr)) { m_pxmlInstall->AddTimeStamp(hInstallItem); if (0 != dwErrorCode) { m_pxmlInstall->AddInstallStatus(hInstallItem, bstrInstallStatus, fNeedsReboot, dwErrorCode); } else { m_pxmlInstall->AddInstallStatus(hInstallItem, bstrInstallStatus, fNeedsReboot); } bstrClient = T2BSTR(lpcszClient); m_pxmlInstall->AddClientInfo(hInstallItem, bstrClient); m_pxmlInstall->CloseItem(hInstallItem); m_fSavePending = TRUE; }
SysFreeString(bstrClient); SysFreeString(bstrInstallStatus); return hr; }
// ------------------------------------------------------------------
//
// public function UpdateHistoryItemInstallStatus()
// this function should be called when you want to record the
// install status of this item. This function will go to the
// existing history tree and find the first item that matches
// the identity of hCatalogItem, and assume that one as
// the one you want to modify the install status
//
//
// return:
// HRESULT - S_OK if succeeded
// - E_HANDLE if can't find hCatalogItem from
// the current history log tree
// - or other HRESULT error
//
// ------------------------------------------------------------------
HRESULT CIUHistory::UpdateHistoryItemInstallStatus( CXmlCatalog* pCatalog, HANDLE_NODE hCatalogItem, // a handle points to node in catalog
_HISTORY_STATUS enInstallStatus, BOOL fNeedsReboot, DWORD dwErrorCode /*= 0*/ ) { LOG_Block("UpdateHistoryItemInstallStatus()");
HRESULT hr = S_OK;
HANDLE_NODE hInstallItem = HANDLE_NODE_INVALID;
ReturnFailedAllocSetHrMsg(m_pxmlInstall);
if (!CanSaveHistory) { return m_ErrorCode; }
BSTR bstrInstallStatus = GetBSTRStatus(enInstallStatus); //
// append a new node
//
hr = m_pxmlInstall->FindItem(pCatalog, hCatalogItem, &hInstallItem); if (SUCCEEDED(hr)) { m_pxmlInstall->AddTimeStamp(hInstallItem); if (0 != dwErrorCode) { m_pxmlInstall->UpdateItemInstallStatus(hInstallItem, bstrInstallStatus, fNeedsReboot, dwErrorCode); } else { m_pxmlInstall->UpdateItemInstallStatus(hInstallItem, bstrInstallStatus, fNeedsReboot); } m_pxmlInstall->CloseItem(hInstallItem); m_fSavePending = TRUE; }
SysFreeString(bstrInstallStatus); return hr; }
/*
// ------------------------------------------------------------------
//
// public function RetrieveItemDownloadPath()
// this function will go to the existing history tree and find
// the first item that matches the identity of hCatalogItem, and
// assume that's the one you want to retrieve the download path from
//
// return:
// HRESULT - S_OK if succeeded
// - E_HANDLE if can't find hCatalogItem from
// the current history log tree
// - or other HRESULT error
//
// ------------------------------------------------------------------
HRESULT CIUHistory::RetrieveItemDownloadPath( CXmlCatalog* pCatalog, HANDLE_NODE hCatalogItem, // a handle points to node in catalog
BSTR* pbstrDownloadPath ) { HRESULT hr = S_OK;
if (NULL == m_Existing.'DocumentPtr()) { //
// need to read the existing history
//
WaitForSingleObject(m_hMutex, INFINITE);
hr = ReadHistoryFromDisk(NULL); if (FAILED(hr)) { //
// if we can't load the existing history
// we can't do anything here
//
ReleaseMutex(m_hMutex); return hr; }
ReleaseMutex(m_hMutex); }
hr = m_Existing.GetItemDownloadPath(pCatalog, hCatalogItem, pbstrDownloadPath); return hr; } */ // ------------------------------------------------------------------
//
// public function ReadHistoryFromDisk()
// this function will read the history from the given file
//
// if the file path is NULL, assumes default IU log file locally
//
// ------------------------------------------------------------------
HRESULT CIUHistory::ReadHistoryFromDisk(LPCTSTR lpszLogFile, BOOL fCorpAdmin /*= FALSE*/) { LOG_Block("ReadHistoryFromDisk()");
HRESULT hr = S_OK; TCHAR szLogPath[MAX_PATH];
ReturnFailedAllocSetHrMsg(m_pxmlExisting);
//
// check to see if we use designated path (comsumer)
// or user-specified path (corporate)
//
if ((NULL == lpszLogFile || _T('\0') == lpszLogFile[0]) && !fCorpAdmin) { GetIndustryUpdateDirectory(szLogPath); hr = PathCchAppend(szLogPath, ARRAYSIZE(szLogPath), C_LOG_FILE); if (FAILED(hr)) { LOG_ErrorMsg(hr); return hr; } } else { //
// this is corporate case to read log file from
// a server location
//
if (fCorpAdmin) { GetIndustryUpdateDirectory(szLogPath); hr = PathCchAppend(szLogPath, ARRAYSIZE(szLogPath), C_LOG_FILE_CORP_ADMIN); if (FAILED(hr)) { LOG_ErrorMsg(hr); return hr; } } else { hr = StringCchCopyEx(szLogPath, ARRAYSIZE(szLogPath), lpszLogFile, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); return hr; } hr = PathCchAppend(szLogPath, ARRAYSIZE(szLogPath), C_LOG_FILE_CORP); if (FAILED(hr)) { LOG_ErrorMsg(hr); return hr; } } }
//
// if we are not passing in the class file path buffer,
// then update the class path buffer with this new path
//
if (szLogPath != m_szLogFilePath) { hr = StringCchCopyEx(m_szLogFilePath, ARRAYSIZE(m_szLogFilePath), szLogPath, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); return hr; } }
//
// load the xml file
//
m_pxmlExisting->Clear(); BSTR bstrLogPath = T2BSTR(szLogPath); hr = m_pxmlExisting->LoadXMLDocumentFile(bstrLogPath); SysFreeString(bstrLogPath); return hr; }
// ------------------------------------------------------------------
//
// public function SaveHistoryToDisk()
// this function will re-read the history in exclusive mode, and
// merge the newly added data to the tree (so we don't overwrite
// new changes made by other instances of this control) and
// write it back
//
// ------------------------------------------------------------------
HRESULT CIUHistory::SaveHistoryToDisk(void) { LOG_Block("SaveHistoryToDisk()");
HRESULT hr = S_OK, hr2 = S_OK; BSTR bstrLogFilePath = NULL;
ReturnFailedAllocSetHrMsg(m_pxmlExisting); ReturnFailedAllocSetHrMsg(m_pxmlDownload); ReturnFailedAllocSetHrMsg(m_pxmlInstall);
if (!CanSaveHistory) { return m_ErrorCode; }
if (!m_fSavePending) { //
// nothing to save
//
return S_OK; }
//
// first, we need to gain exclusive access
// to the log file before reading it
//
// since this is not a long process, so I
// don't think we need to take care of WM_QUIT
// message
//
WaitForSingleObject(m_hMutex, INFINITE);
BSTR bstrCorpSite = SysAllocString(C_IU_CORP_SITE); ReturnFailedAllocSetHrMsg(bstrCorpSite);
if (!CompareBSTRsEqual(bstrCorpSite, m_bstrCurrentClientName)) { SysFreeString(bstrCorpSite); //
// re-read history file
//
hr = ReadHistoryFromDisk(NULL);
//
// comment out...if we get failure on reading,
// we recreate a new history file later when saving.
//
//if (FAILED(hr))
//{
// //
// // if we can't load the existing history
// // we can't do anything here
// //
// ReleaseMutex(m_hMutex);
// return hr;
//}
//
// merge changes:
//
// loop through m_Download, insert each node to top of m_Existing
//
hr = m_pxmlExisting->MergeItemDownloaded(m_pxmlDownload); if (FAILED(hr)) { ReleaseMutex(m_hMutex); return hr; }
//
// loop through m_Install, for each node in m_Install
// find the one in m_Existing, update install status
//
hr = m_pxmlExisting->UpdateItemInstalled(m_pxmlInstall); if (FAILED(hr)) { ReleaseMutex(m_hMutex); return hr; }
//
// save the xml file
//
bstrLogFilePath = T2BSTR(m_szLogFilePath); hr = m_pxmlExisting->SaveXMLDocument(bstrLogFilePath); SafeSysFreeString(bstrLogFilePath); if (SUCCEEDED(hr)) { m_fSavePending = FALSE; } } else { //
// this is the corporate case...
//
SysFreeString(bstrCorpSite); if (NULL != m_pszDownloadBasePath && _T('\0') != m_pszDownloadBasePath[0]) { //
// re-read corp history from download base folder
//
ReadHistoryFromDisk(m_pszDownloadBasePath);
//
// merge new items downloaded
//
hr = m_pxmlExisting->MergeItemDownloaded(m_pxmlDownload); if (FAILED(hr)) { ReleaseMutex(m_hMutex); return hr; }
//
// save the xml file
//
bstrLogFilePath = T2BSTR(m_szLogFilePath); hr = m_pxmlExisting->SaveXMLDocument(bstrLogFilePath); SafeSysFreeString(bstrLogFilePath); } //
// re-read corp admin history from windowsupdate folder
//
ReadHistoryFromDisk(m_pszDownloadBasePath, TRUE);
//
// merge new items downloaded
//
hr2 = m_pxmlExisting->MergeItemDownloaded(m_pxmlDownload); if (FAILED(hr2)) { ReleaseMutex(m_hMutex); return hr2; }
//
// save the xml file
//
bstrLogFilePath = T2BSTR(m_szLogFilePath); hr2 = m_pxmlExisting->SaveXMLDocument(bstrLogFilePath); SafeSysFreeString(bstrLogFilePath); if (SUCCEEDED(hr) && SUCCEEDED(hr2)) { m_fSavePending = FALSE; } }
ReleaseMutex(m_hMutex); SysFreeString(bstrLogFilePath); hr = SUCCEEDED(hr) ? hr2 : hr; return hr; }
// ------------------------------------------------------------------
//
// public function to set the client name
//
// a client name is used to put in history to denode who
// caused download/install happened.
//
// ------------------------------------------------------------------
void CIUHistory::SetClientName(BSTR bstrClientName) { if (NULL != m_bstrCurrentClientName) { SysFreeString(m_bstrCurrentClientName); m_bstrCurrentClientName = NULL; } if (NULL != bstrClientName) { m_bstrCurrentClientName = SysAllocString(bstrClientName); } }
// ------------------------------------------------------------------
//
// public function GetHistory
//
// read the current history XML file and convert it
// into bstr to pass out
//
// ------------------------------------------------------------------
HRESULT CIUHistory::GetHistoryStr( LPCTSTR lpszLogFile, BSTR BeginDateTime, BSTR EndDateTime, BSTR* pbstrHistory) { LOG_Block("GetHistoryStr()");
HRESULT hr = S_OK;
ReturnFailedAllocSetHrMsg(m_pxmlExisting);
//
// need to read the existing history
//
WaitForSingleObject(m_hMutex, INFINITE);
BSTR bstrCorpSite = SysAllocString(C_IU_CORP_SITE); if (bstrCorpSite == NULL) { hr = E_OUTOFMEMORY; goto done; }
if (CompareBSTRsEqual(bstrCorpSite, m_bstrCurrentClientName)) { TCHAR szLogPath[MAX_PATH]; TCHAR szLogFileParam[MAX_PATH];
if (lpszLogFile != NULL && lpszLogFile[0] != _T('\0')) { hr = StringCchCopyEx(szLogFileParam, ARRAYSIZE(szLogFileParam), lpszLogFile, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto done; }
hr = PathCchAddBackslash(szLogFileParam, ARRAYSIZE(szLogFileParam)); if (FAILED(hr)) { LOG_ErrorMsg(hr); goto done; } } else { szLogFileParam[0] = _T('\0'); } //
// corporate case
//
GetIndustryUpdateDirectory(szLogPath); if (_T('\0') == szLogFileParam[0] || !lstrcmpi(szLogPath, szLogFileParam)) { // corp admin history
hr = ReadHistoryFromDisk(szLogPath, TRUE); } else { // corp history
hr = ReadHistoryFromDisk(lpszLogFile); } } else { HRESULT hrAppend; //
// consumer case
//
hr = ReadHistoryFromDisk(NULL);
//
// migrate V3 history to iuhist.xml
// - if succeeded, save the updated iuhist.xml file and delete wuhistv3.log
// - if failed, just log error and keep using the current iuhist.xml
//
TCHAR szLogPath[MAX_PATH]; GetWindowsUpdateV3Directory(szLogPath); hrAppend = PathCchAppend(szLogPath, ARRAYSIZE(szLogPath), C_V3_LOG_FILE); if (FAILED(hrAppend)) { LOG_ErrorMsg(hrAppend); if (SUCCEEDED(hr)) hr = hrAppend; goto done; }
if (0xffffffff != GetFileAttributes(szLogPath)) { // V3 history file "wuhistv3.log" exists, so start migration
if (FAILED(m_pxmlExisting->MigrateV3History(szLogPath))) { LOG_Out(_T("Failed to migrate v3 consumer history")); } else { BSTR bstrLogFilePath = T2BSTR(m_szLogFilePath); if (FAILED(m_pxmlExisting->SaveXMLDocument(bstrLogFilePath))) { LOG_Out(_T("Failed to save the updated history file %s"), m_szLogFilePath); } else { DeleteFile(szLogPath); } SafeSysFreeString(bstrLogFilePath); } } }
done: ReleaseMutex(m_hMutex); SafeSysFreeString(bstrCorpSite);
if (FAILED(hr)) { //
// if we can't load the existing history
// we can't do anything here. Return empty string.
//
*pbstrHistory = SysAllocString(L""); LOG_Out(_T("Loading the history xml file failed")); return S_FALSE; }
//
// traverse history tree, inspect each node
// to see if time/clientName fit. If not, delete it
// then output the string
//
hr = m_pxmlExisting->GetFilteredHistoryBSTR(BeginDateTime, EndDateTime, m_bstrCurrentClientName, pbstrHistory);
return hr; }
// *****************************************************************
//
// IUENGINE.DLL Public API:
//
// *****************************************************************
HRESULT WINAPI CEngUpdate::GetHistory( BSTR bstrDateTimeFrom, BSTR bstrDateTimeTo, BSTR bstrClient, BSTR bstrPath, BSTR* pbstrLog) { LOG_Block("GetHistory()");
USES_IU_CONVERSION;
HRESULT hr = S_OK; BSTR bsStart = NULL; BSTR bsEnd = NULL; CIUHistory cHistory;
//
// first, check to see if this is to ask historical speed
//
if (NULL != bstrClient && lstrcmpiW(C_HISTORICALSPEED, (LPWSTR)((LPOLESTR) bstrClient)) == 0) { HKEY hKey = NULL; TCHAR szSpeed[32]; DWORD dwSpeed = 0x0; DWORD dwSize = sizeof(dwSpeed); LONG lResult = ERROR_SUCCESS;
//
// get speed here from reg; if failure, dwSpeed remains to be 0.
//
if (ERROR_SUCCESS == (lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_IUCTL, 0, KEY_READ, &hKey))) { lResult = RegQueryValueEx(hKey, REGVAL_HISTORICALSPEED, NULL, NULL, (LPBYTE)&dwSpeed, &dwSize); RegCloseKey(hKey);
if (ERROR_SUCCESS != lResult) { *pbstrLog = SysAllocString(L"0"); LOG_Out(_T("GetHistoricalSpeed registry key not found, it must be no downloads happened yet")); return hr; } } else { *pbstrLog = SysAllocString(L"0"); LOG_ErrorMsg((DWORD)lResult); return hr; }
hr = StringCchPrintfEx(szSpeed, ARRAYSIZE(szSpeed), NULL, NULL, MISTSAFE_STRING_FLAGS, _T("%d"), dwSpeed); if (FAILED(hr)) { *pbstrLog = SysAllocString(L"0"); LOG_ErrorMsg(hr); return hr; } *pbstrLog = SysAllocString(T2OLE(szSpeed)); LOG_Out(_T("GetHistoricalSpeed get called! Return value %s"), szSpeed); return hr; }
//
// really asking history log
//
//
// set the client name
//
if (NULL != bstrClient && SysStringLen(bstrClient) > 0) { LOG_Out(_T("Set client name as %s"), OLE2T(bstrClient)); cHistory.SetClientName(bstrClient); } else { LOG_Out(_T("Set client name as NULL")); cHistory.SetClientName(NULL); }
//
// for script: they may pass empty string. we treat them
// as NULL
//
if (NULL != bstrDateTimeFrom && SysStringLen(bstrDateTimeFrom) > 0) { LOG_Out(_T("DateTimeFrom=%s"), OLE2T(bstrDateTimeFrom)); bsStart = bstrDateTimeFrom; } if (NULL != bstrDateTimeTo && SysStringLen(bstrDateTimeTo) > 0) { LOG_Out(_T("DateTimeTo=%s"), OLE2T(bstrDateTimeTo)); bsEnd = bstrDateTimeTo; }
//
// we do NOT validate the format of these two date/time strings.
// They are supposed to be in XML datetime format. If not, then
// the returned history logs may be filtered incorrectly.
//
hr = cHistory.GetHistoryStr(OLE2T(bstrPath), bsStart, bsEnd, pbstrLog);
SysFreeString(bsStart); SysFreeString(bsEnd); return hr; }
|