|
|
///////////////////////////////////////////////////////////////////////////////
//This file has the implementation for the WMI Provider for WHQL.
//Uses MFC
///////////////////////////////////////////////////////////////////////////////
// WhqlObj.cpp : Implementation of CWhqlObj
#include "stdafx.h"
#include "WhqlProv.h"
#include "WhqlObj.h"
#include "setupapi.h"
/////////////////////////////////////////////////////////////////////////////
// CWhqlObj
//Fn which actually checks the signature when passed an Inf File.
extern BOOL IsInfSigned(LPTSTR FullInfPath , IWbemClassObject *pInstance = NULL); inline VOID AddSlashes(LPTSTR str); inline SCODE PutPropertyValue( IWbemClassObject* pInstance , LPCTSTR lpszProperty , LPCTSTR lpszValue );
STDMETHODIMP CWhqlObj::Initialize(LPWSTR pszUser, LONG lFlags, LPWSTR pszNamespace, LPWSTR pszLocale, IWbemServices *pNamespace, IWbemContext *pCtx, IWbemProviderInitSink *pInitSink) { if (pNamespace) pNamespace->AddRef();
//Standard Var Inits.
m_pNamespace = pNamespace; m_csPathStr.Empty(); m_csAntecedent.Empty();
//Let CIMOM know you are initialized
pInitSink->SetStatus(WBEM_S_INITIALIZED, 0);
DEBUGTRACE(L"Provider Initialized\n"); return WBEM_S_NO_ERROR; }
SCODE CWhqlObj::CreateInstanceEnumAsync(BSTR RefStr, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pHandler) { DEBUGTRACE(L"Provider called for class = %s\n",RefStr);
HRESULT hr = S_OK; IWbemClassObject *pClass = NULL; IWbemClassObject **ppInstances = NULL; VOID **ppData = NULL; LONG cInstances, lIndex;
cInstances = lIndex = 0L; m_ptrArr.RemoveAll(); m_csPathStr.Empty(); m_csAntecedent.Empty();
// Do a check of arguments and make sure we have pointer to Namespace
if(pHandler == NULL || m_pNamespace == NULL) return WBEM_E_INVALID_PARAMETER;
// Get a class object from CIMOM
hr = m_pNamespace->GetObject(RefStr, 0, pCtx, &pClass, NULL); if ( FAILED(hr) ) return WBEM_E_FAILED;
m_pClass = pClass;
VARIANT var; VariantInit(&var); Classes_Provided eClasses;
if(lstrcmpi(RefStr , L"Win32_PnPSignedDriver") == 0) { eClasses = Class_Win32_PnPSignedDriver; } else if(lstrcmpi(RefStr , L"Win32_PnPSignedDriverCIMDataFile") == 0) { eClasses = Class_Win32_PnPSignedDriverCIMDataFile; } else if(lstrcmpi(RefStr , L"Win32_PnPSignedDriverWin32PnPEntity") == 0) { eClasses = Class_Win32_PnPSignedDriverWin32PnPEntity; }
hr = PutData( pCtx , eClasses); if ( FAILED(hr) ) return WBEM_E_FAILED; VariantClear(&var); hr = pClass->Release();
cInstances = (LONG)m_ptrArr.GetSize(); ppInstances = (IWbemClassObject**)new LPVOID[cInstances];
for(int nIndex = 0 ;nIndex<cInstances ;nIndex++) ppInstances[nIndex] = (IWbemClassObject*)m_ptrArr[nIndex];
if (SUCCEEDED(hr)) { // Send the instances to the caller
hr = pHandler->Indicate(cInstances, ppInstances);
for (lIndex = 0; lIndex < cInstances; lIndex++) ppInstances[lIndex]->Release(); }
// Cleanup
if (ppInstances) { delete []ppInstances; ppInstances = NULL; } m_ptrArr.RemoveAll(); m_csPathStr.Empty(); m_csAntecedent.Empty(); m_csCurPnPID.Empty();
m_pClass = NULL; // End Cleanup
// Set status
hr = pHandler->SetStatus(0, hr, NULL, NULL); return hr; }
/////////////////////////////////////
//Obsolete! For testing purposes only
//
int CWhqlObj::GetInfFileCount() { TCHAR lpszInfPath[MAX_PATH]; int iInstances = 0;
GetWindowsDirectory(lpszInfPath , sizeof(lpszInfPath)); lstrcat(lpszInfPath , L"\\inf\\*.inf");
WIN32_FIND_DATA findData; HANDLE hFile = FindFirstFile(lpszInfPath , &findData); BOOL bRet = TRUE;
while(hFile != INVALID_HANDLE_VALUE && bRet) { iInstances++; bRet = FindNextFile(hFile , &findData); }
return iInstances; }
////////////////////////////////////////////////////
//Fills up pInstance fields passed an Inf file.
//
SCODE CWhqlObj::GetInfData(LPTSTR lpszInfFile , LPTSTR lpszInfSection , LPTSTR szDeviceID , IWbemContext *pCtx , IWbemClassObject *pInstance) { TCHAR lpReturnedString[2048]; TCHAR lpSectionList[2048]; CStringArray csarrCopyFileSection; CStringArray csarrCopyFileBinaries; HRESULT hr = S_FALSE; HINF InfHandle = SetupOpenInfFile(lpszInfFile, NULL,INF_STYLE_WIN4,NULL); INFCONTEXT Context; BOOL bRet ;
if(INVALID_HANDLE_VALUE == InfHandle) return S_FALSE;
VARIANT v; VariantInit(&v);
//Find CopyFiles section.
LPTSTR lpSection = NULL;
bRet = SetupFindFirstLine(InfHandle,lpszInfSection,L"CopyFiles",&Context); if(!bRet) { SetupCloseInfFile( InfHandle ); return S_FALSE; }
bRet = SetupGetLineText(&Context,InfHandle,NULL,NULL,lpReturnedString, sizeof(lpReturnedString),NULL); if(!bRet) { SetupCloseInfFile( InfHandle ); return S_FALSE; }
DEBUGTRACE(L"CopyFiles string for file %s is %s\n",lpszInfFile,lpReturnedString);
LPTSTR seps = L","; LPTSTR token ;
token = wcstok( lpReturnedString, seps ); csarrCopyFileSection.Add(token);//Add token to csarrCopyFileSection
while (token) { lpSection = token; token = wcstok( NULL, seps );
if(token) csarrCopyFileSection.Add(token);//Add token to csarrCopyFileSection
}
for(int nIndex = 0;nIndex < csarrCopyFileSection.GetSize() ; nIndex++) { //Sometimes CopyFile Might have an entry like @mydriver.sys. Take care of that.
if((csarrCopyFileSection[nIndex])[0] == '@') { csarrCopyFileBinaries.Add(LPCTSTR(csarrCopyFileSection[nIndex]) + 1); DEBUGTRACE(L"Adding binary file %s\n" , LPCTSTR(csarrCopyFileSection[nIndex]) + 1); continue; } bRet = SetupFindFirstLine(InfHandle,csarrCopyFileSection[nIndex],NULL,&Context);
//Put all the files mentioned in the CopyFiles section in an array.
LPTSTR token = NULL; while(bRet) { bRet = SetupGetLineText(&Context,InfHandle,NULL,NULL,lpSectionList, sizeof(lpSectionList),NULL);
token = wcstok(lpSectionList , L","); if(token != NULL) lstrcpy(lpSectionList ,token);
DEBUGTRACE(L"Adding binary file %s\n" , lpSectionList); csarrCopyFileBinaries.Add(lpSectionList); bRet = SetupFindNextLine(&Context,&Context); } }
CString csTemp;
TCHAR str[MAX_PATH] ; for(nIndex = 0 ; nIndex < csarrCopyFileBinaries.GetSize() ; nIndex++) { hr = m_pClass->SpawnInstance(0, &pInstance); m_ptrArr.Add(pInstance); if(GetSystemDirectory(str , sizeof(str)) == 0) return S_FALSE;
lstrcat(str , L"\\Drivers\\");
AddSlashes(str);
csarrCopyFileBinaries[nIndex] = str + csarrCopyFileBinaries[nIndex];
csTemp = m_csPathStr ; csTemp += L":CIM_DataFile.Name=\""; csTemp += csarrCopyFileBinaries[nIndex] + L"\"";
PutPropertyValue( pInstance , L"Antecedent" , m_csAntecedent );
PutPropertyValue( pInstance , L"Dependent" , csTemp );
//////////////////BugBug !
PutPropertyValue( pInstance , L"PnPID" , m_csCurPnPID );
} if( InfHandle != INVALID_HANDLE_VALUE ) SetupCloseInfFile( InfHandle );
return hr; }
///////////////////////////////////////////////////////////////////////
//Creates Instances & populates data given the class type asked for
//Classes_Provided eClasses
//
SCODE CWhqlObj::PutData(IWbemContext *pCtx , Classes_Provided eClasses) { if(pCtx == NULL) return S_FALSE;
if( eClasses == Class_Win32_PnPSignedDriverWin32PnPEntity) { return CreateAssoc( pCtx ); }
LPCTSTR szRegKey = L"SYSTEM\\CurrentControlSet\\Control\\Class"; HKEY hKey , hSubKey ; TCHAR szName[MAX_PATH] ; TCHAR szDeviceID[MAX_PATH] , szClassGuid[MAX_PATH] , szDeviceDesc[MAX_PATH]; LONG nInstanceIndex; LONG len = sizeof(szName); HRESULT hr = S_FALSE; ULONG lRet = 0L; LONG lIndex = 0L;
IEnumWbemClassObject *pEnum = NULL; IWbemClassObject *pInstance = NULL; IWbemClassObject *pObject = NULL;
if(ERROR_SUCCESS != RegOpenKey(HKEY_LOCAL_MACHINE , szRegKey , &hKey)) return S_FALSE;//goto cleanup;
//Get DeviceID , ClassGuid , Description , __NameSpace , __Server from Win32_PnpEntity class
//We need all these fields for further Proc.This call will create an Enum.
BSTR language = SysAllocString(L"WQL"); BSTR query = SysAllocString(L"select DeviceID , ClassGuid , Description , __NameSpace , __Server from Win32_PnpEntity");
hr = m_pNamespace->ExecQuery(language , query , WBEM_FLAG_RETURN_IMMEDIATELY|WBEM_FLAG_FORWARD_ONLY, pCtx , &pEnum);
SysFreeString(language); SysFreeString(query);
if(pEnum == NULL) return S_FALSE;
VARIANT v; VariantInit(&v); m_csAntecedent.Empty(); m_csCurPnPID.Empty();
//Iterate thro' the Enum for each DeviceID.
for(lIndex = 0L ; (WBEM_S_NO_ERROR == pEnum->Next(WBEM_INFINITE , 1 , &pObject , &lRet )) ; ) { //Fill m_csPathStr.Its value will be used in Antecedent & Dependent in assoc. class.
//At the end of the condition we should have something like
//m_csPathStr = "\\\\A-KJAW-RI1\\root\\CIMV2"
if(m_csPathStr.IsEmpty()) { hr = pObject->Get(L"__Server", 0, &v, NULL , NULL); if( SUCCEEDED(hr) ) { m_csPathStr += L"\\\\"; m_csPathStr += V_BSTR(&v); hr = pObject->Get(L"__NameSpace", 0, &v, NULL , NULL); if( SUCCEEDED(hr) ) { m_csPathStr += L"\\"; m_csPathStr += V_BSTR(&v); } DEBUGTRACE(L"Server & Namespace Path = %s\n" , m_csPathStr); VariantClear(&v); } }
//Get DeviceID from the pObject in the Enum.
hr = pObject->Get(L"DeviceID", 0, &v, NULL , NULL); if( SUCCEEDED(hr) ) { if(eClasses == Class_Win32_PnPSignedDriverCIMDataFile) { lstrcpy(szDeviceID , V_BSTR(&v));
m_csCurPnPID = szDeviceID; AddSlashes(szDeviceID);
CString csTemp = m_csPathStr ; csTemp += L":Win32_PnPSignedDriver.PnPId=\""; csTemp += szDeviceID; csTemp += L"\"";
m_csAntecedent = csTemp;//This will be used in assoc. class.
}
lstrcpy(szDeviceID , V_BSTR(&v)); DEBUGTRACE(L"PnPID = %s\n" , szDeviceID); VariantClear(&v); }
//Get ClassGuid from the pObject in the Enum.
hr = pObject->Get(L"ClassGuid", 0, &v, NULL , NULL); if( SUCCEEDED(hr) ) { lstrcpy(szClassGuid , V_BSTR(&v)); DEBUGTRACE(L"ClassGuid = %s\n" , szClassGuid); TRACE(V_BSTR(&v)); }
//Get Description from the pObject in the Enum.
hr = pObject->Get(L"Description", 0, &v, NULL , NULL); if( SUCCEEDED(hr) ) { lstrcpy(szDeviceDesc , V_BSTR(&v)); DEBUGTRACE(L"DriverDesc = %s\n" , szDeviceDesc); TRACE(V_BSTR(&v)); }
if(ERROR_SUCCESS == RegOpenKey(hKey, szClassGuid, &hSubKey) ) { //BugBug:
//Get no. of subKeys for the GUID.
//If there are no subKeys (i presume) this means no Instances of the device
//represented by the GUID are present so we need not Instantiate an Instance Of win32_PnPSignedDriver.
RegQueryInfoKey(hSubKey , NULL , NULL , NULL , &lRet , NULL , NULL , NULL , NULL , NULL , NULL , NULL);
if(eClasses == Class_Win32_PnPSignedDriver && lRet > 0) { hr = m_pClass->SpawnInstance(0, &pInstance); if(FAILED(hr)) continue;
m_ptrArr.Add(pInstance);
PutPropertyValue( pInstance , L"PnPID" , szDeviceID );
PutPropertyValue( pInstance , L"ClassGuid" , szClassGuid );
//PutPropertyValue( pInstance , L"DriverDesc" , szDeviceDesc );
PutPropertyValue( pInstance , L"Description" , szDeviceDesc );
szName[0] = '\0'; hr = GetService(szDeviceID , szName , (ULONG*)&len); CStringArray hwidArr; LPTSTR szHwid = szName;
hwidArr.RemoveAll();
while(len > 0 && *szHwid != '\0') { hwidArr.Add(szHwid); szHwid += lstrlen(szHwid) + 1; }
if( hwidArr.GetSize() ) { LPSAFEARRAY psa; LONG lArrSize = (LONG)hwidArr.GetSize(); SAFEARRAYBOUND rgsabound[] = { lArrSize , 0 };
psa = SafeArrayCreate(VT_BSTR, 1, rgsabound); if (!psa) { return E_OUTOFMEMORY; }
long dim[1]; BSTR bstr = NULL;
for(int nIdx = 0 ; nIdx < lArrSize ; nIdx++) { bstr = hwidArr[nIdx].AllocSysString(); dim[0] = nIdx; hr = SafeArrayPutElement(psa, dim , bstr); }
v.vt = VT_ARRAY|VT_BSTR; v.parray = psa; hr = pInstance->Put(L"HardwareID", 0, &v, 0 ); VariantClear(&v);
hr = SafeArrayDestroyData(psa); } }
nInstanceIndex = 0; EnumRegKeys(hSubKey , pInstance , szDeviceDesc , szDeviceID , pCtx , eClasses); RegCloseKey(hSubKey);
if(eClasses == Class_Win32_PnPSignedDriver) { PutPropertyValue( pInstance , L"Service" , szName ); } } else { continue; }
hr = pObject->Release(); }
//cleanup:
RegCloseKey(hKey); if(pEnum) pEnum->Release();
return S_OK; }
//////////////////////////////////////////////////
//Will return the Service the device supports.
//
SCODE CWhqlObj::GetService(IN LPTSTR szDeviceID , OUT LPTSTR szName , IN ULONG* plen ) { HKEY hSubKey , hInstanceKey; DWORD dwType = REG_SZ ;
if(ERROR_SUCCESS != RegOpenKey(HKEY_LOCAL_MACHINE , L"SYSTEM\\CurrentControlSet\\Enum", &hSubKey)) return S_FALSE;
if(ERROR_SUCCESS != RegOpenKey(hSubKey , szDeviceID, &hInstanceKey)) { RegCloseKey(hSubKey); return S_FALSE; }
//RegQueryValueEx(hInstanceKey , L"Service", NULL , &dwType
// , (UCHAR*)szName , (ULONG*)&len );
//Return HardwareID from this func. not service.
dwType = REG_MULTI_SZ; LONG lRet = RegQueryValueEx(hInstanceKey , L"HardwareID", NULL , &dwType , (UCHAR*)szName , (ULONG*)plen);
RegCloseKey(hSubKey); RegCloseKey(hInstanceKey);
return S_OK; }
///////////////////////////////////////////////////////
//Enumerates reg key & fills up pInstance fields.
//
SCODE CWhqlObj::EnumRegKeys(HKEY hSubKey , IWbemClassObject* pInstance , LPTSTR szDeviceDesc , LPTSTR szDeviceID , IWbemContext *pCtx , Classes_Provided eClasses) { LONG nInstanceIndex = 0; HKEY hInstanceKey; TCHAR szTempStr[MAX_PATH]; ULONG len = sizeof(szTempStr); LONG lRet = -1; DWORD dwType = REG_SZ ; HRESULT hr = S_FALSE; BOOL bIsSigned = FALSE; TCHAR szInfPath[MAX_PATH] , szInfSection[MAX_PATH] , str[MAX_PATH] ;
VARIANT v; VariantInit(&v);
while(ERROR_SUCCESS == RegEnumKey(hSubKey , nInstanceIndex++, szTempStr , sizeof(szTempStr)) ) { lRet = RegOpenKey(hSubKey, szTempStr, &hInstanceKey); if(lRet != ERROR_SUCCESS) continue;
//Get DriverDesc
len = MAX_PATH; lRet = RegQueryValueEx(hInstanceKey , L"DriverDesc", NULL , &dwType , (UCHAR*)szTempStr, &len );
if(lRet != ERROR_SUCCESS) continue;
if(lstrcmpi(szTempStr , szDeviceDesc) != 0) { RegCloseKey(hInstanceKey); continue; }
if(eClasses == Class_Win32_PnPSignedDriver) {
DEBUGTRACE(L"DriverDesc= %s\n" , szTempStr);
//Put DriverDesc
PutPropertyValue( pInstance , L"DriverDesc" , szTempStr );
//Get DriverDate
len = MAX_PATH; lRet = RegQueryValueEx(hInstanceKey,L"DriverDate", NULL , &dwType , (UCHAR*)szTempStr,(ULONG*)&len ); //if(lRet != ERROR_SUCCESS)
// continue;
DEBUGTRACE(L"DriverDate = %s\n" , szTempStr);
//Put DriverDesc
PutPropertyValue( pInstance , L"DriverDate" , szTempStr );
//Get DriverVersion
len = MAX_PATH; lRet = RegQueryValueEx(hInstanceKey,L"DriverVersion", NULL , &dwType , (UCHAR*)szTempStr,(ULONG*)&len ); //if(lRet != ERROR_SUCCESS)
// continue;
DEBUGTRACE(L"DriverVersion= %s\n" , szTempStr);
//Put DriverVersion
PutPropertyValue( pInstance , L"DriverVersion" , szTempStr );
//Get ProviderName
len = MAX_PATH; lRet = RegQueryValueEx(hInstanceKey,L"ProviderName", NULL , &dwType , (UCHAR*)szTempStr,(ULONG*)&len ); //if(lRet != ERROR_SUCCESS)
// continue;
DEBUGTRACE(L"ProviderName= %s\n" , szTempStr);
//Put ProviderName
PutPropertyValue( pInstance , L"ProviderName" , szTempStr ); }
//Get InfPath
szInfPath[0] = '\0'; len = MAX_PATH; lRet = RegQueryValueEx(hInstanceKey,L"InfPath", NULL , &dwType , (UCHAR*)szInfPath,(ULONG*)&len ); //if(lRet != ERROR_SUCCESS)
// continue;
DEBUGTRACE(L"InfPath = %s\n" , szInfPath);
//Put InfPath
if(eClasses == Class_Win32_PnPSignedDriver) { PutPropertyValue( pInstance , L"InfPath" , szInfPath ); }
//Get Infsection
szInfSection[0] = '\0'; len = MAX_PATH; lRet = RegQueryValueEx(hInstanceKey,L"InfSection", NULL , &dwType , (UCHAR*)szInfSection,(ULONG*)&len ); //if(lRet != ERROR_SUCCESS)
// continue;
DEBUGTRACE(L"InfSection = %s\n" , szInfSection);
//Get InfsectionExt
szTempStr[0] = '\0'; len = MAX_PATH; lRet = RegQueryValueEx(hInstanceKey,L"InfSectionExt", NULL , &dwType , (UCHAR*)szTempStr,(ULONG*)&len ); //if(lRet != ERROR_SUCCESS)
// continue;
DEBUGTRACE(L"InfSectionExt = %s\n" , szTempStr);
lstrcat(szInfSection , szTempStr);
//Only if Class_Win32_PnPSignedDriverCIMDataFile call GetInfData to fill up all the
//Win32_PnPSignedDriverCIMDataFile class data.
if(eClasses == Class_Win32_PnPSignedDriverCIMDataFile) { GetInfData(szInfPath , szInfSection , szDeviceID , pCtx , pInstance); }
RegCloseKey(hInstanceKey);
if(eClasses == Class_Win32_PnPSignedDriver) { if(GetWindowsDirectory(str , sizeof(str)) == 0) return S_FALSE;
lstrcat(str , L"\\inf\\" ); lstrcat(str , szInfPath); IsInfSigned( str , pInstance); } break; } return hr; }
SCODE CWhqlObj::CreateAssoc( IWbemContext *pCtx ) { IEnumWbemClassObject *pEnum = NULL; IWbemClassObject *pInstance = NULL; IWbemClassObject *pObject = NULL;
CComBSTR language = L"WQL"; CComBSTR query = L"select DeviceID , ClassGuid , __NameSpace , __Server from Win32_PnpEntity"; CComBSTR tmpBstr = NULL; CComVariant v; ULONG ulRet = 0;
HRESULT hr = m_pNamespace->ExecQuery(language , query , WBEM_FLAG_RETURN_IMMEDIATELY|WBEM_FLAG_FORWARD_ONLY, pCtx , &pEnum);
if(pEnum == NULL) return hr; for( ; (WBEM_S_NO_ERROR == pEnum->Next(WBEM_INFINITE , 1 , &pObject , &ulRet )) ; ) { //Fill m_csPathStr.Its value will be used in Antecedent & Dependent in assoc. class.
//At the end of the condition we should have something like
//m_csPathStr = "\\\\A-KJAW-RI1\\root\\CIMV2"
if(m_csPathStr.IsEmpty()) { hr = pObject->Get(L"__Server", 0, &v, NULL , NULL); if( SUCCEEDED(hr) ) { m_csPathStr += L"\\\\"; m_csPathStr += V_BSTR(&v); hr = pObject->Get(L"__NameSpace", 0, &v, NULL , NULL); if( SUCCEEDED(hr) ) { m_csPathStr += L"\\"; m_csPathStr += V_BSTR(&v); } DEBUGTRACE(L"Server & Namespace Path = %s\n" , m_csPathStr); VariantClear(&v); } }
hr = m_pClass->SpawnInstance(0, &pInstance);
hr = pObject->Get(L"__PATH", 0, &v, NULL , NULL); hr = pInstance->Put(L"Dependent", 0, &v, 0 ); v.Clear(); hr = pObject->Get(L"DeviceID", 0, &v, NULL , NULL); tmpBstr = m_csPathStr + "\\Win32_PnPSignedDriver.PnPID=\""; tmpBstr += v.bstrVal; tmpBstr += "\"";
PutPropertyValue( pInstance , L"Antecedent" , tmpBstr );
m_ptrArr.Add(pInstance); }
return hr; }
VOID AddSlashes(LPTSTR str) { LONG len = lstrlen(str); TCHAR szTmp[MAX_PATH]; int nIndex1,nIndex2; for(nIndex1 = nIndex2= 0 ; nIndex1 < len ; nIndex1++ , nIndex2++) { if(str[nIndex1] == '\\') szTmp[nIndex2++] = '\\'; szTmp[nIndex2] = str[nIndex1]; } szTmp[nIndex2] = '\0'; lstrcpy(str , szTmp); }
VOID DEBUGTRACE(LPTSTR pszText , ... ) { #ifdef _DEBUG
TCHAR szDebugStr[256];
va_list argList; va_start(argList, pszText); vswprintf(szDebugStr , pszText, argList); va_end(argList);
OutputDebugString(szDebugStr); #endif
}
SCODE PutPropertyValue( IWbemClassObject* pInstance , LPCTSTR lpszProperty , LPCTSTR lpszValue ) { HRESULT hr = S_FALSE; if(pInstance == NULL) return hr; CComVariant var; var = lpszValue;
hr = pInstance->Put(lpszProperty , 0, &var, 0 ); return hr; }
|