/*++

Copyright (C) 1995-2001 Microsoft Corporation

Module Name:

    GENERAL.CPP

Abstract:

	Containes some general purpose classes 
	which are of use to serveral providers.
	Specifically, this contains the code for
	the classes used to cache open handles and
	the classes used to parse the mapping strings.

History:

	a-davj  11-Nov-95   Created.

--*/

#include "precomp.h"
#include <tchar.h>
//***************************************************************************
//
//  CEntry::CEntry()
//
//  DESCRIPTION:
//
//  Constructor.
//
//***************************************************************************

CEntry::CEntry()
{
    hHandle = NULL;
    sPath.Empty();
}

//***************************************************************************
//
//  CEntry::~CEntry()
//
//  DESCRIPTION:
//
//  Destructor.
//
//***************************************************************************

CEntry::~CEntry()
{
    sPath.Empty();
}
    
//***************************************************************************
//
//  CHandleCache::~CHandleCache
//
//  DESCRIPTION:
//
//  Destructor.
//
//***************************************************************************

CHandleCache::~CHandleCache()
{
    Delete(0);    
}

//***************************************************************************
//
//  CHandleCache::lAddToList
//
//  DESCRIPTION:
//
//  Adds an entry to the handle list.
//
//
//  PARAMETERS:
//
//  pAdd    Name that will be used to retrieve the handle
//  hAdd    handle to be added
//  
//  RETURN VALUE:
//  
//  S_OK                    all is well.
//  WBEM_E_OUT_OF_MEMORY     out of memory
//
//***************************************************************************

long int CHandleCache::lAddToList(
                        IN const TCHAR * pAdd, 
                        IN HANDLE hAdd)
{
    CEntry * pNew = new CEntry();
    if(pNew == NULL) 
        return WBEM_E_OUT_OF_MEMORY; 
    pNew->hHandle = hAdd;
    pNew->sPath = pAdd;
    
    if(CFlexArray::no_error != List.Add(pNew))
        return WBEM_E_OUT_OF_MEMORY;
    return S_OK;
}

//***************************************************************************
//
//  CHandleCache::lGetNumMatch
//
//  DESCRIPTION:
//
//  Returns the number of entries which match the path tokens.  For example,
//  the path might be    HKEY_LOCAL_MACHINE, hardware, description, xyz, and 
//  if the tokens, were, HKEY_LOCAL_MACHINE, hardware, devicemap, xyz then a 
//  two would be returned since the first two parts matched.
//
//  PARAMETERS:
//
//  iEntry      Entry to start checking at
//  iToken      Token to start checking
//  Path        This object supplies the tokens to be checked.
//
//  RETURN VALUE:
//
//  see description.
//
//***************************************************************************

long int CHandleCache::lGetNumMatch(
                        IN int iEntry,
                        IN int iToken, 
                        IN CProvObj & Path)
{
    int iMatch = 0;
    for(;iEntry < List.Size() && iToken < Path.iGetNumTokens();
            iEntry++, iToken++, iMatch++) 
    {
        CEntry * pCurr = (CEntry *)List.GetAt(iEntry);
        TString sTemp = Path.sGetFullToken(iToken);
        if(lstrcmpi(pCurr->sPath,sTemp))
            break;
    }
    return iMatch;            
}

//***************************************************************************
//
//  CHandleCache::Delete
//
//  DESCRIPTION:
// 
//  Empties all or part of the cache.
//
//  PARAMETERS:
//
//  lStart      Indicates first element to delete.  To empty entire cache,
//              a zero should be entered.
//
//***************************************************************************

void CHandleCache::Delete(
                    IN long int lStart)
{
    int iCurr;
    for(iCurr = List.Size()-1; iCurr >= lStart; iCurr--) 
    {
        CEntry * pCurr = (CEntry *)List.GetAt(iCurr);
        delete pCurr;
        List.RemoveAt(iCurr);        
    }
}


//***************************************************************************
//
//  CHandleCache::hGetHandle
//
//  DESCRIPTION:
//
//  Gets a handle.
//
//  PARAMETERS:
//
//  lIndex      Indicates which handle to get.  0 is the first.
//
//  RETURN VALUE:
//  handle retuested, NULL only if bad index is entered.
//
//***************************************************************************

HANDLE CHandleCache::hGetHandle(
                        IN long int lIndex)
{
    if(lIndex < List.Size()) 
    {
        CEntry * pCurr = (CEntry *)List.GetAt(lIndex);
        if(pCurr)
            return pCurr->hHandle;
    }
    return NULL;
}

