/*++

Copyright (c) 1995-2000  Microsoft Corporation

Module Name:

    dnsc_wmi.c

Abstract:

    WMI functions for DNSCMD

Author:

    Jeff Westhead (jwesth)      November 2000

Revision History:

--*/


#include "dnsclip.h"
#include "dnsc_wmi.h"


#define DNSCMD_CHECK_WMI_ENABLED()                          \
    if ( !g_UseWmi )                                        \
    {                                                       \
        ASSERT( g_UseWmi );                                 \
        printf( "Internal error: WMI is not enabled!\n" );  \
        return ERROR_NOT_SUPPORTED;                         \
    }

#define HRES_TO_STATUS( hres )      ( hres )

#define DNS_WMI_NAMESPACE           L"ROOT\\MicrosoftDNS"
#define DNS_WMI_RELPATH             L"__RELPATH"
#define DNS_WMI_TIMEOUT             20000               //  timeout in msecs

#define DNS_WMI_BLANK_STRING \
    L"                                                                       "

#define MYTEXT2(str)     L##str
#define MYTEXT(str)      MYTEXT2(str)


#define wmiRelease( pWmiObject )        \
    if ( pWmiObject )                   \
    {                                   \
        pWmiObject->Release();          \
        pWmiObject = NULL;              \
    }


//
//  Globals
//


IWbemServices *     g_pIWbemServices = NULL;


//
//  Static functions
//



static DNS_STATUS
getEnumerator( 
    IN      PSTR                    pszZoneName,
    OUT     IEnumWbemClassObject ** ppEnum
    )
