564 lines
16 KiB
C++
564 lines
16 KiB
C++
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Windows 2000 Active Directory Service domain trust verification WMI provider
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1992 - 2000
|
||
|
//
|
||
|
// File: domain.cpp
|
||
|
//
|
||
|
// Contents: domain class implementation
|
||
|
//
|
||
|
// Classes: CDomainInfo
|
||
|
//
|
||
|
// History: 27-Mar-00 EricB created
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
#include <stdafx.h>
|
||
|
|
||
|
PCWSTR CSTR_PROP_LOCAL_DNS_NAME = L"DNSname"; // String
|
||
|
PCWSTR CSTR_PROP_LOCAL_FLAT_NAME = L"FlatName"; // String
|
||
|
PCWSTR CSTR_PROP_LOCAL_SID = L"SID"; // String
|
||
|
PCWSTR CSTR_PROP_LOCAL_TREE_NAME = L"TreeName"; // String
|
||
|
PCWSTR CSTR_PROP_LOCAL_DC_NAME = L"DCname"; // String
|
||
|
// TODO: string property listing the FSMOs owned by this DC?
|
||
|
|
||
|
//Implementaion of CDomainInfo class
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: CDomainInfo
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CDomainInfo::CDomainInfo()
|
||
|
{
|
||
|
TRACE(L"CDomainInfo::CDomainInfo\n");
|
||
|
|
||
|
m_liLastEnumed.QuadPart = 0;
|
||
|
}
|
||
|
|
||
|
CDomainInfo::~CDomainInfo()
|
||
|
{
|
||
|
TRACE(L"CDomainInfo::~CDomainInfo\n");
|
||
|
|
||
|
Reset();
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Method: CDomainInfo::Init
|
||
|
//
|
||
|
// Synopsis: Initializes the CDomainInfo object.
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
HRESULT
|
||
|
CDomainInfo::Init(IWbemClassObject * pClassDef)
|
||
|
{
|
||
|
TRACE(L"CDomainInfo::Init\n");
|
||
|
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
OBJECT_ATTRIBUTES objectAttributes;
|
||
|
CSmartPolicyHandle chPolicy;
|
||
|
|
||
|
m_sipClassDefLocalDomain = pClassDef;
|
||
|
|
||
|
InitializeObjectAttributes(&objectAttributes, NULL, 0L, NULL, NULL);
|
||
|
|
||
|
// Get Local policy
|
||
|
Status = LsaOpenPolicy(NULL, // Local server
|
||
|
&objectAttributes,
|
||
|
MAXIMUM_ALLOWED, // Needed for Rediscover
|
||
|
&chPolicy);
|
||
|
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
TRACE(L"LsaOpenPolicy failed with error %d\n", Status);
|
||
|
return HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
|
||
|
}
|
||
|
|
||
|
PPOLICY_DNS_DOMAIN_INFO pDnsDomainInfo;
|
||
|
|
||
|
Status = LsaQueryInformationPolicy(chPolicy,
|
||
|
PolicyDnsDomainInformation,
|
||
|
(PVOID *)&pDnsDomainInfo);
|
||
|
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
TRACE(L"LsaQueryInformationPolicy failed with error %d\n", Status);
|
||
|
return HRESULT_FROM_WIN32(LsaNtStatusToWinError(Status));
|
||
|
}
|
||
|
|
||
|
m_strDomainFlatName = pDnsDomainInfo->Name.Buffer;
|
||
|
m_strDomainDnsName = pDnsDomainInfo->DnsDomainName.Buffer;
|
||
|
m_strForestName = pDnsDomainInfo->DnsForestName.Buffer;
|
||
|
|
||
|
if (!SetSid(pDnsDomainInfo->Sid))
|
||
|
{
|
||
|
ASSERT(false);
|
||
|
LsaFreeMemory(pDnsDomainInfo);
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
LsaFreeMemory(pDnsDomainInfo);
|
||
|
|
||
|
DWORD dwBufSize = MAX_COMPUTERNAME_LENGTH + 1;
|
||
|
|
||
|
if (!GetComputerName(m_strDcName.GetBuffer(dwBufSize), &dwBufSize))
|
||
|
{
|
||
|
DWORD dwErr = GetLastError();
|
||
|
TRACE(L"GetComputerName failed with error %d\n", dwErr);
|
||
|
return HRESULT_FROM_WIN32(dwErr);
|
||
|
}
|
||
|
|
||
|
m_strDcName.ReleaseBuffer();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Method: CDomainInfo::Reset
|
||
|
//
|
||
|
// Synopsis: Free the contents of the trust array and re-initialize it.
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void
|
||
|
CDomainInfo::Reset(void)
|
||
|
{
|
||
|
TRACE(L"CDomainInfo::Reset\n");
|
||
|
|
||
|
CTrustInfo * pTrustInfo = NULL;
|
||
|
|
||
|
if (IsEnumerated())
|
||
|
{
|
||
|
// Empty cache
|
||
|
for (UINT i = 0; i < m_vectTrustInfo.size(); ++i)
|
||
|
{
|
||
|
pTrustInfo = m_vectTrustInfo[i];
|
||
|
if (pTrustInfo)
|
||
|
delete pTrustInfo;
|
||
|
}
|
||
|
m_vectTrustInfo.clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Method: CDomainInfo::SetSid
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
BOOL
|
||
|
CDomainInfo::SetSid(PSID pSid)
|
||
|
{
|
||
|
if (!pSid)
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#if !defined(NT4_BUILD)
|
||
|
PWSTR buffer;
|
||
|
|
||
|
BOOL fRet = ConvertSidToStringSid(pSid, &buffer);
|
||
|
|
||
|
if (fRet)
|
||
|
{
|
||
|
m_strSid = buffer;
|
||
|
LocalFree(buffer);
|
||
|
}
|
||
|
|
||
|
return fRet;
|
||
|
#else
|
||
|
// TODO: Code for NT4 ??
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Method: CDomainInfo::EnumerateTrusts
|
||
|
//
|
||
|
// Synopsis: List the trusts for this domain.
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
#if !defined(NT4_BUILD)
|
||
|
HRESULT
|
||
|
CDomainInfo::EnumerateTrusts(void)
|
||
|
{
|
||
|
TRACE(L"CDomainInfo::EnumerateTrusts\n");
|
||
|
|
||
|
DWORD dwRet = ERROR_SUCCESS;
|
||
|
CTrustInfo * pTrustInfo = NULL;
|
||
|
PDS_DOMAIN_TRUSTS rgTrusts = NULL;
|
||
|
ULONG nTrustCount = 0;
|
||
|
|
||
|
Reset();
|
||
|
|
||
|
dwRet = DsEnumerateDomainTrusts(NULL,
|
||
|
DS_DOMAIN_DIRECT_OUTBOUND |
|
||
|
DS_DOMAIN_DIRECT_INBOUND,
|
||
|
&rgTrusts,
|
||
|
&nTrustCount);
|
||
|
|
||
|
if (ERROR_SUCCESS != dwRet)
|
||
|
{
|
||
|
TRACE(L"DsEnumerateDomainTrusts failed with error %d\n", dwRet);
|
||
|
return HRESULT_FROM_WIN32(dwRet);
|
||
|
}
|
||
|
|
||
|
for (ULONG i = 0; i < nTrustCount; i++)
|
||
|
{
|
||
|
pTrustInfo = new CTrustInfo();
|
||
|
BREAK_ON_NULL(pTrustInfo);
|
||
|
if (rgTrusts[i].DnsDomainName)
|
||
|
{
|
||
|
// Downlevel domains don't have a DNS name.
|
||
|
//
|
||
|
pTrustInfo->SetTrustedDomain(rgTrusts[i].DnsDomainName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// So use the flat name instead.
|
||
|
//
|
||
|
pTrustInfo->SetTrustedDomain(rgTrusts[i].NetbiosDomainName);
|
||
|
}
|
||
|
pTrustInfo->SetFlatName(rgTrusts[i].NetbiosDomainName);
|
||
|
BREAK_ON_NULL(pTrustInfo->SetSid(rgTrusts[i].DomainSid));
|
||
|
pTrustInfo->SetTrustType(rgTrusts[i].TrustType);
|
||
|
pTrustInfo->SetTrustDirectionFromFlags(rgTrusts[i].Flags);
|
||
|
pTrustInfo->SetTrustAttributes(rgTrusts[i].TrustAttributes);
|
||
|
pTrustInfo->SetFlags(rgTrusts[i].Flags);
|
||
|
|
||
|
m_vectTrustInfo.push_back(pTrustInfo);
|
||
|
|
||
|
pTrustInfo = NULL;
|
||
|
}
|
||
|
|
||
|
if (rgTrusts)
|
||
|
{
|
||
|
NetApiBufferFree(rgTrusts);
|
||
|
}
|
||
|
|
||
|
if (ERROR_SUCCESS == dwRet)
|
||
|
{
|
||
|
SYSTEMTIME st;
|
||
|
|
||
|
GetSystemTime(&st);
|
||
|
SystemTimeToFileTime(&st, (LPFILETIME)&m_liLastEnumed);
|
||
|
}
|
||
|
|
||
|
return HRESULT_FROM_WIN32(dwRet);
|
||
|
}
|
||
|
|
||
|
#else // NT4_BUILD
|
||
|
|
||
|
HRESULT
|
||
|
CDomainInfo::EnumerateTrusts(void)
|
||
|
{
|
||
|
TRACE(L"CDomainInfo::EnumerateTrusts\n");
|
||
|
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
DWORD dwErr = ERROR_SUCCESS;
|
||
|
ULONG i = 0;
|
||
|
CTrustInfo * pTrustInfo = NULL;
|
||
|
|
||
|
Reset();
|
||
|
|
||
|
LSA_ENUMERATION_HANDLE hEnumContext = NULL;
|
||
|
ULONG nTrustCount = 0;
|
||
|
ULONG nTotalCount = 0;
|
||
|
ULONG j = 0;
|
||
|
PLSA_TRUST_INFORMATION pTrustDomainInfo = NULL;
|
||
|
DWORD hResumeHandle = 0;
|
||
|
LPUSER_INFO_0 pUserList = NULL;
|
||
|
CTrustInfo * pTempTrustInfo = NULL;
|
||
|
LPWSTR Lop = NULL;
|
||
|
CSmartPolicyHandle chPolicy;
|
||
|
OBJECT_ATTRIBUTES objectAttributes;
|
||
|
|
||
|
InitializeObjectAttributes(&objectAttributes, NULL, 0L, NULL, NULL);
|
||
|
|
||
|
//
|
||
|
// We'll have to do this the old fashioned way. That means that we'll enumerate all of
|
||
|
// the trust directly, save them off in a list, and then go through and enumerate all
|
||
|
// of the interdomain trust accounts and merge those into the list.
|
||
|
//
|
||
|
do
|
||
|
{
|
||
|
Status = LsaOpenPolicy(NULL, // Local server
|
||
|
&objectAttributes,
|
||
|
MAXIMUM_ALLOWED, // Needed for Rediscover
|
||
|
&chPolicy);
|
||
|
|
||
|
Status = LsaEnumerateTrustedDomains(chPolicy,
|
||
|
&hEnumContext,
|
||
|
(void**)&pTrustDomainInfo,
|
||
|
ULONG_MAX,
|
||
|
&nTrustCount );
|
||
|
|
||
|
if (NT_SUCCESS(Status) || Status == STATUS_MORE_ENTRIES)
|
||
|
{
|
||
|
dwErr = ERROR_SUCCESS;
|
||
|
for ( i = 0; i < nTrustCount; i++ )
|
||
|
{
|
||
|
pTrustInfo = new CTrustInfo();
|
||
|
CHECK_NULL( pTrustInfo, CLEAN_RETURN );
|
||
|
pTrustInfo->SetTrustedDomain( pTrustDomainInfo[i].Name.Buffer );
|
||
|
pTrustInfo->SetFlatName( pTrustDomainInfo[i].Name.Buffer );
|
||
|
pTrustInfo->SetSid( pTrustDomainInfo[i].Sid );
|
||
|
pTrustInfo->SetTrustType( TRUST_TYPE_DOWNLEVEL );
|
||
|
pTrustInfo->SetTrustDirection( TRUST_DIRECTION_OUTBOUND );
|
||
|
m_vectTrustInfo.push_back( pTrustInfo );
|
||
|
pTrustInfo = NULL;
|
||
|
}
|
||
|
LsaFreeMemory( pTrustDomainInfo );
|
||
|
pTrustDomainInfo = NULL;
|
||
|
}
|
||
|
else
|
||
|
dwErr = LsaNtStatusToWinError(Status);
|
||
|
} while (Status == STATUS_MORE_ENTRIES);
|
||
|
|
||
|
if( Status == STATUS_NO_MORE_ENTRIES )
|
||
|
dwErr = ERROR_SUCCESS;
|
||
|
//
|
||
|
// Now, let's add in the user accounts
|
||
|
//
|
||
|
if ( dwErr == ERROR_SUCCESS )
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
nTrustCount = 0;
|
||
|
nTotalCount = 0;
|
||
|
|
||
|
dwErr = NetUserEnum(NULL,
|
||
|
0,
|
||
|
FILTER_INTERDOMAIN_TRUST_ACCOUNT,
|
||
|
(LPBYTE *)&pUserList,
|
||
|
MAX_PREFERRED_LENGTH,
|
||
|
&nTrustCount,
|
||
|
&nTotalCount,
|
||
|
&hResumeHandle);
|
||
|
|
||
|
if ( dwErr == ERROR_SUCCESS || dwErr == ERROR_MORE_DATA )
|
||
|
{
|
||
|
dwErr = ERROR_SUCCESS;
|
||
|
for ( i = 0; i < nTrustCount; i++ )
|
||
|
{
|
||
|
Lop = wcsrchr( pUserList[ i ].usri0_name, L'$' );
|
||
|
if ( Lop )
|
||
|
{
|
||
|
*Lop = UNICODE_NULL;
|
||
|
}
|
||
|
|
||
|
for ( j = 0; j < m_vectTrustInfo.size(); j++ )
|
||
|
{
|
||
|
pTempTrustInfo = m_vectTrustInfo[j];
|
||
|
if ( _wcsicmp( pUserList[ i ].usri0_name, pTempTrustInfo->GetTrustedDomain() ) == 0 )
|
||
|
{
|
||
|
pTempTrustInfo->SetTrustDirection( TRUST_DIRECTION_INBOUND | TRUST_DIRECTION_OUTBOUND );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If it wasn't found, add it...
|
||
|
if ( j == m_vectTrustInfo.size() )
|
||
|
{
|
||
|
pTrustInfo = new CTrustInfo();
|
||
|
CHECK_NULL( pTrustInfo, CLEAN_RETURN );
|
||
|
pTrustInfo->SetTrustedDomain( pUserList[ i ].usri0_name );
|
||
|
pTrustInfo->SetFlatName( pUserList[ i ].usri0_name );
|
||
|
pTrustInfo->SetTrustType( TRUST_TYPE_DOWNLEVEL );
|
||
|
pTrustInfo->SetTrustDirection( TRUST_DIRECTION_INBOUND );
|
||
|
|
||
|
m_vectTrustInfo.push_back( pTrustInfo );
|
||
|
|
||
|
pTrustInfo = NULL;
|
||
|
}
|
||
|
|
||
|
if ( Lop )
|
||
|
{
|
||
|
*Lop = L'$';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NetApiBufferFree( pUserList );
|
||
|
pUserList = NULL;
|
||
|
}
|
||
|
|
||
|
} while ( dwErr == ERROR_MORE_DATA );
|
||
|
}
|
||
|
|
||
|
CLEAN_RETURN:
|
||
|
if( pUserList )
|
||
|
NetApiBufferFree( pUserList );
|
||
|
if( pTrustDomainInfo )
|
||
|
LsaFreeMemory( pTrustDomainInfo );
|
||
|
|
||
|
if (ERROR_SUCCESS == dwErr)
|
||
|
{
|
||
|
SYSTEMTIME st;
|
||
|
|
||
|
GetSystemTime(&st);
|
||
|
SystemTimeToFileTime(&st, (LPFILETIME)&m_liLastEnumed);
|
||
|
}
|
||
|
|
||
|
return HRESULT_FROM_WIN32(dwErr);
|
||
|
}
|
||
|
#endif // NT4_BUILD
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Method: CDomainInfo::FindTrust
|
||
|
//
|
||
|
// Synopsis: Find a trust by trusted Domain Name
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTrustInfo *
|
||
|
CDomainInfo::FindTrust(PCWSTR pwzTrust)
|
||
|
{
|
||
|
TRACE(L"CDomainInfo::FindTrust\n");
|
||
|
TRACE(L"\nlooking for domain %s\n", pwzTrust);
|
||
|
ASSERT(IsEnumerated());
|
||
|
|
||
|
ULONG i = 0;
|
||
|
for( i = 0; i < m_vectTrustInfo.size(); ++i )
|
||
|
{
|
||
|
int nStrComp = CompareString(LOCALE_SYSTEM_DEFAULT,
|
||
|
NORM_IGNORECASE,
|
||
|
(m_vectTrustInfo[i])->GetTrustedDomain(), -1,
|
||
|
pwzTrust, -1 );
|
||
|
ASSERT( nStrComp );
|
||
|
|
||
|
if( CSTR_EQUAL == nStrComp )
|
||
|
{
|
||
|
TRACE(L"Trust found!\n");
|
||
|
return m_vectTrustInfo[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL; // Not Found
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Method: CDomainInfo::GetTrustByIndex
|
||
|
//
|
||
|
// Synopsis: Get trust info by Index
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTrustInfo *
|
||
|
CDomainInfo::GetTrustByIndex(size_t index)
|
||
|
{
|
||
|
ASSERT(IsEnumerated());
|
||
|
|
||
|
if (index < Size())
|
||
|
{
|
||
|
return m_vectTrustInfo[index];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Method: CDomainInfo::IsTrustListStale
|
||
|
//
|
||
|
// Synopsis: Checks to see if the last emumeration time is older than the
|
||
|
// passed in criteria.
|
||
|
//
|
||
|
// Returns: TRUE if older.
|
||
|
//
|
||
|
// Notes: If the trusts haven't been enumerated (m_liLastEnumed == 0),
|
||
|
// then the enumeration is defined to be stale.
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
BOOL
|
||
|
CDomainInfo::IsTrustListStale(LARGE_INTEGER liMaxAge)
|
||
|
{
|
||
|
TRACE(L"CDomainInfo::IsTrustListStale(0x%08x), MaxAge = %d\n",
|
||
|
this, liMaxAge.QuadPart / TRUSTMON_FILETIMES_PER_MINUTE);
|
||
|
BOOL fStale = FALSE;
|
||
|
LARGE_INTEGER liCurrentTime;
|
||
|
SYSTEMTIME st;
|
||
|
|
||
|
GetSystemTime(&st);
|
||
|
SystemTimeToFileTime(&st, (LPFILETIME)&liCurrentTime);
|
||
|
|
||
|
fStale = (m_liLastEnumed.QuadPart + liMaxAge.QuadPart) < liCurrentTime.QuadPart;
|
||
|
|
||
|
return fStale;
|
||
|
}
|
||
|
|
||
|
//+----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Method: CDomainInfo::CreateAndSendInst
|
||
|
//
|
||
|
// Synopsis: Returns a copy of the current instance back to WMI
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
HRESULT
|
||
|
CDomainInfo::CreateAndSendInst(IWbemObjectSink * pResponseHandler)
|
||
|
{
|
||
|
TRACE(L"CDomainInfo::CreateAndSendInst\n");
|
||
|
HRESULT hr = WBEM_S_NO_ERROR;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
CComPtr<IWbemClassObject> ipNewInst;
|
||
|
CComVariant var;
|
||
|
|
||
|
//
|
||
|
// Create a new instance of the WMI class object
|
||
|
//
|
||
|
hr = m_sipClassDefLocalDomain->SpawnInstance(0, &ipNewInst);
|
||
|
|
||
|
BREAK_ON_FAIL;
|
||
|
|
||
|
// Set the DNS property value
|
||
|
var = GetDnsName();
|
||
|
hr = ipNewInst->Put(CSTR_PROP_LOCAL_DNS_NAME, 0, &var, 0);
|
||
|
TRACE(L"\tCreating instance %s\n", var.bstrVal);
|
||
|
BREAK_ON_FAIL;
|
||
|
|
||
|
// Set the flat name property value
|
||
|
var = GetFlatName();
|
||
|
hr = ipNewInst->Put(CSTR_PROP_LOCAL_FLAT_NAME, 0, &var, 0);
|
||
|
TRACE(L"\twith flat name %s\n", var.bstrVal);
|
||
|
BREAK_ON_FAIL;
|
||
|
|
||
|
// Set the SID property value
|
||
|
var = GetSid();
|
||
|
hr = ipNewInst->Put(CSTR_PROP_LOCAL_SID, 0, &var, 0);
|
||
|
TRACE(L"\twith SID %s\n", var.bstrVal);
|
||
|
BREAK_ON_FAIL;
|
||
|
|
||
|
// Set the forest name property value
|
||
|
var = GetForestName();
|
||
|
hr = ipNewInst->Put(CSTR_PROP_LOCAL_TREE_NAME, 0, &var, 0);
|
||
|
TRACE(L"\twith forest name %s\n", var.bstrVal);
|
||
|
BREAK_ON_FAIL;
|
||
|
|
||
|
// Set the DC name property value
|
||
|
var = GetDcName();
|
||
|
hr = ipNewInst->Put(CSTR_PROP_LOCAL_DC_NAME, 0, &var, 0);
|
||
|
TRACE(L"\ton DC %s\n", var.bstrVal);
|
||
|
BREAK_ON_FAIL;
|
||
|
|
||
|
//
|
||
|
// Send the object to the caller
|
||
|
//
|
||
|
// [In] param, no need to addref.
|
||
|
|
||
|
IWbemClassObject * pNewInstance = ipNewInst;
|
||
|
|
||
|
hr = pResponseHandler->Indicate(1, &pNewInstance);
|
||
|
|
||
|
BREAK_ON_FAIL;
|
||
|
|
||
|
} while(FALSE);
|
||
|
|
||
|
return hr;
|
||
|
}
|