//***************************************************************************
//
//  CHandleCache::sGetString
//
//  DESCRIPTION:
//
//  Gets the string associated with a cache entry.
//
//  PARAMETERS:
//
//  lIndex          Index in cache, 0 is the first element.
//
//  RETURN VALUE:
//
//  Returns a pointer to the string.  NULL if bad index.
//
//***************************************************************************

const TCHAR * CHandleCache::sGetString(
                                IN long int lIndex)
{
    if(lIndex < List.Size()) {
        CEntry * pCurr = (CEntry *)List.GetAt(lIndex);
        if(pCurr)
            return pCurr->sPath;
        }
    return NULL;
}

//***************************************************************************
//
//  CToken::CToken
//
//  DESCRIPTION:
//
//  constructor.
// 
//  PARAMETERS:
//
//  cpStart         String to be parsed.
//  cDelim          Token delimeter.
//  bUsesEscapes    If true, then need to look for special characters
//
//***************************************************************************

#define MAX_TEMP 150

CToken::CToken(
                IN const TCHAR * cpStart,
                IN const OLECHAR cDelim, 
                bool bUsesEscapes)
{
    const TCHAR * cpCurr;        //atoi
    iOriginalLength = -1;
    TString *psExp;
    int iNumQuote;
    BOOL bLastWasEsc, bInExp, bInString;
    bLastWasEsc = bInExp = bInString = FALSE;

	
	// Before doing an elaborate parse, first check for the simple case where there
	// are no quotes, escapes, commas, etc

    bool bGotSpecialChar = false;

	for(cpCurr = cpStart; *cpCurr && *cpCurr != cDelim; cpCurr++)
	{
		if(*cpCurr == ESC || *cpCurr == '\"' || *cpCurr == '(')
			bGotSpecialChar = true;
	}


	if(!bUsesEscapes || cDelim != MAIN_DELIM || *cpCurr == cDelim || !bGotSpecialChar)
	{
		// Simple case do it quickly

		iOriginalLength = cpCurr - cpStart;
		if(iOriginalLength < MAX_TEMP)
		{
			TCHAR tcTemp[MAX_TEMP];
			lstrcpyn(tcTemp, cpStart, iOriginalLength + 1);
			sFullData = tcTemp;
			sData = tcTemp;
			if(*cpCurr)
				iOriginalLength++;
			return;
		}
	}


    for(cpCurr = cpStart; *cpCurr; cpCurr++) {
        
        // check if end of token

        if(*cpCurr == cDelim && !bLastWasEsc) {
            cpCurr++;
            break;                    
            }

        // full data stores everything.  Check if character
        // is the escape which means that the following
        // character should be interpreted as a literal

        sFullData += *cpCurr;
        if(*cpCurr == ESC && !bLastWasEsc) {
            bLastWasEsc = TRUE;
            continue;
            }
        
        // tokens can include indexs of the form 
        // (xxx) or ("xxx").  If an index is detected,
        // then store the characters between () separately

        if((*cpCurr == '(' && !bInExp && !bLastWasEsc)||
           (*cpCurr == ',' && bInExp && !bLastWasEsc)) {
            
            // start of index expression. Allocate a new
            // string to store it and store the string 
            // in the expression collection. 
    
            psExp = new (TString);
            if(psExp) {    
                Expressions.Add((CObject *)psExp);
                bInExp = TRUE;
                iNumQuote = 0;
                }
            }
        else if(*cpCurr == ')' && bInExp && !bInString && !bLastWasEsc)
            bInExp = FALSE;    // end of index expression
        else if (*cpCurr == '\"' && bInExp && !bLastWasEsc) {
            iNumQuote++;
            if(iNumQuote == 1) {
                bInString = TRUE;
                *psExp += *cpCurr;
                }
            else if(iNumQuote == 2)
                bInString = FALSE;
            else 
                return; 
            }
        else if (bInExp)
            *psExp += *cpCurr;
        else
            sData += *cpCurr;
        bLastWasEsc = FALSE;
        }
    if(bInString || bInExp)   
        return; // got junk!!!
    iOriginalLength = cpCurr - cpStart;
    return;
}

//***************************************************************************
//
//  CToken::~CToken
//
//  DESCRIPTION:
//
//  Destructor.
//
//***************************************************************************