/*++

Routine Description:

    Retrieves WMI object enumerator. The caller must call Release on
    the enum object when done.

Arguments:

    pszZoneName - zone name or NULL for server object

    ppEnum - ptr to ptr to WMI enumerator

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS      status = ERROR_SUCCESS;
    WCHAR           wsz[ 1024 ];
    BSTR            bstrWQL = NULL;
    BSTR            bstrQuery = NULL;
	HRESULT         hres = 0;

    if ( pszZoneName )
    {
        wsprintfW(
            wsz, 
            L"select * from MicrosoftDNS_Zone where Name='%S'",
            pszZoneName );
    }
    else
    {
        wsprintfW(
            wsz, 
            L"select * from MicrosoftDNS_Server" );
    }
    bstrWQL = SysAllocString( L"WQL" );
    bstrQuery = SysAllocString( wsz );
    if ( !bstrWQL || !bstrQuery )
    {
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    hres = g_pIWbemServices->ExecQuery(
                bstrWQL,
                bstrQuery,
                WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
                NULL,
                ppEnum );
    if ( FAILED( hres ) )
    {
        status = hres;
        goto Done;
    }

    Done:

    SysFreeString( bstrWQL );
    SysFreeString( bstrQuery );

    return status;
}   //  getEnumerator



static DNS_STATUS
getRelpath( 
    IN      IWbemClassObject  *     pObj,
    OUT     VARIANT *               pVar
    )
/*++

Routine Description:

    Loads a VARIANT with the WMI __RELPATH of the object.

Arguments:

    pObj - object to retrieve relpath for

    pVar - ptr to variant - caller must VariantClear() it later

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS      status = ERROR_SUCCESS;

    if ( pObj == NULL || pVar == NULL )
    {
        status = ERROR_INVALID_PARAMETER;
    }
    else
    {
        VariantClear( pVar );
        HRESULT hres = pObj->Get( DNS_WMI_RELPATH, 0, pVar, NULL, NULL );
        status = hres;
    }
    return status;
}   //  getRelpath



static DNS_STATUS
getNextObjectInEnum( 
    IN      IEnumWbemClassObject *  pEnum,
    OUT     IWbemClassObject **     ppObj,
    IN      bool                    fPrintRelpath = TRUE
    )
/*++

Routine Description:

    Retrieves WMI object enumerator. The caller must call Release
    on the class object.

    When there are no more objects to enumerate this function will
    return a NULL pObj and ERROR_SUCCESS.

Arguments:

    pszZoneName - zone name or NULL for server object

    ppEnum - ptr to ptr to WMI enumerator

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS      status = ERROR_SUCCESS;
    ULONG           returnedCount = 0;
	HRESULT         hres = 0;

    if ( !pEnum || !ppObj )
    {
        status = ERROR_INVALID_PARAMETER;
        goto Done;
    }

    hres = pEnum->Next(
                DNS_WMI_TIMEOUT,
                1,                  //  requested instance count
                ppObj,
                &returnedCount );
    if ( hres == WBEM_S_FALSE )
    {
        status = ERROR_SUCCESS;
        *ppObj = NULL;
    }

    if ( *ppObj && fPrintRelpath )
    {
        //
        //  Print RELPATH for this object.
        //

        VARIANT var;

        status = getRelpath( *ppObj, &var );
        if ( status == ERROR_SUCCESS )
        {
            printf( "%S\n", V_BSTR( &var ) );
        }
        else
        {
            printf( "WMI error 0x%08X getting RELPATH\n", hres );
        }
        VariantClear( &var );
    }

    Done:

    return status;
}   //  getNextObjectInEnum



static SAFEARRAY *
createSafeArrayForIpList( 
    IN      DWORD               dwIpCount,
    IN      PIP_ADDRESS         pIpList
    )
/*++

Routine Description:

    Creates a SAFEARRAY of strings representing a list of IP addresses.

Arguments:

    pIpList - array of IP address DWORDs

    dwIpCount - number of elements in pIpList

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    if ( !pIpList )
    {
        return NULL;
    }

    SAFEARRAYBOUND sabound = { dwIpCount, 0 };
    SAFEARRAY * psa = SafeArrayCreate( VT_BSTR, 1, &sabound );
    for ( ULONG i = 0; i < dwIpCount; ++i )
    {
        PWSTR pwsz = ( PWSTR ) Dns_NameCopyAllocate(
                                inet_ntoa(
                                    *( struct in_addr * )
                                    &pIpList[ i ] ),
                                0,
                                DnsCharSetUtf8,
                                DnsCharSetUnicode );
        BSTR bstr = SysAllocString( pwsz );
        SafeArrayPutElement(
            psa,
            ( PLONG ) &i,
            bstr );
        SysFreeString( bstr );
        FREE_HEAP( pwsz );
    }
    return psa;
}



static SAFEARRAY *
createSafeArrayForIpArray( 
    IN      PIP_ARRAY       pIpArray
    )
/*++

Routine Description:

    Creates a SAFEARRAY of strings representing the IP addresses
    in pIpArray. 

Arguments:

    pIpArray - IP array to create string SAFEARRAY for

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    if ( !pIpArray )
    {
        return NULL;
    }
    return createSafeArrayForIpList(
                pIpArray->AddrCount,
                pIpArray->AddrArray );
}



PWCHAR 
valueToString(
    CIMTYPE dwType,
    VARIANT *pValue,
    WCHAR **pbuf )
/*++

Routine Description:

    Convert VARIANT to string. Stole this code from WMI\Samples\VC\UtilLib.

Arguments:

    dwType - value CIMTYPE

    pValue - value to convert to string

    **pbuf - ptr to allocated string buffer - caller must free() it

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    #define BLOCKSIZE                   ( 32 * sizeof( WCHAR ) )
    #define CVTBUFSIZE                  ( 309 + 40 )
    
    DWORD iTotBufSize, iLen;
    
    WCHAR *vbuf = NULL;
    WCHAR *buf = NULL;
    
    WCHAR lbuf[BLOCKSIZE];
    
    switch (pValue->vt) 
    {
        
    case VT_EMPTY:
        buf = (WCHAR *)malloc(BLOCKSIZE);
        if ( !buf ) goto AllocFailed;
        wcscpy(buf, L"<empty>");
        break;
        
    case VT_NULL:
        buf = (WCHAR *)malloc(BLOCKSIZE);
        if ( !buf ) goto AllocFailed;
        wcscpy(buf, L"<null>");
        break;
        
    case VT_BOOL: 
        {
            VARIANT_BOOL b = pValue->boolVal;
            buf = (WCHAR *)malloc(BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            if (!b) {
                wcscpy(buf, L"FALSE");
            } else {
                wcscpy(buf, L"TRUE");
            }
            break;
        }
        
    case VT_I1: 
        {
            char b = pValue->bVal;
            buf = (WCHAR *)malloc(BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            if (b >= 32) {
                swprintf(buf, L"'%c' (%hd, 0x%hX)", b, (signed char)b, b);
            } else {
                swprintf(buf, L"%hd (0x%hX)", (signed char)b, b);
            }
            break;
        }
        
    case VT_UI1: 
        {
            unsigned char b = pValue->bVal;
            buf = (WCHAR *)malloc(BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            if (b >= 32) {
                swprintf(buf, L"'%c' (%hu, 0x%hX)", b, (unsigned char)b, b);
            } else {
                swprintf(buf, L"%hu (0x%hX)", (unsigned char)b, b);
            }
            break;
        }
        
    case VT_I2:
        {
            SHORT i = pValue->iVal;
            buf = (WCHAR *)malloc(BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            swprintf(buf, L"%hd (0x%hX)", i, i);
            break;
        }
        
    case VT_UI2:
        {
            USHORT i = pValue->uiVal;
            buf = (WCHAR *)malloc(BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            swprintf(buf, L"%hu (0x%hX)", i, i);
            break;
        }
        
    case VT_I4: 
        {
            LONG l = pValue->lVal;
            buf = (WCHAR *)malloc(BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            swprintf(buf, L"%d (0x%X)", l, l);
            break;
        }
        
    case VT_UI4: 
        {
            ULONG l = pValue->ulVal;
            buf = (WCHAR *)malloc(BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            swprintf(buf, L"%u (0x%X)", l, l);
            break;
        }
        
    case VT_R4: 
        {
            float f = pValue->fltVal;
            buf = (WCHAR *)malloc(CVTBUFSIZE * sizeof(WCHAR));
            if ( !buf ) goto AllocFailed;
            swprintf(buf, L"%10.4f", f);
            break;
        }
        
    case VT_R8: 
        {
            double d = pValue->dblVal;
            buf = (WCHAR *)malloc(CVTBUFSIZE * sizeof(WCHAR));
            if ( !buf ) goto AllocFailed;
            swprintf(buf, L"%10.4f", d);
            break;
        }
        
    case VT_BSTR: 
        {
            if (dwType == CIM_SINT64)
            {
                // a little redundant, but it makes me feel better
                LPWSTR pWStr = pValue->bstrVal;
                __int64 l = _wtoi64(pWStr);
                
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                swprintf(buf, L"%I64d", l, l);
            } 
            else if (dwType == CIM_UINT64)
            {
                // a little redundant, but it makes me feel better
                LPWSTR pWStr = pValue->bstrVal;
                __int64 l = _wtoi64(pWStr);
                
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                swprintf(buf, L"%I64u", l, l);
            }
            else // string, datetime, reference
            {
                LPWSTR pWStr = pValue->bstrVal;
                buf = (WCHAR *)malloc((wcslen(pWStr) * sizeof(WCHAR)) + sizeof(WCHAR) + (2 * sizeof(WCHAR)));
                if ( !buf ) goto AllocFailed;
                swprintf(buf, L"%wS", pWStr);
            }
            break;
        }
        
    case VT_BOOL|VT_ARRAY: 
        {
            SAFEARRAY *pVec = pValue->parray;
            long iLBound, iUBound;
            BOOL bFirst = TRUE;
            
            SafeArrayGetLBound(pVec, 1, &iLBound);
            SafeArrayGetUBound(pVec, 1, &iUBound);
            if ((iUBound - iLBound + 1) == 0) {
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"<empty array>");
                break;
            }
            
            buf = (WCHAR *)malloc((iUBound - iLBound + 1) * BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            wcscpy(buf, L"");
            
            for (long i = iLBound; i <= iUBound; i++) {
                if (!bFirst) {
                    wcscat(buf, L",");
                } else {
                    bFirst = FALSE;
                }
                
                VARIANT_BOOL v;
                SafeArrayGetElement(pVec, &i, &v);
                if (v) {
                    wcscat(buf, L"TRUE");
                } else {
                    wcscat(buf, L"FALSE");
                }
            }
            
            break;
        }
        
    case VT_I1|VT_ARRAY: 
        {
            SAFEARRAY *pVec = pValue->parray;
            long iLBound, iUBound;
            BOOL bFirst = TRUE;
            
            SafeArrayGetLBound(pVec, 1, &iLBound);
            SafeArrayGetUBound(pVec, 1, &iUBound);
            if ((iUBound - iLBound + 1) == 0) {
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"<empty array>");
                break;
            }
            
            buf = (WCHAR *)malloc((iUBound - iLBound + 1) * BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            wcscpy(buf, L"");
            WCHAR *pos = buf;
            DWORD len;
            
            BYTE *pbstr;
            SafeArrayAccessData(pVec, (void HUGEP* FAR*)&pbstr);
            
            for (long i = iLBound; i <= iUBound; i++) {
                if (!bFirst) {
                    wcscpy(pos, L",");
                    pos += 1;
                } else {
                    bFirst = FALSE;
                }
                
                char v;
                //            SafeArrayGetElement(pVec, &i, &v);
                v = pbstr[i];
                
                if (v < 32) {
                    len = swprintf(lbuf, L"%hd (0x%X)", v, v);
                } else {
                    len = swprintf(lbuf, L"'%c' %hd (0x%X)", v, v, v);
                }
                
                wcscpy(pos, lbuf);
                pos += len;
                
            }
            
            SafeArrayUnaccessData(pVec);
            
            break;
        }
        
    case VT_UI1|VT_ARRAY: 
        {
            SAFEARRAY *pVec = pValue->parray;
            long iLBound, iUBound;
            BOOL bFirst = TRUE;
            
            SafeArrayGetLBound(pVec, 1, &iLBound);
            SafeArrayGetUBound(pVec, 1, &iUBound);
            if ((iUBound - iLBound + 1) == 0) {
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"<empty array>");
                break;
            }
            
            buf = (WCHAR *)malloc((iUBound - iLBound + 1) * BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            wcscpy(buf, L"");
            WCHAR *pos = buf;
            DWORD len;
            
            BYTE *pbstr;
            SafeArrayAccessData(pVec, (void HUGEP* FAR*)&pbstr);
            
            for (long i = iLBound; i <= iUBound; i++) {
                if (!bFirst) {
                    wcscpy(pos, L",");
                    pos += 1;
                } else {
                    bFirst = FALSE;
                }
                
                unsigned char v;
                //            SafeArrayGetElement(pVec, &i, &v);
                v = pbstr[i];
                
                if (v < 32) {
                    len = swprintf(lbuf, L"%hu (0x%X)", v, v);
                } else {
                    len = swprintf(lbuf, L"'%c' %hu (0x%X)", v, v, v);
                }
                
                wcscpy(pos, lbuf);
                pos += len;
                
            }
            
            SafeArrayUnaccessData(pVec);
            
            break;
        }
        
    case VT_I2|VT_ARRAY: 
        {
            SAFEARRAY *pVec = pValue->parray;
            long iLBound, iUBound;
            BOOL bFirst = TRUE;
            
            SafeArrayGetLBound(pVec, 1, &iLBound);
            SafeArrayGetUBound(pVec, 1, &iUBound);
            if ((iUBound - iLBound + 1) == 0) {
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"<empty array>");
                break;
            }
            
            buf = (WCHAR *)malloc((iUBound - iLBound + 1) * BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            wcscpy(buf, L"");
            
            for (long i = iLBound; i <= iUBound; i++) {
                if (!bFirst) {
                    wcscat(buf, L",");
                } else {
                    bFirst = FALSE;
                }
                
                SHORT v;
                SafeArrayGetElement(pVec, &i, &v);
                swprintf(lbuf, L"%hd", v);
                wcscat(buf, lbuf);
            }
            
            break;
        }
        
    case VT_UI2|VT_ARRAY: 
        {
            SAFEARRAY *pVec = pValue->parray;
            long iLBound, iUBound;
            BOOL bFirst = TRUE;
            
            SafeArrayGetLBound(pVec, 1, &iLBound);
            SafeArrayGetUBound(pVec, 1, &iUBound);
            if ((iUBound - iLBound + 1) == 0) {
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"<empty array>");
                break;
            }
            
            buf = (WCHAR *)malloc((iUBound - iLBound + 1) * BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            wcscpy(buf, L"");
            
            for (long i = iLBound; i <= iUBound; i++) {
                if (!bFirst) {
                    wcscat(buf, L",");
                } else {
                    bFirst = FALSE;
                }
                
                USHORT v;
                SafeArrayGetElement(pVec, &i, &v);
                swprintf(lbuf, L"%hu", v);
                wcscat(buf, lbuf);
            }
            
            break;
        }
        
    case VT_I4|VT_ARRAY: 
        {
            SAFEARRAY *pVec = pValue->parray;
            long iLBound, iUBound;
            BOOL bFirst = TRUE;
            
            SafeArrayGetLBound(pVec, 1, &iLBound);
            SafeArrayGetUBound(pVec, 1, &iUBound);
            if ((iUBound - iLBound + 1) == 0) {
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"<empty array>");
                break;
            }
            
            buf = (WCHAR *)malloc((iUBound - iLBound + 1) * BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            wcscpy(buf, L"");
            
            for (long i = iLBound; i <= iUBound; i++) {
                if (!bFirst) {
                    wcscat(buf, L",");
                } else {
                    bFirst = FALSE;
                }
                
                LONG v;
                SafeArrayGetElement(pVec, &i, &v);
                _ltow(v, lbuf, 10);
                wcscat(buf, lbuf);
            }
            
            break;
        }
        
    case VT_UI4|VT_ARRAY: 
        {
            SAFEARRAY *pVec = pValue->parray;
            long iLBound, iUBound;
            BOOL bFirst = TRUE;
            
            SafeArrayGetLBound(pVec, 1, &iLBound);
            SafeArrayGetUBound(pVec, 1, &iUBound);
            if ((iUBound - iLBound + 1) == 0) {
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"<empty array>");
                break;
            }
            
            buf = (WCHAR *)malloc((iUBound - iLBound + 1) * BLOCKSIZE);
            if ( !buf ) goto AllocFailed;
            wcscpy(buf, L"");
            
            for (long i = iLBound; i <= iUBound; i++) {
                if (!bFirst) {
                    wcscat(buf, L",");
                } else {
                    bFirst = FALSE;
                }
                
                ULONG v;
                SafeArrayGetElement(pVec, &i, &v);
                _ultow(v, lbuf, 10);
                wcscat(buf, lbuf);
            }
            
            break;
        }
        
    case CIM_REAL32|VT_ARRAY: 
        {
            SAFEARRAY *pVec = pValue->parray;
            long iLBound, iUBound;
            BOOL bFirst = TRUE;
            
            SafeArrayGetLBound(pVec, 1, &iLBound);
            SafeArrayGetUBound(pVec, 1, &iUBound);
            if ((iUBound - iLBound + 1) == 0) {
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"<empty array>");
                break;
            }
            
            buf = (WCHAR *)malloc((iUBound - iLBound + 1) * (CVTBUFSIZE * sizeof(WCHAR)));
            if ( !buf ) goto AllocFailed;
            wcscpy(buf, L"");
            
            for (long i = iLBound; i <= iUBound; i++) {
                if (!bFirst) {
                    wcscat(buf, L",");
                } else {
                    bFirst = FALSE;
                }
                
                FLOAT v;
                SafeArrayGetElement(pVec, &i, &v);
                swprintf(lbuf, L"%10.4f", v);
                wcscat(buf, lbuf);
            }
            
            break;
        }
        
    case CIM_REAL64|VT_ARRAY: 
        {
            SAFEARRAY *pVec = pValue->parray;
            long iLBound, iUBound;
            BOOL bFirst = TRUE;
            
            SafeArrayGetLBound(pVec, 1, &iLBound);
            SafeArrayGetUBound(pVec, 1, &iUBound);
            if ((iUBound - iLBound + 1) == 0) {
                buf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"<empty array>");
                break;
            }
            
            buf = (WCHAR *)malloc((iUBound - iLBound + 1) * (CVTBUFSIZE * sizeof(WCHAR)));
            if ( !buf ) goto AllocFailed;
            wcscpy(buf, L"");
            
            for (long i = iLBound; i <= iUBound; i++) {
                if (!bFirst) {
                    wcscat(buf, L",");
                } else {
                    bFirst = FALSE;
                }
                
                double v;
                SafeArrayGetElement(pVec, &i, &v);
                swprintf(lbuf, L"%10.4f", v);
                wcscat(buf, lbuf);
            }
            
            break;
        }
        
    case VT_BSTR|VT_ARRAY: 
        {
            
            if (dwType == (CIM_UINT64|VT_ARRAY))
            {
                SAFEARRAY *pVec = pValue->parray;
                long iLBound, iUBound;
                BOOL bFirst = TRUE;
                
                SafeArrayGetLBound(pVec, 1, &iLBound);
                SafeArrayGetUBound(pVec, 1, &iUBound);
                if ((iUBound - iLBound + 1) == 0) {
                    buf = (WCHAR *)malloc(BLOCKSIZE);
                    if ( !buf ) goto AllocFailed;
                    wcscpy(buf, L"<empty array>");
                    break;
                }
                
                buf = (WCHAR *)malloc((iUBound - iLBound + 1) * BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"");
                
                for (long i = iLBound; i <= iUBound; i++) {
                    if (!bFirst) {
                        wcscat(buf, L",");
                    } else {
                        bFirst = FALSE;
                    }
                    
                    BSTR v = NULL;
                    
                    SafeArrayGetElement(pVec, &i, &v);
                    
                    swprintf(lbuf, L"%I64u", _wtoi64(v));
                    wcscat(buf, lbuf);
                }
            }
            else if (dwType == (CIM_SINT64|VT_ARRAY))
            {
                SAFEARRAY *pVec = pValue->parray;
                long iLBound, iUBound;
                BOOL bFirst = TRUE;
                
                SafeArrayGetLBound(pVec, 1, &iLBound);
                SafeArrayGetUBound(pVec, 1, &iUBound);
                if ((iUBound - iLBound + 1) == 0) {
                    buf = (WCHAR *)malloc(BLOCKSIZE);
                    if ( !buf ) goto AllocFailed;
                    wcscpy(buf, L"<empty array>");
                    break;
                }
                
                buf = (WCHAR *)malloc((iUBound - iLBound + 1) * BLOCKSIZE);
                if ( !buf ) goto AllocFailed;
                wcscpy(buf, L"");
                
                for (long i = iLBound; i <= iUBound; i++) {
                    if (!bFirst) {
                        wcscat(buf, L",");
                    } else {
                        bFirst = FALSE;
                    }
                    
                    BSTR v = NULL;
                    
                    SafeArrayGetElement(pVec, &i, &v);
                    
                    swprintf(lbuf, L"%I64d", _wtoi64(v));
                    wcscat(buf, lbuf);
                }
            }
            else // string, datetime, reference
            {
                
                SAFEARRAY *pVec = pValue->parray;
                long iLBound, iUBound;
                DWORD iNeed;
                DWORD iVSize;
                DWORD iCurBufSize;
                
                SafeArrayGetLBound(pVec, 1, &iLBound);
                SafeArrayGetUBound(pVec, 1, &iUBound);
                if ((iUBound - iLBound + 1) == 0) {
                    buf = (WCHAR *)malloc(BLOCKSIZE);
                    if ( !buf ) goto AllocFailed;
                    wcscpy(buf, L"<empty array>");
                    break;
                }
                
                iTotBufSize = (iUBound - iLBound + 1) * BLOCKSIZE;
                buf = (WCHAR *)malloc(iTotBufSize);
                if ( !buf ) goto AllocFailed;
                buf[0] = L'\0';
                iCurBufSize = 0;
                iVSize = BLOCKSIZE;
                vbuf = (WCHAR *)malloc(BLOCKSIZE);
                if ( !vbuf ) goto AllocFailed;
                
                for (long i = iLBound; i <= iUBound; i++) {
                    BSTR v = NULL;
                    SafeArrayGetElement(pVec, &i, &v);
                    iLen = (wcslen(v) + 1) * sizeof(WCHAR);
                    if (iLen > iVSize) {
                        vbuf = (WCHAR *)realloc(vbuf, iLen + sizeof(WCHAR));
                        iVSize = iLen;
                    }
                    
                    // String size + (quotes + comma + null)
                    iNeed = (swprintf(vbuf, L"%wS", v) + 4) * sizeof(WCHAR);
                    if (iNeed + iCurBufSize > iTotBufSize) {
                        iTotBufSize += (iNeed * 2);  // Room enough for 2 more entries
                        buf = (WCHAR *)realloc(buf, iTotBufSize);
                    }
                    wcscat(buf, L"\"");
                    wcscat(buf, vbuf);
                    if (i + 1 <= iUBound) {
                        wcscat(buf, L"\",");
                    } else {
                        wcscat(buf, L"\"");
                    }
                    iCurBufSize += iNeed;
                    SysFreeString(v);
                    
                }
                free(vbuf);
            }
            
            break;
      }
      
      default: 
          {
              buf = (WCHAR *)malloc(BLOCKSIZE);
              if ( !buf ) goto AllocFailed;
              wcscpy(buf, L"<conversion error>");
              break;
          }
          
   }
   
   AllocFailed:

   *pbuf = buf;   
   return buf;
}   //  valueToString


DNS_STATUS
printWmiObjectProperties(
    IWbemClassObject *      pObj
    )
{
    DNS_STATUS          status = ERROR_SUCCESS;
	HRESULT             hres = 0;
    SAFEARRAY *         pNames = NULL;
    BSTR                bstrPropName = NULL;
    VARIANT             var;
    BSTR                bstrCimType = SysAllocString( L"CIMTYPE" );
    PWSTR               pwszVal = NULL;

    if ( !bstrCimType )
    {
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    VariantClear( &var );

    //
    //  Get the RELPATH for this object.
    //

    status = getRelpath( pObj, &var );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }
    printf( "%S\n\n", V_BSTR( &var ) );

    //
    //  Enumerate all properties of this object.
    //

    hres = pObj->GetNames(
                    NULL,               //  qualifier
                    WBEM_FLAG_ALWAYS | WBEM_FLAG_NONSYSTEM_ONLY,
                    NULL,               //  qualifier value
                    &pNames );
    if ( FAILED( hres ) )
    {
        goto Done;
    }
    ASSERT( pNames );

    long lowerBound;
    long upperBound; 
    SafeArrayGetLBound( pNames, 1, &lowerBound );
    SafeArrayGetUBound( pNames, 1, &upperBound );

    for ( long i = lowerBound; i <= upperBound; ++i )
    {
        //
        //  Print the name and type of this property value.
        //

        hres = SafeArrayGetElement( pNames, &i, &bstrPropName );
        if ( !SUCCEEDED( hres ) )
        {
            ASSERT( SUCCEEDED( hres ) );
            continue;
        }

        IWbemQualifierSet * pQualSet = NULL;
        hres = pObj->GetPropertyQualifierSet( bstrPropName, &pQualSet );
        if ( !SUCCEEDED( hres ) )
        {
            ASSERT( SUCCEEDED( hres ) );
            continue;
        }

        VariantClear( &var );
        pQualSet->Get( bstrCimType, 0, &var, NULL );
        if ( !SUCCEEDED( hres ) )
        {
            ASSERT( SUCCEEDED( hres ) );
            continue;
        }

        int padlen = 30 - wcslen( bstrPropName ) - wcslen( V_BSTR( &var ) );
        printf(
            "%S (%S) %.*S = ",
            bstrPropName,
            V_BSTR( &var ),
            padlen > 0 ? padlen : 0,
            DNS_WMI_BLANK_STRING );

        //
        //  Print the property value.
        //

        VariantClear( &var );
        CIMTYPE cimType = 0;
        hres = pObj->Get( bstrPropName, 0, &var, &cimType, NULL );
        if ( !SUCCEEDED( hres ) )
        {
            ASSERT( SUCCEEDED( hres ) );
            continue;
        }

        printf( "%S\n", valueToString( cimType, &var, &pwszVal ) );
        free( pwszVal );
        pwszVal = NULL;
    }

    Done:

    free( pwszVal );
    SysFreeString( bstrCimType );
    SafeArrayDestroy( pNames );

    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = HRES_TO_STATUS( hres );
    }

    return status;
}   //  printWmiObjectProperties


//
//  External functions
//



DNS_STATUS
DnscmdWmi_Initialize(
    IN      PWSTR       pwszServerName
    )
/*++

Routine Description:

    Setup mod buffer for max count of items.

Arguments:

    pwszServerName -- IP address or name of target server

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    static const char * fn = "DnscmdWmi_Initialize";

    DNS_STATUS          status = ERROR_SUCCESS;
	HRESULT             hres = 0;
	IWbemLocator *      pIWbemLocator = NULL;
    BSTR                bstrNamespace = NULL;
    IWbemServices *     pIWbemServices = NULL;
    WCHAR               wsz[ 1024 ];

    DNSCMD_CHECK_WMI_ENABLED();

    //
    //  Initialize COM.
    //

    if ( FAILED( hres = CoInitialize( NULL ) ) )
    {
        printf( "%s: CoInitialize returned 0x%08X\n", fn, hres );
        goto Done;
    }

    //
    //  Initialize security.
    //

    hres = CoInitializeSecurity(
                NULL,                   //  permissions
                -1,                     //  auth service count
                NULL,                   //  auth services
                NULL,                   //  reserved
                RPC_C_AUTHZ_NONE,
                RPC_C_IMP_LEVEL_IMPERSONATE,
                NULL,                   //  auth list
                0,                      //  capabilities
                0 );                    //  reserved
    if ( FAILED( hres ) )
    {
        printf(
            "%s: CoInitializeSecurity() returned 0x%08X\n",
            fn,
            hres );
        goto Done;
    }

    //
    //  Create instance of WbemLocator interface.
    //

    hres = CoCreateInstance(
                    CLSID_WbemLocator,
                    NULL,
                    CLSCTX_INPROC_SERVER,
                    IID_IWbemLocator,
                    ( LPVOID * ) &pIWbemLocator );
    if ( FAILED( hres ) )
    {
        printf(
            "%s: CoCreateInstance( CLSID_WbemLocator ) returned 0x%08X\n",
            fn,
            hres );
        goto Done;
    }

    //
    //  Connect to MicrosoftDNS namespace on server.
    //

    wsprintfW(
        wsz,
        L"\\\\%s\\%s",
        pwszServerName,
        DNS_WMI_NAMESPACE );
    bstrNamespace = SysAllocString( wsz );
    if ( !bstrNamespace )
    {
        ASSERT( bstrNamespace );
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }
        
    hres = pIWbemLocator->ConnectServer(
                                bstrNamespace,
                                NULL,               //  user id
                                NULL,               //  password
                                NULL,               //  locale
                                0,                  //  security flags
                                NULL,               //  domain
                                NULL,               //  context
                                &pIWbemServices );
    if ( FAILED( hres ) )
    {
        printf(
            "%s: ConnectServer( %S ) returned 0x%08X\n",
            fn,
            DNS_WMI_NAMESPACE,
            hres );
        goto Done;
    }

    if ( !pIWbemServices )
    {
        ASSERT( pIWbemServices );
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    //
    //  Set security.
    //

    hres = CoSetProxyBlanket(
                pIWbemServices,
                RPC_C_AUTHN_WINNT,
                RPC_C_AUTHZ_NONE,
                NULL,                           //  principal name
                RPC_C_AUTHN_LEVEL_CALL,
                RPC_C_IMP_LEVEL_IMPERSONATE,
                NULL,                           //  client identify
                EOAC_NONE );
    if ( FAILED( hres ) )
    {
        printf(
            "%s: CoSetProxyBlanket() returned 0x%08X\n",
            fn,
            hres );
        goto Done;
    }

    //
    //  Cleanup and return.
    //

    Done:
    
    SysFreeString( bstrNamespace );

    if ( pIWbemLocator )
    {
        pIWbemLocator->Release();
    }

    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = HRES_TO_STATUS( hres );
    }

    if ( status == ERROR_SUCCESS )
    {
        g_pIWbemServices = pIWbemServices;
    }

    return status;
}   //  DnscmdWmi_Initialize



DNS_STATUS
DnscmdWmi_Free(
    VOID
    )
/*++

Routine Description:

    Close WMI session and free globals.

Arguments:

    None.

Return Value:

    None.

--*/
{
    DBG_FN( "DnscmdWmi_Free" )

    DNSCMD_CHECK_WMI_ENABLED();

    if ( g_pIWbemServices )
    {
        g_pIWbemServices->Release();
        g_pIWbemServices = NULL;
    }

    CoUninitialize();

    return ERROR_SUCCESS;
}   //  DnscmdWmi_Free



