//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1998. // // File: wqiter.cxx // // Contents: WEB Query cache iterators // // History: 96/Jan/3 DwightKr Created // //---------------------------------------------------------------------------- #include #pragma hdrstop //+--------------------------------------------------------------------------- // // Member: CBaseQueryResultsIter::CBaseQueryResultsIter - public constructor // // Arguments: [item] - the query results we are iterating over // [hAccessor] - an accessor to the query results // [cCols] - number of output columns // [xVariableSet] - a list of replaceable parameters // // Synopsis: Initializes common vars between sequential & non-sequential // iterators // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- CBaseQueryResultsIter::CBaseQueryResultsIter( CWQueryItem & item, HACCESSOR hAccessor, ULONG cCols ) : _item(item), _hAccessor(hAccessor), _cColumns(cCols), _pVariableSet(0), _cRowsReturnedToCache(0), _iCurrentCachedRow(0), _lFirstRecordNumber(1), _lMaxRecordsPerPage(0), _lNextRecordNumber(1), _lRecordsToRetrieve(0), _lTotalMatchedRecords(LONG_MAX), _pOutputColumns(_aOutputColumns) { } //+--------------------------------------------------------------------------- // // Member: CBaseQueryResultsIter::~CBaseQueryResultsIter - public destructor // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- CBaseQueryResultsIter::~CBaseQueryResultsIter() { if ( _pOutputColumns != _aOutputColumns ) { delete _pOutputColumns; } } //+--------------------------------------------------------------------------- // // Member: CBaseQueryResultsIter::InitalizeLocalVariables - protected // // Arguments: [variableSet] -- local variables from browser // [outputFormat] -- Output formatter // // Synopsis: Sets up the common local variables for this query // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CBaseQueryResultsIter::InitializeLocalVariables( CVariableSet & variableSet, COutputFormat & outputFormat ) { // // Setup the following variables: // // CiMaxRecordsPerPage // CiBookmark // CiCurrentPageNumber // CiContainsFirstRecord // CiOutOfDate // CiQueryTime // CiQueryDate // CiQueryTimezone // CiColumns // CiTemplate // _pVariableSet = &variableSet; PROPVARIANT Variant; CIDQFile const & idqFile = _item.GetIDQFile(); // // Set CiMaxRecordsPerPage // _lMaxRecordsPerPage = ReplaceNumericParameter ( idqFile.GetMaxRecordsPerPage(), variableSet, outputFormat, 10, 1, 0x10000 ); _lRecordsToRetrieve = _lMaxRecordsPerPage; Variant.vt = VT_I4; Variant.lVal = _lMaxRecordsPerPage; _pVariableSet->SetVariable( ISAPI_CI_MAX_RECORDS_PER_PAGE, &Variant, 0 ); // // Calulate the # of the first record to display // ULONG cwcValue; CVariable * pVariable = variableSet.Find(ISAPI_CI_BOOKMARK); if ( (0 != pVariable) && (0 != pVariable->GetStringValueRAW(outputFormat, cwcValue)) && (0 != cwcValue) ) { CWQueryBookmark bookMark( pVariable->GetStringValueRAW(outputFormat, cwcValue) ); _lFirstRecordNumber = bookMark.GetRecordNumber(); } pVariable = variableSet.Find(ISAPI_CI_BOOKMARK_SKIP_COUNT); if ( (0 != pVariable) && (0 != pVariable->GetStringValueRAW(outputFormat, cwcValue)) ) { _lFirstRecordNumber += IDQ_wtol( pVariable->GetStringValueRAW(outputFormat, cwcValue) ); } // // Set CiContainsFirstRecord // Variant.vt = VT_BOOL; Variant.boolVal = VARIANT_FALSE; if ( 1 == _lFirstRecordNumber ) { Variant.boolVal = VARIANT_TRUE; } _pVariableSet->SetVariable( ISAPI_CI_CONTAINS_FIRST_RECORD, &Variant, 0 ); // // The first record must be at least 1. If the browser specified a // large negative skipcount, we need to move the first record number // forward to 1. // _lFirstRecordNumber = max( 1, _lFirstRecordNumber ); // // For sequential queries, _lTotalMatchedRecords is LONG_MAX, not the // total number of matched records. // if (_lFirstRecordNumber > _lTotalMatchedRecords) _lFirstRecordNumber = (LONG) _lTotalMatchedRecords+1; // // Set CiBookmark // CWQueryBookmark bookMark( _item.IsSequential(), &_item, _item.GetSequenceNumber(), _lFirstRecordNumber ); ULONG cwcBookmark = wcslen( bookMark.GetBookmark() ); XArray wcsCiBookMark( cwcBookmark + 1 ); RtlCopyMemory( wcsCiBookMark.Get(), bookMark.GetBookmark(), (cwcBookmark+1) * sizeof(WCHAR) ); Variant.vt = VT_LPWSTR; Variant.pwszVal = wcsCiBookMark.Get(); _pVariableSet->SetVariable( ISAPI_CI_BOOKMARK, &Variant, eParamOwnsVariantMemory ); wcsCiBookMark.Acquire(); _lNextRecordNumber = _lFirstRecordNumber; // // // Set CiCurrentPageNumber // Variant.vt = VT_I4; Variant.lVal = _lFirstRecordNumber / _lMaxRecordsPerPage; if ( (_lFirstRecordNumber % _lMaxRecordsPerPage) != 0 ) { Variant.lVal++; } _pVariableSet->SetVariable( ISAPI_CI_CURRENT_PAGE_NUMBER, &Variant, 0 ); SetCGIVariables( *_pVariableSet, outputFormat ); #if 1 //nuke this soon // // Set CiQueryTime // ULONG cwcQueryTime = 40; XArray wcsQueryTime(cwcQueryTime-1); cwcQueryTime = outputFormat.FormatTime( _item.GetQueryTime(), wcsQueryTime.GetPointer(), cwcQueryTime ); // // SetCiQueryDate // ULONG cwcQueryDate = 40; XArray wcsQueryDate(cwcQueryDate-1); cwcQueryDate = outputFormat.FormatDate( _item.GetQueryTime(), wcsQueryDate.GetPointer(), cwcQueryDate ); variableSet.AcquireStringValue( ISAPI_CI_QUERY_TIME, wcsQueryTime.GetPointer(), 0 ); wcsQueryTime.Acquire(); variableSet.AcquireStringValue( ISAPI_CI_QUERY_DATE, wcsQueryDate.GetPointer(), 0 ); wcsQueryDate.Acquire(); #endif variableSet.CopyStringValue( ISAPI_CI_QUERY_TIMEZONE, _item.GetQueryTimeZone(), 0 ); variableSet.CopyStringValue( ISAPI_CI_COLUMNS, _item.GetColumns(), 0 ); variableSet.CopyStringValue( ISAPI_CI_TEMPLATE, _item.GetTemplate(), 0 ); } //+--------------------------------------------------------------------------- // // Member: CQueryResultsIter::CQueryResultsIter - public constructor // // Arguments: [item] - the query results we are iterating over // [pIRowsetScroll] - a IRowsetScroll interface // [hAccessor] - an accessor to the query results // [cCols] - number of output columns // // Synopsis: Builds an iterator used to access a non-sequential set of // query results. // // Notes: ownership of the xVariableSet is transferred to this class // as well as the IRowsetScroll interface // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- CQueryResultsIter::CQueryResultsIter( CWQueryItem & item, IRowsetScroll * pIRowsetScroll, HACCESSOR hAccessor, ULONG cCols ) : CBaseQueryResultsIter( item, hAccessor, cCols ), _pIRowsetScroll(pIRowsetScroll) { Win4Assert( pIRowsetScroll != 0 ); Win4Assert( hAccessor != 0 ); } //+--------------------------------------------------------------------------- // // Member: CQueryResultsIter::~CQueryResultsIter - public destructor // // Synopsis: Releases storage & IRowsetScroll interface // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- CQueryResultsIter::~CQueryResultsIter() { ReleaseRowsetCache(); _pIRowsetScroll->Release(); } //+--------------------------------------------------------------------------- // // Member: CQueryResultsIter::Init - public // // Arguments: [variableSet] -- local variables from the user's browser // [outputFormat] -- format of $'s & dates // // Synopsis: Sets up the number & date/time formatting, and fills the // cache with the first set of rows. // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CQueryResultsIter::Init( CVariableSet & variableSet, COutputFormat & outputFormat ) { // // If the in-line buffer isn't big enough, allocate a new buffer // if ( _cColumns > NUMBER_INLINE_COLS ) { _pOutputColumns = new COutputColumn[_cColumns]; } InitializeLocalVariables( variableSet, outputFormat ); FillRowsetCache(); } //+--------------------------------------------------------------------------- // // Member: CQueryResultsIter::Next - public // // Synopsis: Sets up the next row to be acessed by the GetRowData() // method, and fills the cache if necessary. // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CQueryResultsIter::Next() { _iCurrentCachedRow++; if ( _iCurrentCachedRow >= _cRowsReturnedToCache ) { ReleaseRowsetCache(); FillRowsetCache(); } Win4Assert( _iCurrentCachedRow <= _cRowsReturnedToCache ); } //+--------------------------------------------------------------------------- // // Member: CQueryResultsIter::InitalizeLocalVariables - private // // Arguments: [variableSet] - local variables from browser // [outputFormat] - format of numbers & dates // // Synopsis: Sets up the local variables for this query // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CQueryResultsIter::InitializeLocalVariables( CVariableSet & variableSet, COutputFormat & outputFormat ) { // // Set CiMatchedRecordCount // DBCOUNTITEM ulPosition; HRESULT sc = _pIRowsetScroll->GetApproximatePosition(0,0,0, &ulPosition, (DBCOUNTITEM *) &_lTotalMatchedRecords ); if ( FAILED( sc ) ) { THROW( CException(sc) ); } PROPVARIANT Variant; Variant.vt = VT_I4; Variant.lVal = (LONG) _lTotalMatchedRecords; variableSet.SetVariable( ISAPI_CI_MATCHED_RECORD_COUNT, &Variant, 0 ); // // Setup the following variables: // // CiMatchRecordCount // CiContainsLastRecord // CiTotalNumberPages // CiFirstRecordNumber // CiLastRecordNumber // CBaseQueryResultsIter::InitializeLocalVariables( variableSet, outputFormat ); CIDQFile const & idqFile = _item.GetIDQFile(); // // Set CiContainsLastRecord // Variant.vt = VT_BOOL; Variant.boolVal = VARIANT_FALSE; if ( (_lFirstRecordNumber + _lMaxRecordsPerPage) > _lTotalMatchedRecords ) { Variant.boolVal = VARIANT_TRUE; } _pVariableSet->SetVariable( ISAPI_CI_CONTAINS_LAST_RECORD, &Variant, 0 ); // // Set CiTotalNumberPages // Variant.vt = VT_I4; Variant.lVal = (LONG) _lTotalMatchedRecords / _lMaxRecordsPerPage; if ( (_lTotalMatchedRecords % _lMaxRecordsPerPage) != 0 ) { Variant.lVal++; } _pVariableSet->SetVariable( ISAPI_CI_TOTAL_NUMBER_PAGES, &Variant, 0 ); // // Set CiFirstRecordNumber // Variant.vt = VT_I4; Variant.lVal = _lFirstRecordNumber; _pVariableSet->SetVariable( ISAPI_CI_FIRST_RECORD_NUMBER, &Variant, 0 ); // // Set CiLastRecordNumber // LONG lLastRecordNumber = _lFirstRecordNumber + _lMaxRecordsPerPage - 1; if ( lLastRecordNumber > _lTotalMatchedRecords ) { lLastRecordNumber = (LONG) _lTotalMatchedRecords; } Variant.vt = VT_I4; Variant.lVal = lLastRecordNumber; _pVariableSet->SetVariable( ISAPI_CI_LAST_RECORD_NUMBER, &Variant, 0 ); // // Set CiRecordsNextPage // Variant.vt = VT_I4; if ( (lLastRecordNumber + _lMaxRecordsPerPage) <= _lTotalMatchedRecords ) { Variant.lVal = _lMaxRecordsPerPage; } else { Variant.lVal = (LONG) _lTotalMatchedRecords - lLastRecordNumber; } _pVariableSet->SetVariable( ISAPI_CI_RECORDS_NEXT_PAGE , &Variant, 0 ); } //+--------------------------------------------------------------------------- // // Member: CQueryResultsIter::FillRowsetCache - public // // Synopsis: Fills the cache with the next set of hRows // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CQueryResultsIter::FillRowsetCache() { ULONG ulRecordsToRetrieve = min(ITER_CACHE_SIZE, _lRecordsToRetrieve ); if ( ulRecordsToRetrieve > 0 ) { BYTE bookMark = DBBMK_FIRST; HROW * phCachedRows = _ahCachedRows; HRESULT sc = _pIRowsetScroll->GetRowsAt( DBWATCHREGION_NULL, NULL, 1, &bookMark, _lNextRecordNumber - 1, ulRecordsToRetrieve, &_cRowsReturnedToCache, &phCachedRows ); if ( DB_E_BADSTARTPOSITION == sc ) { // Requested fetch outside available rows. Treat as end of rowset. Win4Assert(_lNextRecordNumber > 0); sc = DB_S_ENDOFROWSET; _cRowsReturnedToCache = 0; } if ( FAILED(sc) ) { THROW( CException(sc) ); } _lNextRecordNumber += (LONG) _cRowsReturnedToCache; if ( 0 == _cRowsReturnedToCache || DB_S_ENDOFROWSET == sc ) { Win4Assert( DB_S_ENDOFROWSET == sc ); _lRecordsToRetrieve = 0; } else { _lRecordsToRetrieve -= (LONG) _cRowsReturnedToCache; } } } //+--------------------------------------------------------------------------- // // Member: CQueryResultsIter::ReleaseRowsetCache - public // // Synopsis: Releases the hRows currently in the cache // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CQueryResultsIter::ReleaseRowsetCache() { if ( _cRowsReturnedToCache > 0 ) { _pIRowsetScroll->ReleaseRows( _cRowsReturnedToCache, _ahCachedRows, 0, 0, 0 ); } _cRowsReturnedToCache = 0; _iCurrentCachedRow = 0; } //+--------------------------------------------------------------------------- // // Member: CQueryResultsIter::GetRowData - public // // Synopsis: Returns the output columns for the current row. // // Results: [COutputColumn *] an array of output columns // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- COutputColumn * CQueryResultsIter::GetRowData() { Win4Assert( _iCurrentCachedRow < _cRowsReturnedToCache ); HRESULT hr = _pIRowsetScroll->GetData( _ahCachedRows[_iCurrentCachedRow], _hAccessor, _pOutputColumns ); if ( FAILED(hr) ) { THROW( CException(hr) ); } return _pOutputColumns; } //+--------------------------------------------------------------------------- // // Member: CSeqQueryResultsIter::CSeqQueryResultsIter - public constructor // // Arguments: [item] - the query results we are iterating over // [pIRowset] - an IRowset interface // [hAccessor] - an accessor to the query results // [cCols] - number of output columns // [xVariableSet] - a list of replaceable parameters // [ulNextRecordNumnber] - next available rec # in this query // // Synopsis: Builds an iterator used to access a non-sequential set of // query results. // // Notes: ownership of the pVariableSet is transferred to this class // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- CSeqQueryResultsIter::CSeqQueryResultsIter( CWQueryItem & item, IRowset * pIRowset, HACCESSOR hAccessor, ULONG cCols, ULONG ulNextRecordNumber ) : CBaseQueryResultsIter( item, hAccessor, cCols ), _pIRowset(pIRowset) { Win4Assert( pIRowset != 0 ); Win4Assert( hAccessor != 0 ); _lNextRecordNumber = ulNextRecordNumber; } //+--------------------------------------------------------------------------- // // Member: CSeqQueryResultsIter::~CSeqQueryResultsIter - public destructor // // Synopsis: Releases storage. // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- CSeqQueryResultsIter::~CSeqQueryResultsIter() { ReleaseRowsetCache(); } //+--------------------------------------------------------------------------- // // Member: CSeqQueryResultsIter::Init - public // // Arguments: [outputFormat] - format of numbers & dates // [variableSet] - local variables from the user's browser // // Synopsis: Sets up the number & date/time formatting, and fills the // cache with the first set of rows. // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CSeqQueryResultsIter::Init( CVariableSet & variableSet, COutputFormat & outputFormat ) { // // If the in-line buffer isn't big enough, allocate a new buffer // if ( _cColumns > NUMBER_INLINE_COLS ) { _pOutputColumns = new COutputColumn[_cColumns]; } InitializeLocalVariables( variableSet, outputFormat ); ULONG cRowsToSkip = _lFirstRecordNumber - _item.GetNextRecordNumber(); FillRowsetCache( cRowsToSkip ); } //+--------------------------------------------------------------------------- // // Member: CSeqQueryResultsIter::Next - public // // Synopsis: Sets up the next row to be acessed by the GetRowData() // method, and fills the cache if necessary. // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CSeqQueryResultsIter::Next() { _iCurrentCachedRow++; if ( _iCurrentCachedRow >= _cRowsReturnedToCache ) { ReleaseRowsetCache(); FillRowsetCache(0); } Win4Assert( _iCurrentCachedRow <= _cRowsReturnedToCache ); } //+--------------------------------------------------------------------------- // // Member: CSeqQueryResultsIter::FillRowsetCache - public // // Arguments: [cRowsToSkip] - number of rows to skip before filling cache // // Synopsis: Fills the cache with the next set of hRows // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CSeqQueryResultsIter::FillRowsetCache( ULONG cRowsToSkip ) { ULONG ulRecordsToRetrieve = min(ITER_CACHE_SIZE, _lRecordsToRetrieve); if ( ulRecordsToRetrieve > 0 ) { HROW * phCachedRows = _ahCachedRows; HRESULT sc = _pIRowset->GetNextRows( NULL, cRowsToSkip, ulRecordsToRetrieve, &_cRowsReturnedToCache, &phCachedRows ); if ( DB_E_BADSTARTPOSITION == sc ) sc = DB_S_ENDOFROWSET; if ( FAILED(sc) ) { THROW( CException(sc) ); } _lNextRecordNumber += (LONG) _cRowsReturnedToCache; _lRecordsToRetrieve -= (LONG) _cRowsReturnedToCache; // // Set the CiContainsLastRecord variable to TRUE if we have // exhausted the query results. // PROPVARIANT Variant; Variant.vt = VT_BOOL; Variant.boolVal = VARIANT_FALSE; if ( 0 == _cRowsReturnedToCache || DB_S_ENDOFROWSET == sc || DB_S_STOPLIMITREACHED == sc ) { Win4Assert( DB_S_ENDOFROWSET == sc || DB_S_STOPLIMITREACHED == sc ); _lRecordsToRetrieve = 0; Variant.boolVal = VARIANT_TRUE; } _pVariableSet->SetVariable( ISAPI_CI_CONTAINS_LAST_RECORD, &Variant, 0 ); if ( DB_S_STOPLIMITREACHED == sc ) { Variant.boolVal = VARIANT_TRUE; _pVariableSet->SetVariable( ISAPI_CI_QUERY_TIMEDOUT, &Variant, 0 ); } // // Set CiLastRecordNumber // Variant.vt = VT_I4; Variant.lVal = _lNextRecordNumber - 1; _pVariableSet->SetVariable( ISAPI_CI_LAST_RECORD_NUMBER, &Variant, 0 ); _item.SetNextRecordNumber( _lNextRecordNumber ); } } //+--------------------------------------------------------------------------- // // Member: CSeqQueryResultsIter::ReleaseRowsetCache - public // // Synopsis: Releases the hRows currently in the cache // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CSeqQueryResultsIter::ReleaseRowsetCache() { if ( _cRowsReturnedToCache > 0 ) { _pIRowset->ReleaseRows( _cRowsReturnedToCache, _ahCachedRows, 0, 0, 0 ); } _cRowsReturnedToCache = 0; _iCurrentCachedRow = 0; } //+--------------------------------------------------------------------------- // // Member: CSeqQueryResultsIter::GetRowData - public // // Synopsis: Returns the output columns for the current row. // // Results: [COutputColumn *] an array of output columns // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- COutputColumn * CSeqQueryResultsIter::GetRowData() { Win4Assert( _iCurrentCachedRow < _cRowsReturnedToCache ); HRESULT hr = _pIRowset->GetData( _ahCachedRows[_iCurrentCachedRow], _hAccessor, _pOutputColumns ); if ( FAILED(hr) ) { THROW( CException(hr) ); } return _pOutputColumns; } //+--------------------------------------------------------------------------- // // Member: CSeqQueryResultsIter::InitalizeLocalVariables - private // // Arguments: [variableSet] - local variables from browser // [outputFormat] - format of numbers & dates // // Synopsis: Sets up the local variables for this query // // History: 96-Jan-18 DwightKr Created // //---------------------------------------------------------------------------- void CSeqQueryResultsIter::InitializeLocalVariables( CVariableSet & variableSet, COutputFormat & outputFormat ) { // // Setup the following variables: // // CiContainsLastRecord // CiFirstRecordNumber // CBaseQueryResultsIter::InitializeLocalVariables( variableSet, outputFormat ); // // Set CiFirstRecordNumber // PROPVARIANT Variant; Variant.vt = VT_I4; Variant.lVal = _lFirstRecordNumber; _pVariableSet->SetVariable( ISAPI_CI_FIRST_RECORD_NUMBER, &Variant, 0 ); } //+--------------------------------------------------------------------------- // // Member: SetCGIVariables - public // // History: 96-Mar-04 DwightKr Created // //---------------------------------------------------------------------------- void SetCGIVariables( CVariableSet & variableSet, CWebServer & webServer ) { { XArray xValue; ULONG cwcValue; if ( webServer.GetCGI_REQUEST_METHOD( xValue, cwcValue ) ) variableSet.SetVariable( ISAPI_REQUEST_METHOD, xValue ); if ( webServer.GetCGI_PATH_INFO( xValue, cwcValue ) ) variableSet.SetVariable( ISAPI_PATH_INFO, xValue ); if ( webServer.GetCGI_PATH_TRANSLATED( xValue, cwcValue ) ) variableSet.SetVariable( ISAPI_PATH_TRANSLATED, xValue ); if ( webServer.GetCGI_CONTENT_TYPE( xValue, cwcValue ) ) variableSet.SetVariable( ISAPI_CONTENT_TYPE, xValue ); } // // The HTTP variables can be obtained from ALL_HTTP. attribute/value // pairs are delimited by \n and the attribute is separated from // the value by a colon. For example: // // HTTP_DWIGHT:This is a HTTP_DWIGHT\nHTTP_KRUGER:This is another\n... // XArray wcsALLHTTP; ULONG cwcALLHTTP; if ( webServer.GetCGIVariable( "ALL_HTTP", wcsALLHTTP, cwcALLHTTP ) ) { WCHAR * wcsToken = wcsALLHTTP.Get(); XArray wcsCopyOfValue; while ( 0 != wcsToken ) { WCHAR * wcsAttribute = wcsToken; wcsToken = wcschr( wcsToken, L'\n' ); WCHAR * wcsValue = wcschr( wcsAttribute, L':' ); if ( 0 != wcsValue ) { *wcsValue++ = '\0'; ULONG cwcValue; if (wcsToken) { if ( wcsValue > wcsToken ) THROW( CException( E_INVALIDARG ) ); cwcValue = (ULONG)(wcsToken - wcsValue); *wcsToken++ = '\0'; } else { cwcValue = wcslen( wcsValue ); } wcsCopyOfValue.Init(cwcValue+1); RtlCopyMemory( wcsCopyOfValue.Get(), wcsValue, (cwcValue+1) * sizeof(WCHAR) ); PROPVARIANT Variant; Variant.vt = VT_LPWSTR; Variant.pwszVal = wcsCopyOfValue.Get(); variableSet.SetVariable( wcsAttribute, &Variant, eParamOwnsVariantMemory ); wcsCopyOfValue.Acquire(); } } } }