CToken::~CToken()
{
    int iCnt;
    TString * pCurr;
    for (iCnt = 0; iCnt < Expressions.Size(); iCnt++) {
        pCurr = (TString *)Expressions.GetAt(iCnt);
        delete pCurr;
        }
    Expressions.Empty();
    sData.Empty();
    sFullData.Empty();
}

//***************************************************************************
//
//  CToken::iConvOprand
//
//  DESCRIPTION:
//
//  Converts the characters in a string into an integer.
//
//  PARAMETERS:
//
//  tpCurr      String to be converted.
//  iArray      Not used anymore.
//  dwValue     where the result is set.
//
//  RETURN VALUE:
//
//  Number of digits converted.
//
//***************************************************************************

long int CToken::iConvOprand(
                    IN const TCHAR * tpCurr, 
                    IN int iArray, 
                    OUT long int & dwValue)
{
    TString sTemp;
    long int iRet = 0;

    // Build up a string containing
    // all characters upto the first non didgit

    for(;*tpCurr; tpCurr++)
        if(iswdigit(*tpCurr)) 
        {
            sTemp += *tpCurr;
            iRet++;
        }
        else
            break;        
    
    // Convert and return the length

    dwValue = _ttoi(sTemp);    
    return iRet;
}

//***************************************************************************
//
//  CToken::GetIntExp
//
//  DESCRIPTION:
//
//  Converts the expression into an integer.  The expression
//  can only be made up of integers, '+', '-' and a special
//  character ('#' as of now)for substituting iArray.  Examples,
//  (23)  (#+3)  (#-4)
//
//  PARAMETERS:
//  
//  iExp        which integer to retrieve in the case where you have 2,5,8
//  iArray      Not used anymore
//
//  RETURN VALUE:
//  
//  -1 if error.
//
//***************************************************************************

long int CToken::GetIntExp(int iExp,int iArray)
{
    TString * psTest;
    TString sNoBlanks;
    TCHAR const * tpCurr;
    long int lOpSize;
    int iCnt;
    long int lAccum = 0, lOperand;
    TCHAR tUnary = ' ';
    TCHAR tBinary = '+';
    BOOL bNeedOperator = FALSE; // Start off needing operand
    
    // Do some intial check such as making sure the expression
    // exists and that it isnt a string expression

    if(Expressions.Size() <= iExp)
        return -1;
    if(IsExpString(iExp)) 
        return -1;
    psTest = (TString *)Expressions.GetAt(iExp);
    if(psTest == NULL) {
        return -1;
        }

    // Get rid of any blanks

    for(iCnt = 0; iCnt < psTest->Length(); iCnt++)
        if(psTest->GetAt(iCnt) != ' ')
            sNoBlanks += psTest->GetAt(iCnt);
    
    // Evalate the expression

    for(tpCurr = sNoBlanks; *tpCurr; tpCurr++) 
    {
        if(*tpCurr == '+' || *tpCurr == '-') 
        {
            
            // got an operator.  Note that if an operator is not needed,
            // such as before the first operand, then it must be a unary 
            // operator.  Only one unary operator in a row is valid.

            if(bNeedOperator) 
            {
                tBinary = *tpCurr;
                bNeedOperator = FALSE;
            }
            else
            {
                if(tUnary != ' ') // Gratuitous unary operator
                    return -1;
                tUnary = *tpCurr;
            }
        }
        else 
        {
            // got an operand
            
            if(bNeedOperator) // Gratuitous unary operand
                return -1;
            lOpSize = iConvOprand(tpCurr,iArray,lOperand);
            if(lOpSize > 1)
                tpCurr += lOpSize-1;
            if(tUnary == '-')
                lOperand =  -lOperand;
            if(tBinary == '+')
                lAccum = lAccum + lOperand;
            else
                lAccum = lAccum - lOperand;
            bNeedOperator = TRUE;
            tUnary = ' ';
        }
    }
    return lAccum;
}

//***************************************************************************
//
//  CToken::GetStringExp
//
//  DESCRIPTION:
//
//  Returns a string expression.
//
//  PARAMETERS:
//
//  iExp        Which string to use when they are separated by commans
//
//  RETURN VALUE:
//
//  Pointer to string, NULL if iExp is bogus.
//
//***************************************************************************