DNS_STATUS
DnscmdWmi_ProcessDnssrvQuery(
    IN      PSTR        pszZoneName,
    IN      PCSTR       pszQuery
    )
/*++

Routine Description:

    Perform query.

Arguments:

    pszZoneName -- zone name or NULL for server level query

    pszQuery -- query name

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS              status = ERROR_SUCCESS;
    BSTR                    bstrClassName = NULL;
    IEnumWbemClassObject *  pEnum = NULL;
    IWbemClassObject *      pObj = NULL;
    ULONG                   returnedCount = 1;

    DNSCMD_CHECK_WMI_ENABLED();

    //
    //  Get WMI object.
    //

    status = getEnumerator( pszZoneName, &pEnum );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }

    status = getNextObjectInEnum( pEnum, &pObj );
    if ( status != ERROR_SUCCESS || !pObj )
    {
        goto Done;
    }

    printWmiObjectProperties( pObj );

    //
    //  Cleanup and return.
    //

    Done:

    SysFreeString( bstrClassName );

    if ( pObj )
    {
        pObj->Release();
    }

    if ( pEnum )
    {
        pEnum->Release();
    }
    
    return status;
}   //  DnscmdWmi_ProcessDnssrvQuery



DNS_STATUS
DnscmdWmi_ProcessEnumZones(
    IN      DWORD                   dwFilter
    )
/*++

Routine Description:

    Enumerate zones.

Arguments:

    dwFilter -- filter

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS              status = ERROR_SUCCESS;
    BSTR                    bstrClassName = NULL;
    IEnumWbemClassObject *  pEnum = NULL;
    IWbemClassObject *      pObj = NULL;
    HRESULT                 hres = 0;
    ULONG                   returnedCount = 1;

    DNSCMD_CHECK_WMI_ENABLED();

    //
    //  Create zone enumerator.
    //

    bstrClassName = SysAllocString( L"MicrosoftDNS_Zone" );
    if ( !bstrClassName )
    {
        ASSERT( bstrClassName );
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    hres = g_pIWbemServices->CreateInstanceEnum(
                                bstrClassName,
                                0,                  //  flags
                                NULL,               //  context
                                &pEnum );
    if ( FAILED( hres ) )
    {
        goto Done;
    }
    ASSERT( pEnum );

    //
    //  Enumerate zones.
    //

    while ( returnedCount == 1 )
    {
        VARIANT             val;
        CIMTYPE             cimtype = 0;
        PWSTR               pwszVal = NULL;

        VariantInit( &val );

        status = getNextObjectInEnum( pEnum, &pObj, FALSE );
        if ( status != ERROR_SUCCESS || !pObj )
        {
            goto Done;
        }

        //
        //  Print properties for this zone.
        //

        #define CHECK_HRES( hresult, propname )                             \
        if ( FAILED( hresult ) )                                            \
            {                                                               \
            printf( "\n\nWMI error 0x%08X reading property %S!\n",          \
                    hresult, propname );                                    \
            goto Done;                                                      \
            }

        hres = pObj->Get( L"Name", 0, &val, &cimtype, NULL );
        CHECK_HRES( hres, L"Name" );
        printf( " %-29S", valueToString( cimtype, &val, &pwszVal ) );
        free( pwszVal );
        VariantClear( &val );

        hres = pObj->Get( L"ZoneType", 0, &val, &cimtype, NULL );
        CHECK_HRES( hres, L"ZoneType" );
        ASSERT( val.vt == VT_I4 );
        printf( "%3d  ", val.lVal );
        VariantClear( &val );

        hres = pObj->Get( L"DsIntegrated", 0, &val, &cimtype, NULL );
        CHECK_HRES( hres, L"DsIntegrated" );
        ASSERT( val.vt == VT_BOOL );
        printf( "%-4S  ", val.boolVal ? L"DS" : L"file" );
        VariantClear( &val );

        hres = pObj->Get( L"Reverse", 0, &val, &cimtype, NULL );
        CHECK_HRES( hres, L"Reverse" );
        ASSERT( val.vt == VT_BOOL );
        printf( "%-3S  ", val.boolVal ? L"Rev" : L"" );
        VariantClear( &val );

        hres = pObj->Get( L"AutoCreated", 0, &val, &cimtype, NULL );
        CHECK_HRES( hres, L"AutoCreated" );
        ASSERT( val.vt == VT_BOOL );
        printf( "%-4S  ", val.boolVal ? L"Auto" : L"" );
        VariantClear( &val );

        hres = pObj->Get( L"AllowUpdate", 0, &val, &cimtype, NULL );
        CHECK_HRES( hres, L"AllowUpdate" );
        ASSERT( val.vt == VT_BOOL );
        printf( "Up=%d ", val.boolVal ? 1 : 0 );
        VariantClear( &val );

        hres = pObj->Get( L"Aging", 0, &val, &cimtype, NULL );
        CHECK_HRES( hres, L"Aging" );
        ASSERT( val.vt == VT_BOOL );
        printf( "%-5S ", val.boolVal ? L"Aging" : L"" );
        VariantClear( &val );

        hres = pObj->Get( L"Paused", 0, &val, &cimtype, NULL );
        CHECK_HRES( hres, L"Paused" );
        ASSERT( val.vt == VT_BOOL );
        printf( "%-6S ", val.boolVal ? L"Paused" : L"" );
        VariantClear( &val );

        hres = pObj->Get( L"Shutdown", 0, &val, &cimtype, NULL );
        CHECK_HRES( hres, L"Shutdown" );
        ASSERT( val.vt == VT_BOOL );
        printf( "%-6S", val.boolVal ? L"Shutdn" : L"" );
        VariantClear( &val );

        printf( "\n\n" );
    }

    //
    //  Cleanup and return.
    //

    Done:

    SysFreeString( bstrClassName );

    if ( pObj )
    {
        pObj->Release();
    }

    if ( pEnum )
    {
        pEnum->Release();
    }
    
    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = HRES_TO_STATUS( hres );
    }

    return status;
}   //  DnscmdWmi_ProcessEnumZones



DNS_STATUS
DnscmdWmi_ProcessZoneInfo(
    IN      LPSTR                   pszZone
    )
/*++

Routine Description:

    Enumerate zones.

Arguments:

    dwFilter -- filter

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS              status = ERROR_SUCCESS;
    IWbemClassObject *      pObj = NULL;
    IEnumWbemClassObject *  pEnum = NULL;
    ULONG                   returnedCount = 1;
    WCHAR                   wsz[ 1024 ];

    DNSCMD_CHECK_WMI_ENABLED();

    //
    //  Get WMI object.
    //

    status = getEnumerator( pszZone, &pEnum );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }

    status = getNextObjectInEnum( pEnum, &pObj, FALSE );
    if ( status != ERROR_SUCCESS || !pObj )
    {
        goto Done;
    }

    printWmiObjectProperties( pObj );

    //
    //  Cleanup and return.
    //

    Done:

    if ( pObj )
    {
        pObj->Release();
    }

    if ( pEnum )
    {
        pEnum->Release();
    }
    
    return status;
}   //  DnscmdWmi_ProcessZoneInfo



DNS_STATUS
DnscmdWmi_ProcessEnumRecords(
    IN      LPSTR                   pszZone,
    IN      LPSTR                   pszNode,
    IN      BOOL                    fDetail,
    IN      DWORD                   dwFlags
    )
/*++

Routine Description:

    Enumerate Records.

Arguments:

    pszZone -- zone name

    pszNode -- name of root node to at which to enumerate records

    fDetail -- print summary or full detail

    dwFlags -- search flags

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS              status = ERROR_SUCCESS;
    BSTR                    bstrWQL = NULL;
    BSTR                    bstrQuery = NULL;
    IWbemClassObject *      pObj = NULL;
    IEnumWbemClassObject *  pEnum = NULL;
    HRESULT                 hres = 0;
    ULONG                   returnedCount = 1;
    WCHAR                   wsz[ 1024 ];

    DNSCMD_CHECK_WMI_ENABLED();

    //
    //  Query for zone.
    //

    if ( pszNode == NULL || strcmp( pszNode, "@" ) == 0 )
    {
        wsprintfW(
            wsz, 
            L"select * from MicrosoftDNS_ResourceRecord "
                L"where ContainerName='%S'",
            pszZone );
    }
    else
    {
        wsprintfW(
            wsz, 
            L"select * from MicrosoftDNS_ResourceRecord "
                L"where DomainName='%S'",
            pszNode );
    }
    bstrWQL = SysAllocString( L"WQL" );
    bstrQuery = SysAllocString( wsz );
    if ( !bstrWQL || !bstrQuery )
    {
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    hres = g_pIWbemServices->ExecQuery(
                bstrWQL,
                bstrQuery,
                WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
                NULL,
                &pEnum );
    if ( FAILED( hres ) )
    {
        goto Done;
    }
    ASSERT( pEnum );

    //
    //  Dump results.
    //

    while ( 1 )
    {
        status = getNextObjectInEnum( pEnum, &pObj );
        if ( status != ERROR_SUCCESS || !pObj )
        {
            break;
        }

        if ( fDetail )
        {
            printWmiObjectProperties( pObj );
        }
        else
        {
            VARIANT             val;
            CIMTYPE             cimtype = 0;
            PWSTR               pwszVal = NULL;

            VariantInit( &val );
            hres = pObj->Get( L"TextRepresentation", 0, &val, &cimtype, NULL );
            CHECK_HRES( hres, L"TextRepresentation" );
            printf( "%S", valueToString( cimtype, &val, &pwszVal ) );
            free( pwszVal );
            VariantClear( &val );

            printf( "\n" );
        }
    }

    //
    //  Cleanup and return.
    //

    Done:

    SysFreeString( bstrWQL );
    SysFreeString( bstrQuery );

    if ( pObj )
    {
        pObj->Release();
    }

    if ( pEnum )
    {
        pEnum->Release();
    }
    
    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = HRES_TO_STATUS( hres );
    }

    return status;
}   //  DnscmdWmi_ProcessEnumRecords



DNS_STATUS
DnscmdWmi_ResetProperty(
    IN      LPSTR                   pszZone,
    IN      LPSTR                   pszProperty,
    IN      DWORD                   cimType,
    IN      PVOID                   value
    )
/*++

Routine Description:

    Reset a server or zone property.

Arguments:

    pszZone -- zone name - NULL for server property

    pszProperty -- name of property to set

    cimType -- variant type of the property, use one of:
        VT_I4 - DWORD
        VT_BSTR - string
        ? - IP list

    value -- new value for property, interpreted based on cimtype:
        VT_I4 - cast pointer directly DWORD
        VT_BSTR - pointer to UTF-8 string
        ? - pointer to IP list

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS              status = ERROR_SUCCESS;
    BSTR                    bstrWQL = NULL;
    BSTR                    bstrQuery = NULL;
    BSTR                    bstrPropName = NULL;
    IWbemClassObject *      pObj = NULL;
    IEnumWbemClassObject *  pEnum = NULL;
    HRESULT                 hres = 0;
    ULONG                   returnedCount = 1;
    WCHAR                   wsz[ 1024 ];
    PWSTR                   pwszPropertyName = NULL;
    PWSTR                   pwszPropertyValue = NULL;

    DNSCMD_CHECK_WMI_ENABLED();

    //
    //  Get WMI object.
    //

    status = getEnumerator( pszZone, &pEnum );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }

    status = getNextObjectInEnum( pEnum, &pObj );
    if ( status != ERROR_SUCCESS || !pObj )
    {
        goto Done;
    }

    //
    //  Print the object's RELPATH (warm fuzzy).
    //

    VARIANT var;

    status = getRelpath( pObj, &var );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }
    printf( "%S\n\n", V_BSTR( &var) );

    //
    //  Set the property.
    //

    pwszPropertyName = ( PWSTR ) Dns_NameCopyAllocate(
                                        pszProperty,
                                        0,
                                        DnsCharSetUtf8,
                                        DnsCharSetUnicode );
    bstrPropName = SysAllocString( pwszPropertyName );

    VariantClear( &var );

    switch ( cimType )
    {
        case VT_BSTR:
            pwszPropertyValue = ( PWSTR ) Dns_NameCopyAllocate(
                                                ( PCHAR ) value,
                                                0,
                                                DnsCharSetUtf8,
                                                DnsCharSetUnicode );
            V_VT( &var ) = VT_BSTR;
            V_BSTR( &var ) = pwszPropertyValue;
            break;

        case PRIVATE_VT_IPARRAY:
        {
            SAFEARRAY * psa = createSafeArrayForIpArray(
                                    ( PIP_ARRAY ) value );
            V_VT( &var ) = VT_ARRAY | VT_BSTR;
            V_ARRAY( &var ) = psa;
            break;
        }

        default:        //  Assume this is DWORD property.
            V_VT( &var ) = VT_I4;
            V_I4( &var ) = ( DWORD ) ( DWORD_PTR ) value;
            break;
    }

    hres = pObj->Put( bstrPropName, 0, &var, 0 );
    VariantClear( &var );
    if ( !SUCCEEDED( hres ) )
    {
        printf( "WMI: unable to Put property error=0x%08X\n", hres );
        goto Done;
    }

    //
    //  Commit the change back to WMI.
    //

    hres = g_pIWbemServices->PutInstance( pObj, 0, NULL, NULL );
    if ( !SUCCEEDED( hres ) )
    {
        printf( "WMI: unable to commit property error=0x%08X\n", hres );
        goto Done;
    }
    
    //
    //  Cleanup and return.
    //

    Done:

    FREE_HEAP( pwszPropertyName );
    FREE_HEAP( pwszPropertyValue );
    SysFreeString( bstrPropName );

    if ( pObj )
    {
        pObj->Release();
    }

    if ( pEnum )
    {
        pEnum->Release();
    }
    
    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = HRES_TO_STATUS( hres );
    }

    return status;
}   //  DnscmdWmi_ResetDwordProperty



/*++

Routine Description:

    Reset server level forwarders.

Arguments:

    cForwarders -- number of forwarder IP addresses

    aipForwarders -- array of forwarder IP addresses

    dwForwardTimeout -- timeout

    fSlave -- slave flag

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
DNS_STATUS
DnscmdWmi_ProcessResetForwarders(
    IN      DWORD               cForwarders,
    IN      PIP_ADDRESS         aipForwarders,
    IN      DWORD               dwForwardTimeout,
    IN      DWORD               fSlave
    )
{
    DNS_STATUS              status = ERROR_SUCCESS;
    IEnumWbemClassObject *  pEnum = NULL;
    IWbemClassObject *      pObj = NULL;
    SAFEARRAY *             psa = NULL;
    HRESULT                 hres = 0;
    VARIANT                 var;

    VariantInit( &var );

    //
    //  Get WMI object for server.
    //

    status = getEnumerator( NULL, &pEnum );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }

    status = getNextObjectInEnum( pEnum, &pObj );
    if ( status != ERROR_SUCCESS || !pObj )
    {
        goto Done;
    }

    //
    //  Set up parameters.
    //

    psa = createSafeArrayForIpList( cForwarders, aipForwarders );
    if ( !psa )
    {
        status = ERROR_INVALID_PARAMETER;
        goto Done;
    }
    V_VT( &var ) = VT_ARRAY | VT_BSTR;
    V_ARRAY( &var ) = psa;
    hres = pObj->Put( MYTEXT( DNS_REGKEY_FORWARDERS ), 0, &var, 0 );
    VariantClear( &var );
    if ( !SUCCEEDED( hres ) )
    {
        printf(
            "WMI: unable to Put property %S error=0x%08X\n",
            MYTEXT( DNS_REGKEY_FORWARDERS ),
            hres );
        goto Done;
    }

    V_VT( &var ) = VT_I4;
    V_I4( &var ) = dwForwardTimeout;
    hres = pObj->Put( MYTEXT( DNS_REGKEY_FORWARD_TIMEOUT ), 0, &var, 0 );
    VariantClear( &var );
    if ( !SUCCEEDED( hres ) )
    {
        printf(
            "WMI: unable to Put property %S error=0x%08X\n",
            MYTEXT( DNS_REGKEY_FORWARD_TIMEOUT ),
            hres );
        goto Done;
    }

    V_VT( &var ) = VT_BOOL;
    V_BOOL( &var ) = ( VARIANT_BOOL ) fSlave;
    hres = pObj->Put( MYTEXT( DNS_REGKEY_SLAVE ), 0, &var, 0 );
    VariantClear( &var );
    if ( !SUCCEEDED( hres ) )
    {
        printf(
            "WMI: unable to Put property %S error=0x%08X\n",
            MYTEXT( DNS_REGKEY_SLAVE ),
            hres );
        goto Done;
    }

    //
    //  Commit the change back to WMI.
    //

    hres = g_pIWbemServices->PutInstance( pObj, 0, NULL, NULL );
    if ( !SUCCEEDED( hres ) )
    {
        printf( "WMI: unable to commit property error=0x%08X\n", hres );
        goto Done;
    }
    
    //
    //  Cleanup and return.
    //

    Done:

    if ( pObj )
    {
        pObj->Release();
    }
    if ( pEnum )
    {
        pEnum->Release();
    }
    VariantClear( &var );

    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = hres;
    }

    return status;
}



/*++

Routine Description:

    Send generic operation to server.

Arguments:

    pszZone -- zone name or NULL for server level operation

    pszOperation -- string identifying operation

    dwTypeId -- DNS RPC data type of data at pvData

    pvData -- DNS RPC data in DNS RPC union format

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
DNS_STATUS
DnscmdWmi_ProcessDnssrvOperation(
    IN      LPSTR               pszZoneName,
    IN      LPSTR               pszOperation,
    IN      DWORD               dwTypeId,
    IN      PVOID               pvData
    )
{
    DNS_STATUS              status = ERROR_SUCCESS;
    HRESULT                 hres = 0;
    IEnumWbemClassObject *  pEnum = NULL;
    IWbemClassObject *      pObj = NULL;
    SAFEARRAY *             psa = NULL;
    PWSTR                   pwszOperation = NULL;
    VARIANT                 var;

    VariantInit( &var );

    //
    //  Get WMI object.
    //

    status = getEnumerator( pszZoneName, &pEnum );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }

    status = getNextObjectInEnum( pEnum, &pObj );
    if ( status != ERROR_SUCCESS || !pObj )
    {
        goto Done;
    }

    //
    //  Process operation.
    //

    pwszOperation = ( PWSTR ) Dns_NameCopyAllocate(
                                    pszOperation,
                                    0,
                                    DnsCharSetUtf8,
                                    DnsCharSetUnicode );
    if ( !pwszOperation )
    {
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    if ( _stricmp( pszOperation, DNS_REGKEY_ZONE_MASTERS ) == 0 ||
        _stricmp( pszOperation, DNS_REGKEY_ZONE_LOCAL_MASTERS ) == 0 )
    {
        //
        //  For these properties do a simple Put operation by converting
        //  the DNS RPC data into VARIANT format and calling Put.
        //

        switch ( dwTypeId )
        {
            case DNSSRV_TYPEID_IPARRAY:
            {
                PIP_ARRAY       pip = ( PIP_ARRAY ) pvData;

                psa = createSafeArrayForIpList(
                            pip ? pip->AddrCount : 0,
                            pip ? pip->AddrArray : NULL );
                if ( !psa )
                {
                    status = ERROR_INVALID_PARAMETER;
                    goto Done;
                }
                V_VT( &var ) = VT_ARRAY | VT_BSTR;
                V_ARRAY( &var ) = psa;
                hres = pObj->Put( pwszOperation, 0, &var, 0 );
                break;
            }

            default:
                status = ERROR_NOT_SUPPORTED;
                break;
        }

        //
        //  Commit the Put operation.
        //

        if ( status == ERROR_SUCCESS && SUCCEEDED( hres ) )
        {
            hres = g_pIWbemServices->PutInstance( pObj, 0, NULL, NULL );
            if ( FAILED( hres ) )
            {
                printf(
                    "WMI: unable to commit property %s error=0x%08X\n",
                        pszOperation,
                        hres );
                goto Done;
            }
        }
    }
    else if ( _stricmp( pszOperation, DNSSRV_OP_ZONE_DELETE ) == 0 ||
        _stricmp( pszOperation, DNSSRV_OP_ZONE_DELETE_FROM_DS ) == 0 )
    {
        //
        //  Delete the zone.
        //

        VARIANT     relpath;

        status = getRelpath( pObj, &relpath );
        if ( status == ERROR_SUCCESS )
        {
            hres = g_pIWbemServices->DeleteInstance(
                                        V_BSTR( &relpath ),
                                        0,
                                        NULL,
                                        NULL );
        }
        VariantClear( &relpath );
    }
    else
    {
        status = ERROR_NOT_SUPPORTED;
    }

    //
    //  Cleanup and return.
    //

    Done:

    if ( psa )
    {
        SafeArrayDestroy( psa );
    }
    if ( pwszOperation )
    {
        FREE_HEAP( pwszOperation );
    }
    if ( pObj )
    {
        pObj->Release();
    }
    if ( pEnum )
    {
        pEnum->Release();
    }
    VariantClear( &var );

    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = hres;
    }

    return status;
}   //  DnscmdWmi_ProcessDnssrvOperation



DNS_STATUS
DnscmdWmi_ProcessRecordAdd(
    IN      LPSTR               pszZoneName,
    IN      LPSTR               pszNodeName,
    IN      PDNS_RPC_RECORD     prrRpc,
    IN      DWORD               Argc,
    IN      LPSTR *             Argv
    )
/*++

Routine Description:

    Add or delete a resource record. This function will take
    of the necessary some data from the RPC record and some from 
    the argument list.

Arguments:

    pszZoneName -- zone name

    pszNodeName -- name of property to set

    prrRpc -- RPC record

    Argc -- count of arguments used to create RPC record

    Argv -- arguments used to create RPC record

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS              status = ERROR_SUCCESS;
    PWSTR                   pwszZoneName = NULL;
    PWSTR                   pwszArgs = NULL;
    PWSTR                   pwszCurrent;
    IWbemClassObject *      pClassObj = NULL;
    IWbemClassObject *      pServerObj = NULL;
    IWbemClassObject *      pInSig = NULL;
    IWbemClassObject *      pOutSig = NULL;
    IWbemClassObject *      pInParams = NULL;
    IEnumWbemClassObject *  pEnum = NULL;
    HRESULT                 hres = 0;
    BSTR                    bstrClassName;
    BSTR                    bstrMethodName;
    VARIANT                 var;
    int                     len;
    int                     i;
    
    DNSCMD_CHECK_WMI_ENABLED();

    //
    //  Allocate and initialize various stuff.
    //

    VariantInit( &var );

    bstrClassName = SysAllocString( L"MicrosoftDNS_ResourceRecord" );
    bstrMethodName = SysAllocString( L"CreateInstanceFromTextRepresentation" );
    if ( !bstrClassName || !bstrMethodName )
    {
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    pwszZoneName = ( PWSTR ) Dns_NameCopyAllocate(
                                    pszZoneName,
                                    0,
                                    DnsCharSetUtf8,
                                    DnsCharSetUnicode );
    if ( !pwszZoneName )
    {
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    //
    //  Get WMI class object for Resource Record class.
    //

    hres = g_pIWbemServices->GetObject(
                bstrClassName,
                WBEM_FLAG_RETURN_WBEM_COMPLETE,
                NULL,
                &pClassObj,
                NULL );
    if ( FAILED( hres ) )
    {
        goto Done;
    }
    ASSERT( pClassObj );

    //
    //  Get WMI object for server.
    //

    status = getEnumerator( NULL, &pEnum );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }
    ASSERT( pEnum );

    status = getNextObjectInEnum( pEnum, &pServerObj );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }
    ASSERT( pServerObj );

    //
    //  Get WMI method signature for CreateInstanceFromTextRepresentation.
    //

    hres = pClassObj->GetMethod(
                bstrMethodName,
                0,
                &pInSig,
                &pOutSig );
    if ( FAILED( hres ) )
    {
        goto Done;
    }
    if ( pInSig == NULL )
    {
        status = ERROR_INVALID_PARAMETER;
        goto Done;
    }

    //
    //  Create an instance of the method input parameters.
    //

    hres = pInSig->SpawnInstance( 0, &pInParams );
    if ( FAILED( hres ) )
    {
        goto Done;
    }
    ASSERT( pInParams );

    //
    //  Collect the arguments into one big string
    //      ->  owner name
    //      ->  record class
    //      ->  record type
    //      ->  Argv array (space separated)
    //

    len = Argc * 2 +                //  for spaces
            30 +                    //  for record type
            strlen( pszNodeName );
    for ( i = 0; i < ( int ) Argc; ++i )
    {
        len += strlen( Argv[ i ] );
    }
    pwszCurrent = pwszArgs = new WCHAR [ len * sizeof( WCHAR ) ];
    if ( !pwszArgs )
    {
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    for ( i = -3; i < ( int ) Argc; ++i )
    {
        CHAR szBuff[ 40 ];
        PSTR psz;
        if ( i == -3 )
        {
            psz = pszNodeName;
        }
        else if ( i == -2 )
        {
            psz = "IN";
        }
        else if ( i == -1 )
        {
            psz = Dns_RecordStringForType( prrRpc->wType );
        }
        else
        {
            psz = Argv[ i ];
        }

        PWSTR pwsz = ( PWSTR ) Dns_NameCopyAllocate(
                                        psz,
                                        0,
                                        DnsCharSetUtf8,
                                        DnsCharSetUnicode );
        if ( !pwsz )
        {
            status = DNS_ERROR_NO_MEMORY;
            goto Done;
        }

        if ( pwszCurrent != pwszArgs )
        {
            wcscpy( pwszCurrent++, L" " );
        }
        wcscpy( pwszCurrent, pwsz );
        pwszCurrent += wcslen( pwsz );
    }

    //
    //  Set method input parameters.
    //

    getRelpath( pServerObj, &var );
    hres = pInParams->Put( L"DnsServerName", 0, &var, 0 );
    VariantClear( &var );

    V_VT( &var ) = VT_BSTR;
    V_BSTR( &var ) = SysAllocString( pwszZoneName );
    hres = pInParams->Put( L"ContainerName", 0, &var, 0 );
    VariantClear( &var );

    V_VT( &var ) = VT_BSTR;
    V_BSTR( &var ) = SysAllocString( pwszArgs );
    hres = pInParams->Put( L"TextRepresentation", 0, &var, 0 );
    VariantClear( &var );

    //
    //  Execute the method (finally!)
    //

    hres = g_pIWbemServices->ExecMethod(
                bstrClassName,
                bstrMethodName,
                0,                      //  flags
                NULL,                   //  context
                pInParams,              //  input params
                NULL,                   //  output params
                NULL );                 //  call result
    if ( FAILED( hres ) )
    {
        goto Done;
    }

    //
    //  Cleanup and return.
    //

    Done:

    VariantClear( &var );
    FREE_HEAP( pwszZoneName );
    delete [] pwszArgs;
    SysFreeString( bstrMethodName );
    SysFreeString( bstrClassName );
    wmiRelease( pEnum );
    wmiRelease( pClassObj );
    wmiRelease( pServerObj );
    wmiRelease( pInSig );
    wmiRelease( pOutSig );
    wmiRelease( pInParams );

    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = HRES_TO_STATUS( hres );
    }
    return status;
}   //  DnscmdWmi_ProcessRecordAdd



DNS_STATUS
DnscmdWmi_GetStatistics(
    IN      DWORD               dwStatId
    )
/*++

Routine Description:

    Retrieves and dumps all statistics matching the dwStatId mask.

Arguments:

    dwStatId -- statistic filter

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS                  status = ERROR_SUCCESS;
    WCHAR                       wsz[ 1024 ];
    BSTR                        bstrWQL = NULL;
    BSTR                        bstrQuery = NULL;
	HRESULT                     hres = 0;
    IEnumWbemClassObject *      pEnum = NULL;
    IWbemClassObject *          pObj = NULL;

    //
    //  Execute query for statistics.
    //

    wsprintfW(
        wsz, 
        L"select * from MicrosoftDNS_Statistic" );

    bstrWQL = SysAllocString( L"WQL" );
    bstrQuery = SysAllocString( wsz );
    if ( !bstrWQL || !bstrQuery )
    {
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    hres = g_pIWbemServices->ExecQuery(
                bstrWQL,
                bstrQuery,
                WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
                NULL,
                &pEnum );
    if ( FAILED( hres ) )
    {
        status = hres;
        goto Done;
    }

    //
    //  Dump query results.
    //

    VARIANT varLastColl;
    VariantInit( &varLastColl );

    while ( 1 )
    {
        status = getNextObjectInEnum( pEnum, &pObj, FALSE );
        if ( status != ERROR_SUCCESS || !pObj )
        {
            break;
        }

        CIMTYPE cimColl = 0;
        CIMTYPE cimName = 0;
        CIMTYPE cimValue = 0;
        CIMTYPE cimStringValue = 0;

        VARIANT varColl;
        VARIANT varName;
        VARIANT varValue;
        VARIANT varStringValue;
        VariantInit( &varColl );
        VariantInit( &varName );
        VariantInit( &varValue );
        VariantInit( &varStringValue );

        hres = pObj->Get( L"CollectionName", 0, &varColl, &cimColl, NULL );
        CHECK_HRES( hres, L"CollectionName" );
        hres = pObj->Get( L"Name", 0, &varName, &cimName, NULL );
        CHECK_HRES( hres, L"Name" );
        hres = pObj->Get( L"Value", 0, &varValue, &cimValue, NULL );
        CHECK_HRES( hres, L"Value" );
        hres = pObj->Get( L"StringValue", 0, &varStringValue, &cimValue, NULL );
        CHECK_HRES( hres, L"StringValue" );

        if ( V_VT( &varLastColl ) == VT_EMPTY ||
            wcscmp( V_BSTR( &varLastColl ), V_BSTR( &varColl ) ) != 0 )
        {
            //
            //  Entering new collection. NOTE: this assumes that stats
            //  are ordered by collection. Probably not a great assumption
            //  but it works for now.
            //

            printf( "\n%S:\n", V_BSTR( &varColl ) );
            VariantCopy( &varLastColl, &varColl );
        }

        printf(
            "  %-35S = ",
            V_BSTR( &varName ) );

        if ( V_VT( &varValue ) != VT_NULL )
        {
            printf( "%lu", V_UI4( &varValue ) );
            //  printf( "%lu  (0x%08X)", V_UI4( &varValue ), V_UI4( &varValue ) );
        }
        else if ( V_VT( &varStringValue ) == VT_BSTR )
        {
            printf( "%S", V_BSTR( &varStringValue ) );
        }
        else
        {
            printf( "invalid value!" );
        }
        printf( "\n" );

        VariantClear( &varColl );
        VariantClear( &varName );
        VariantClear( &varValue );
        VariantClear( &varStringValue );
    }
    VariantClear( &varLastColl );

    //
    //  Cleanup and return
    //

    Done:

    SysFreeString( bstrWQL );
    SysFreeString( bstrQuery );
    if ( pEnum )
    {
        pEnum->Release();
    }
    if ( pObj )
    {
        pObj->Release();
    }

    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = HRES_TO_STATUS( hres );
    }
    return status;
}



DNS_STATUS
DnscmdWmi_ProcessResetZoneSecondaries(
    IN      LPSTR           pszZoneName,
    IN      DWORD           fSecureSecondaries,
    IN      DWORD           cSecondaries,
    IN      PIP_ADDRESS     aipSecondaries,
    IN      DWORD           fNotifyLevel,
    IN      DWORD           cNotify,
    IN      PIP_ADDRESS     aipNotify
    )
/*++

Routine Description:

    Send "zone reset secondaries" command to the server to reset
    the zone secondary and notify list parameters.

Arguments:

    pszZoneName -- zone name

    fSecureSecondaries -- secondary directive (ZONE_SECSECURE_XXX)

    cSecondaries -- count of IP addresses in aipSecondaries

    aipSecondaries -- secondary server IP address array

    fNotifyLevel -- notify directive (ZONE_NOTIFY_XXX)

    cNotify -- count of IP addresses in aipNotify

    aipNotify -- notify server IP address array

Return Value:

    ERROR_SUCCESS if successful or error code on failure.

--*/
{
    DNS_STATUS              status = ERROR_SUCCESS;
    BSTR                    bstrClassName;
    BSTR                    bstrMethodName;
    PWSTR                   pwszZoneName = NULL;
    IWbemClassObject *      pObj = NULL;
    IWbemClassObject *      pClassObj = NULL;
    IWbemClassObject *      pInSig = NULL;
    IWbemClassObject *      pOutSig = NULL;
    IWbemClassObject *      pInParams = NULL;
    IEnumWbemClassObject *  pEnum = NULL;
    VARIANT                 var;
    HRESULT                 hres;
    SAFEARRAY *             psa;

#if 0
    IWbemClassObject *      pServerObj = NULL;
    PWSTR                   pwszArgs = NULL;
    PWSTR                   pwszCurrent;
    int                     len;
    int                     i;
#endif
    
    DNSCMD_CHECK_WMI_ENABLED();

    //
    //  Allocate and initialize various stuff.
    //

    VariantInit( &var );

    bstrClassName = SysAllocString( L"MicrosoftDNS_Zone" );
    bstrMethodName = SysAllocString( L"ResetSecondaries" );
    if ( !bstrClassName || !bstrMethodName )
    {
        status = DNS_ERROR_NO_MEMORY;
        goto Done;
    }

    //
    //  Get WMI class object for the Zone class.
    //

    hres = g_pIWbemServices->GetObject(
                bstrClassName,
                WBEM_FLAG_RETURN_WBEM_COMPLETE,
                NULL,
                &pClassObj,
                NULL );
    if ( FAILED( hres ) )
    {
        goto Done;
    }
    ASSERT( pClassObj );

    //
    //  Get WMI object for specified zone.
    //

    status = getEnumerator( pszZoneName, &pEnum );
    if ( status != ERROR_SUCCESS )
    {
        goto Done;
    }

    status = getNextObjectInEnum( pEnum, &pObj );
    if ( status != ERROR_SUCCESS || !pObj )
    {
        goto Done;
    }

    //
    //  Get WMI method signature for ResetSecondaries.
    //

    hres = pClassObj->GetMethod(
                bstrMethodName,
                0,
                &pInSig,
                &pOutSig );
    if ( FAILED( hres ) )
    {
        goto Done;
    }
    if ( pInSig == NULL )
    {
        status = ERROR_INVALID_PARAMETER;
        goto Done;
    }

    //
    //  Create an instance of the method input parameters.
    //

    hres = pInSig->SpawnInstance( 0, &pInParams );
    if ( FAILED( hres ) )
    {
        goto Done;
    }
    ASSERT( pInParams );

    //
    //  Set method input parameters.
    //

    printWmiObjectProperties( pInParams );

{
    BSTR b = NULL;
    pInParams->GetObjectText( 0, &b );
    printf( "\nObjectText:\n%S\n", b );
}

    VariantClear( &var );

{
    BSTR bstr = SysAllocString( L"SecureSecondaries" );
    V_VT( &var ) = VT_UI4;
    V_UI4( &var ) = fSecureSecondaries;
    hres = pInParams->Put( bstr, 0, &var, 0 );
    VariantClear( &var );
}

#if 0
    V_VT( &var ) = VT_UI4;
    V_UI4( &var ) = fSecureSecondaries;
    hres = pInParams->Put( L"SecureSecondaries", 0, &var, 0 );
    VariantClear( &var );
#endif

    V_VT( &var ) = VT_UI4;
    V_UI4( &var ) = fNotifyLevel;
    hres = pInParams->Put( L"Notify", 0, &var, 0 );
    VariantClear( &var );

    psa = createSafeArrayForIpList( cSecondaries, aipSecondaries );
    V_VT( &var ) = VT_ARRAY | VT_BSTR;
    V_ARRAY( &var ) = psa;
    hres = pInParams->Put( L"SecondaryServers", 0, &var, 0 );
    VariantClear( &var );

    psa = createSafeArrayForIpList( cNotify, aipNotify );
    V_VT( &var ) = VT_ARRAY | VT_BSTR;
    V_ARRAY( &var ) = psa;
    hres = pInParams->Put( L"NotifyServers", 0, &var, 0 );
    VariantClear( &var );

    //
    //  Execute the method.
    //

    hres = g_pIWbemServices->ExecMethod(
                bstrClassName,
                bstrMethodName,
                0,                      //  flags
                NULL,                   //  context
                pInParams,              //  input params
                NULL,                   //  output params
                NULL );                 //  call result
    if ( FAILED( hres ) )
    {
        goto Done;
    }

    //
    //  Cleanup and return.
    //

    Done:

    VariantClear( &var );
    FREE_HEAP( pwszZoneName );
    SysFreeString( bstrMethodName );
    SysFreeString( bstrClassName );
    wmiRelease( pEnum );
    wmiRelease( pClassObj );
    wmiRelease( pInSig );
    wmiRelease( pOutSig );
    wmiRelease( pInParams );

    if ( status == ERROR_SUCCESS && FAILED( hres ) )
    {
        status = HRES_TO_STATUS( hres );
    }
    return status;
}   //  DnscmdWmi_ProcessResetZoneSecondaries


//
//  End dnsc_wmi.c
//