TCHAR const * CToken::GetStringExp(
                        IN int iExp)
{
    TString * psTest;
    TCHAR const * tp;
    
    // start by making sure expression exists.
            
    if(Expressions.Size() <= iExp)
        return NULL;
    psTest = (TString *)Expressions.GetAt(iExp);
    if(psTest != NULL) 
    {
        int iIndex;
        iIndex = psTest->Find('\"');
        if(iIndex != -1) 
        {

            // All is well.  Return a pointer one passed
            // the initial \" whose only purpose is to 
            // indicate that this is a string expression.

            tp = *psTest;
            return tp + iIndex + 1;
            }
    }
    return NULL;  
}

//***************************************************************************
//
//  CToken::IsExpString
//
//  DESCRIPTION:
//
//  Tests if the token contains a string
//
//  PARAMETERS:
//
//  iExp        Indicates which substring for when the strings are divided
//              by commas
//
//  RETURN VALUE:
//  Returns true if the expression is a string.
//
//***************************************************************************

BOOL CToken::IsExpString(int iExp)
{
    TString * psTest;
    
    // make sure that the expression exists.
            
    if(Expressions.Size() <= iExp)
        return FALSE;
    psTest = (TString *)Expressions.GetAt(iExp);
    if(psTest != NULL) {
        int iIndex;
        
        // String expressions always contain at least one \"
        
        iIndex = psTest->Find('\"');
        if(iIndex != -1)
            return TRUE;
        }
    return FALSE;
}
            


//***************************************************************************
//
//  CProvObj::CProvObj(const char * ProviderString,const TCHAR cDelim)
//
//  DESCRIPTION:
//
//  Constructor.
//
//  PARAMETERS:
//
//  ProviderString      String passed from wbem
//  cDelim              Token delimeter
//  bUsesEscapes        True if we need to treat escapes in a special way.
//
//***************************************************************************
#ifndef UNICODE
CProvObj::CProvObj(
                IN const char * ProviderString,
                IN const TCHAR cDelim, bool bUsesEscapes)
{
    m_bUsesEscapes = bUsesEscapes;
    Init(ProviderString,cDelim);
    return;
}
#endif

//***************************************************************************
//
//  CProvObj::CProvObj(const WCHAR * ProviderString,const TCHAR cDelim)
//
//  DESCRIPTION:
//
//  Constructor.
//
//  PARAMETERS:
//
//  ProviderString      String passed from wbem
//  cDelim              Token delimeter
//
//***************************************************************************


CProvObj::CProvObj(
                IN const WCHAR * ProviderString,
                IN const TCHAR cDelim, bool bUsesEscapes)
{
    m_bUsesEscapes = bUsesEscapes;
#ifdef UNICODE
    Init(ProviderString,cDelim);
#else
    char * pTemp = WideToNarrowA(ProviderString);
    if(pTemp == NULL)
        dwStatus = WBEM_E_FAILED;
    else {
        Init(pTemp,cDelim);
        delete pTemp;
        }
#endif
    return;
}

//***************************************************************************
//
//  void CProvObj::Init
//
//  DESCRIPTION:
//
//  Doest the acutal work for the various constructors.
//
//  PARAMETERS:
//  ProviderString      String passed from wbem
//  cDelim              Token delimeter
//
//***************************************************************************

void CProvObj::Init(
                IN const TCHAR * ProviderString,
                IN const TCHAR cDelim)
{
    CToken * pNewToken;
    const TCHAR * cpCurr;

	m_cDelim = cDelim;

    // Create a list of tokens

        for(cpCurr = ProviderString; *cpCurr;cpCurr+=pNewToken->GetOrigLength()) 
        {
			int iRet = 0;
            pNewToken = new CToken(cpCurr,cDelim, m_bUsesEscapes);
			if(pNewToken)
				iRet = myTokens.Add(pNewToken);
			if(pNewToken == NULL || iRet != CFlexArray::no_error)
			{
				dwStatus = WBEM_E_OUT_OF_MEMORY;
				return;
			}
            if(pNewToken->GetOrigLength() == -1)
            {
				dwStatus = WBEM_E_INVALID_PARAMETER;
				return;
            }
        }
        dwStatus = WBEM_NO_ERROR;
        return;

}            

//***************************************************************************
//
//  CProvObj::Empty
//
//  DESCRIPTION:
// 
//  frees up all the data
//
//***************************************************************************

void CProvObj::Empty(void)
{
    int iCnt;
    CToken * pCurr;
    for (iCnt = 0; iCnt < myTokens.Size(); iCnt++) {
        pCurr = (CToken *)myTokens.GetAt(iCnt);
        delete pCurr;
        }
    myTokens.Empty();
}

//***************************************************************************
//
//  BOOL CProvObj::Update
//
//  DESCRIPTION:
//
//  Resets the value with a new provider string. 
//
//  PARAMETERS:
//
//  pwcProvider     New provider string
//
//  RETURN VALUE:
//
//  TRUE if ok.
//***************************************************************************

BOOL CProvObj::Update(
                        IN WCHAR * pwcProvider)
{
	// Do a quick check to see if the "fast" update can be used

	BOOL bComplex = FALSE;
	int iDelim = 0;
	WCHAR * pwcCurr;
	for(pwcCurr = pwcProvider; *pwcCurr; pwcCurr++)
	{
		if(*pwcCurr == m_cDelim) 
			iDelim++;
		else if(*pwcCurr == ESC || *pwcCurr == L'\"' || *pwcCurr == L'(')
		{
			bComplex = TRUE;
			break;
		}
	}

	// If the number of tokens changed, or there is some embedded junk
	// just empty and retry.

	if(bComplex || iDelim != myTokens.Size()-1)
	{
		Empty();
#ifdef UNICODE
		Init(pwcProvider,m_cDelim);
#else
		char * pTemp = WideToNarrowA(pwcProvider);
		if(pTemp == NULL)
			return FALSE;
		Init(pTemp,m_cDelim);
        delete pTemp;
#endif
		return TRUE;
	}

	// We can take the shortcut.  Start by creating a TCHAR temp version

	int iLen = 2*wcslen(pwcProvider) + 1;
	TCHAR * pTemp = new TCHAR[iLen];
	if(pTemp == NULL)
		return FALSE;
#ifdef UNICODE
	wcscpy(pTemp,pwcProvider);
#else
	wcstombs(pTemp, pwcProvider, iLen);
#endif	

	TCHAR * ptcCurr;
	TCHAR * pStart;
	BOOL bTheEnd = FALSE;
    
    iDelim = 0;
	for(pStart = ptcCurr = pTemp;!bTheEnd;ptcCurr++)
	{
		if(*ptcCurr == m_cDelim || *ptcCurr == NULL)
		{
			bTheEnd = (*ptcCurr == NULL);
			*ptcCurr = NULL;
			CToken * pToken = (CToken *)myTokens.GetAt(iDelim);
			if(pToken && lstrcmpi(pStart,pToken->sFullData))
			{
				pToken->sFullData = pStart;
				pToken->sData = pStart;
			}
            iDelim++;
			pStart = ptcCurr+1;
		}
	}

	delete pTemp;
	return TRUE;
}

//***************************************************************************
//
//  CProvObj::GetTokenPointer
//
//  DESCRIPTION:
//
//  Gets a pointer to a token
//
//  PARAMETERS:
//
//  iToken              Which token to get
//
//  RETURN VALUE:
//  
//  pointer to token, or NULL if bad request.
//
//***************************************************************************

CToken * CProvObj::GetTokenPointer(
                        IN int iToken)
{
    if(iToken >= myTokens.Size() || iToken < 0) 
        return NULL;
    return (CToken *)myTokens.GetAt(iToken);
}

//***************************************************************************
//
//  CProvObj::dwGetStatus
//
//  DESCRIPTION:
//
//  Gets status and also checks to make sure a minimum number of tokens
//  exist.
//
//  PARAMETERS:
//
//  iMin                minimum number of tokens.
//
//  RETURN VALUE:
//
//  Returns S_OK if OK, WBEM_E_FAILED otherwise.
//
//***************************************************************************

DWORD CProvObj::dwGetStatus(
                        IN int iMin)
{
    if(dwStatus)
        return dwStatus;
    else
        return (iMin <= myTokens.Size()) ? S_OK : WBEM_E_FAILED;

}
//***************************************************************************
//
//  CProvObj::sGetToken
//
//  DESCRIPTION:
//
//  Gets a token.  Note that the token will not include embleded "(stuff)"
//
//  PARAMETERS:
//
//  iToken              which token to get
//
//  RETURN VALUE:
//
//  pointer to token, NULL if invalid argument
//***************************************************************************

const TCHAR * CProvObj::sGetToken(
                        IN int iToken)
{
    CToken * pCurr = GetTokenPointer(iToken);
    return (pCurr) ? pCurr->GetStringValue() : NULL;
}

//***************************************************************************
//
//  const TCHAR * CProvObj::sGetFullToken
//
//  DESCRIPTION:
//
//  Gets a full and unadulterated token.
//
//  PARAMETERS:
//
//  iToken              token to get
//
//  RETURN VALUE:
//
//  pointer to token, NULL if invalid argument
//***************************************************************************

const TCHAR * CProvObj::sGetFullToken(
                        IN int iToken)
{
    CToken * pCurr = GetTokenPointer(iToken);
    return (pCurr) ? pCurr->GetFullStringValue() : NULL;
}


//***************************************************************************
//
//  const TCHAR * CProvObj::sGetStringExp
//
//  DESCRIPTION:
//
//  Gets a substring for a particular token
//
//  PARAMETERS:
//
//  iToken              token to get
//  iExp                substring to get
//
//  RETURN VALUE:
//
//  pointer to substring, NULL if invalid argument
//***************************************************************************

const TCHAR * CProvObj::sGetStringExp(
                        IN int iToken,
                        IN int iExp)
{
    CToken * pCurr = GetTokenPointer(iToken);
    return (pCurr) ? pCurr->GetStringExp(iExp) : NULL;
}

//***************************************************************************
//
//  long int CProvObj::iGetIntExp
//
//  DESCRIPTION:
//
//  For a particular token, gets the integer value of a substring.
//
//  PARAMETERS:
//
//  iToken              token to get
//  iExp                substring
//  iArray              no longer used
//
//  RETURN VALUE:
//  
//  int value, or -1 if bad argument
//***************************************************************************

long int CProvObj::iGetIntExp(
                        IN int iToken, 
                        IN int iExp, 
                        IN int iArray)
{
    CToken * pCurr = GetTokenPointer(iToken);
    return (pCurr) ? pCurr->GetIntExp(iExp,iArray) : -1;
}

//***************************************************************************
//
//  BOOL CProvObj::IsExpString
//
//  DESCRIPTION:
//
//  Tests a substring to see if it is a string, or numeric
//
//  PARAMETERS:
//
//  iToken              token to get
//  iExp                substring
//
//
//  RETURN VALUE:
//
//  True if arguments are valid and it is not numeric
//***************************************************************************

BOOL CProvObj::IsExpString(
                        IN int iToken, 
                        IN int iExp)
{
    CToken * pCurr = GetTokenPointer(iToken);
    return (pCurr) ? pCurr->IsExpString(iExp) : FALSE;
}

//***************************************************************************
//
//  long int CProvObj::iGetNumExp
//
//  DESCRIPTION:
//
//  Gets the number of subexpressions
//
//  PARAMETERS:
//
//  iToken              token to check
//
//  RETURN VALUE:
//
//  number of substrings (subexpressions) or -1 if invalid argument
//***************************************************************************

long int CProvObj::iGetNumExp(
                        IN int iToken)
{
    CToken * pCurr = GetTokenPointer(iToken);
    return (pCurr) ? pCurr->GetNumExp() : -1;
}


//***************************************************************************
//
//  IWbemClassObject * GetNotifyObj
//
//  DESCRIPTION:
//
//  This utility is useful for setting notify objects 
//  at the end of async calls.
//
//  PARAMETERS:
//
//  pServices           pointer back into WBEM
//  lRet                status code to set in notify object
//                
//  RETURN VALUE:
//
//  Class object.  Null if failure.
//***************************************************************************

IWbemClassObject * GetNotifyObj(
                        IN IWbemServices * pServices, 
                        IN long lRet,
                        IN IWbemContext  *pCtx)
{
    
    if(pServices == NULL)
        return NULL;
    IWbemClassObject * pInst = NULL;
    IWbemClassObject * pClass = NULL;

    SCODE sc = pServices->GetObject(L"__ExtendedStatus", 0, pCtx, &pClass, NULL);
    if(sc != S_OK)
        return NULL;

    sc = pClass->SpawnInstance(0, &pInst);
    pClass->Release();

    if(sc == S_OK && pInst)
    {

        VARIANT v;
        v.vt = VT_I4;
        v.lVal = lRet;
        sc = pInst->Put(L"StatusCode", 0, &v, 0);
        if(sc != S_OK)
        {
            pInst->Release();
            return NULL;
        }
        else
            return pInst;

    }
    return NULL;

}