// [!output IMPL_FILE] : Implementation of [!output CLASS_NAME] #include "stdafx.h" [!if !ATTRIBUTED] #include "[!output PROJECT_NAME].h" [!endif] #include "[!output HEADER_FILE]" ///////////////////////////////////////////////////////////////////////////// // [!output CLASS_NAME] ////////////////////////////////////////////////////////////////////////////// // Class, property and method names // IMPORTANT: the strings defined below are not localizable [!if CLASS_SPECIFIED] const static WCHAR * s_pMyClassName = L"[!output WMICLASSNAME]"; //class name //properies: [!output PROPERTY_DEFINITIONS] //methods: [!output METHOD_DEFINITIONS] [!else] //TODO: define provided class name, e.g.: const static WCHAR * s_pMyClassName = L"MyClassName"; //TODO: define property names of the provided class, e.g.: //const static WCHAR * pMyProperty = L"MyProperty"; //TODO: define method names of the provided class, e.g.: //const static WCHAR * pMyMethod = L"MyMethod"; [!endif] ////////////////////////////////////////////////////////////////////////////// // [!output CLASS_NAME]::Initialize //Refer to MSDN documentation for IWbemProviderInit::Initialize() //for details about implementing this method STDMETHODIMP [!output CLASS_NAME]::Initialize(__in_opt LPWSTR pszUser, LONG lFlags, __in LPWSTR pszNamespace, __in_opt LPWSTR pszLocale, IWbemServices *pNamespace, IWbemContext *pCtx, IWbemProviderInitSink *pInitSink) { CImpersonateClientHelper impersonateClient; HRESULT hr = WBEM_S_NO_ERROR; DWORD dwImpersonationLevel = 0; if(SUCCEEDED(hr = impersonateClient.GetCurrentImpersonationLevel(dwImpersonationLevel))) { // If the current thread has impersonation level set to RPC_C_IMP_LEVEL_IDENTIFY or RPC_C_IMP_LEVEL_ANONYMOUS if(dwImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE) { hr = WBEM_E_ACCESS_DENIED; // Do the access check yourself on the resources accessed based on security configuration for the provider // and change the return code hr appropriately } else { hr = impersonateClient.ImpersonateClient(); } } if(FAILED(hr)) { return hr; } if ( NULL == pNamespace || NULL == pInitSink) { return WBEM_E_INVALID_PARAMETER; } //cache IWbemServices pointer //Note: m_pNamespace is a smart pointer, so it AddRef()'s automatically m_pNamespace = pNamespace; //cache provided class definition //NOTE: the code below assumes that your class definition doesn't change while //your provider is running. If this is not true, you should implement a consumer //for class modification and class deletion events. Refer to WMI documentation //for event consumers on MSDN. BSTR bstrClassName=SysAllocString(s_pMyClassName); if (NULL == bstrClassName) { hr = WBEM_E_OUT_OF_MEMORY; return hr; } hr = m_pNamespace->GetObject(bstrClassName, 0, pCtx, //passing IWbemContext pointer to prevent deadlocks &m_pClass, NULL); SysFreeString(bstrClassName); bstrClassName = NULL; if(FAILED(hr)) { return hr; } //cache path parser class factory hr = CoGetClassObject(CLSID_WbemDefPath, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **) &m_pPathFactory); if (FAILED(hr)) { return hr; } //create helper object m_pHelper = new CInstanceProviderHelper(pNamespace, pCtx); if(m_pHelper == NULL) { return WBEM_E_OUT_OF_MEMORY; } //NOTE: to report a detailed error or status to WMI, you can call //ConstructErrorObject() on m_pHelper anywhere in your provider return pInitSink->SetStatus(WBEM_S_INITIALIZED,0); } ////////////////////////////////////////////////////////////////////////////// // [!output CLASS_NAME]::GetObjectAsync // Refer to MSDN documentation for IWbemServices::GetObjectAsync() // for details about implementing this method STDMETHODIMP [!output CLASS_NAME]::GetObjectAsync( const BSTR bstrObjectPath, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) { [!if SUPPORT_GET ] CImpersonateClientHelper impersonateClient; HRESULT hr = WBEM_S_NO_ERROR; DWORD dwImpersonationLevel = 0; if(SUCCEEDED(hr = impersonateClient.GetCurrentImpersonationLevel(dwImpersonationLevel))) { // If the current thread has impersonation level set to RPC_C_IMP_LEVEL_IDENTIFY or RPC_C_IMP_LEVEL_ANONYMOUS if(dwImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE) { hr = WBEM_E_ACCESS_DENIED; // Do the access check yourself on the resources accessed based on security configuration for the provider // and change the return code hr appropriately } else { hr = impersonateClient.ImpersonateClient(); } } if(FAILED(hr)) { return hr; } if (NULL == pResponseHandler) { return WBEM_E_INVALID_PARAMETER; } CComPtr pInstance; hr = GetInstanceByPath(bstrObjectPath, &pInstance); if (FAILED (hr)) { return hr; } //if all is well, return the object to WMI and indicate success: hr = pResponseHandler->Indicate(1, &(pInstance.p)); return pResponseHandler->SetStatus(0, hr, NULL, NULL); [!else] return WBEM_E_PROVIDER_NOT_CAPABLE; [!endif]} ////////////////////////////////////////////////////////////////////////////// // [!output CLASS_NAME]::PutInstanceAsync() // Refer to MSDN documentation for IWbemServices::PutInstanceAsync() // for details about implementing this method STDMETHODIMP [!output CLASS_NAME]::PutInstanceAsync( IWbemClassObject *pInst, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) { [!if SUPPORT_PUT ] CImpersonateClientHelper impersonateClient; HRESULT hr = WBEM_S_NO_ERROR; DWORD dwImpersonationLevel = 0; if(SUCCEEDED(hr = impersonateClient.GetCurrentImpersonationLevel(dwImpersonationLevel))) { // If the current thread has impersonation level set to RPC_C_IMP_LEVEL_IDENTIFY or RPC_C_IMP_LEVEL_ANONYMOUS if(dwImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE) { hr = WBEM_E_ACCESS_DENIED; // Do the access check yourself on the resources accessed based on security configuration for the provider // and change the return code hr appropriately } else { hr = impersonateClient.ImpersonateClient(); } } if(FAILED(hr)) { return hr; } if ( NULL == pResponseHandler || NULL == pInst) { return WBEM_E_INVALID_PARAMETER; } //TODO: examine possible flag values: WBEM_FLAG_UPDATE_ONLY, //WBEM_FLAG_CREATE_ONLY and WBEM_FLAG_CREATE_OR_UPDATE //and choose the level of support you need and return WBEM_E_PROVIDER_NOT_CAPABLE //for flag values you do not support //TODO: if you are planning to support partial updates, examine pCtx //for "__PUT_EXTENSIONS" and other relevant system context values //and update your instance data appropriately //TODO: handle the instance update or creation here return pResponseHandler->SetStatus(0, WBEM_S_NO_ERROR, NULL, NULL); [!else] return WBEM_E_PROVIDER_NOT_CAPABLE; [!endif] } ////////////////////////////////////////////////////////////////////////////// // [!output CLASS_NAME]::DeleteInstanceAsync() // Refer to MSDN help for IWbemServices::DeleteInstanceAsync() // for details about implementing this method STDMETHODIMP [!output CLASS_NAME]::DeleteInstanceAsync( const BSTR ObjectPath, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) { [!if SUPPORT_DELETE ] CImpersonateClientHelper impersonateClient; HRESULT hr = WBEM_S_NO_ERROR; DWORD dwImpersonationLevel = 0; if(SUCCEEDED(hr = impersonateClient.GetCurrentImpersonationLevel(dwImpersonationLevel))) { // If the current thread has impersonation level set to RPC_C_IMP_LEVEL_IDENTIFY or RPC_C_IMP_LEVEL_ANONYMOUS if(dwImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE) { hr = WBEM_E_ACCESS_DENIED; // Do the access check yourself on the resources accessed based on security configuration for the provider // and change the return code hr appropriately } else { hr = impersonateClient.ImpersonateClient(); } } if(FAILED(hr)) { return hr; } //To implement this method, an instance provider parses the object path string //specified in the strObjectPath parameter and attempts to locate the corresponding //instance and delete it. if (NULL == pResponseHandler) { return WBEM_E_INVALID_PARAMETER; } CComPtr pInstance; hr = GetInstanceByPath(ObjectPath, &pInstance); if (FAILED (hr)) { return hr; } //TODO: iterate through your data source to find the matching object and delete it. //NOTE: If you don't find an object that matches key values indicated in the path, //uncomment the following line to communicate this back to WMI: //return WBEM_E_NOT_FOUND; return pResponseHandler->SetStatus(0, WBEM_S_NO_ERROR, NULL, NULL); [!else] return WBEM_E_PROVIDER_NOT_CAPABLE; [!endif] } ////////////////////////////////////////////////////////////////////////////// // [!output CLASS_NAME]::CreateInstanceEnumAsync() // Refer to MSDN documentation for IWbemServices::CreateInstanceEnumAsync() // for details about implementing this method STDMETHODIMP [!output CLASS_NAME]::CreateInstanceEnumAsync( const BSTR Class, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) { [!if SUPPORT_ENUMERATE ] CImpersonateClientHelper impersonateClient; HRESULT hr = WBEM_S_NO_ERROR; DWORD dwImpersonationLevel = 0; if(SUCCEEDED(hr = impersonateClient.GetCurrentImpersonationLevel(dwImpersonationLevel))) { // If the current thread has impersonation level set to RPC_C_IMP_LEVEL_IDENTIFY or RPC_C_IMP_LEVEL_ANONYMOUS if(dwImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE) { hr = WBEM_E_ACCESS_DENIED; // Do the access check yourself on the resources accessed based on security configuration for the provider // and change the return code hr appropriately } else { hr = impersonateClient.ImpersonateClient(); } } if(FAILED(hr)) { return hr; } if (NULL == pResponseHandler) { return WBEM_E_INVALID_PARAMETER; } hr = WBEM_S_NO_ERROR; [!if IS_SINGLETON] // Prepare an empty object to receive the instance data CComPtr pNewInst; hr = m_pClass->SpawnInstance(0, &pNewInst); VARIANT var; VariantInit(&var); [!if CLASS_SPECIFIED] [!output POPULATE_INSTANCE] [!else] //TODO: populate the instance with properties, e.g.: //var.vt = VT_BSTR; //var.bstrVal = ; //put appropriate value here //BSTR bstrPropertyName=SysAllocString(pMyProperty); //if (NULL == bstrPropertyName) //{ // // depending on how the value was created you may need to clean it up here // hr = WBEM_E_OUT_OF_MEMORY; // return hr; //} //hr = pNewInst->Put(bstrPropertyName, 0, &var, 0); //SysFreeString(bstrPropertyName); //bstrPropertyName = NULL; //// depending on how the value was created you may need to clean it up here //if(FAILED(hr)) //{ // return hr; //} [!endif] // Deliver the class to WMI. hr = pResponseHandler->Indicate(1, &(pNewInst.p)); [!else] // Loop through the private source and create each instance //while () { // Prepare an empty object to receive the class definition. CComPtr pNewInst; hr = m_pClass->SpawnInstance(0, &pNewInst); if (FAILED(hr)) { //TODO: uncomment the line below once the loop condition is in place //break; } VARIANT var; VariantInit(&var); [!if CLASS_SPECIFIED] [!output POPULATE_INSTANCE] [!else] //TODO: populate the instance with properties, e.g.: //var.vt = VT_BSTR; //var.bstrVal = ; //put appropriate value here //BSTR bstrPropertyName=SysAllocString(pMyProperty); //if (NULL == bstrPropertyName) //{ // // depending on how the value was created you may need to clean it up here // hr = WBEM_E_OUT_OF_MEMORY; // return hr; //} //hr = pNewInst->Put(bstrPropertyName, 0, &var, 0); //SysFreeString(bstrPropertyName); //bstrPropertyName = NULL; //// depending on how the value was created you may need to clean it up here //if(FAILED(hr)) //{ // return hr; //} [!endif] // Deliver the class to WMI. hr = pResponseHandler->Indicate(1, &(pNewInst.p)); } [!endif] return pResponseHandler->SetStatus(0, hr, NULL, NULL); [!else] return WBEM_E_PROVIDER_NOT_CAPABLE; [!endif] } ////////////////////////////////////////////////////////////////////////////// //[!output CLASS_NAME]::ExecQueryAsync() // Refer to MSDN documentation for IWbemServices::ExecQueryAsync() // for details about implementing this method [!if SUPPORTS_QUERY] // Refer to MSDN documentaion of IWbemQuery interface for query parsing [!endif] STDMETHODIMP [!output CLASS_NAME]::ExecQueryAsync( const BSTR QueryLanguage, const BSTR Query, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) { CImpersonateClientHelper impersonateClient; HRESULT hr = WBEM_S_NO_ERROR; DWORD dwImpersonationLevel = 0; if(SUCCEEDED(hr = impersonateClient.GetCurrentImpersonationLevel(dwImpersonationLevel))) { // If the current thread has impersonation level set to RPC_C_IMP_LEVEL_IDENTIFY or RPC_C_IMP_LEVEL_ANONYMOUS if(dwImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE) { hr = WBEM_E_ACCESS_DENIED; // Do the access check yourself on the resources accessed based on security configuration for the provider // and change the return code hr appropriately } else { hr = impersonateClient.ImpersonateClient(); } } if(FAILED(hr)) { return hr; } [!if SUPPORTS_QUERY] // Create WMI Query Parser object to parse the given query if(m_pQuery == NULL) { hr = CoCreateInstance(CLSID_WbemQuery, 0, CLSCTX_INPROC_SERVER, IID_IWbemQuery, (LPVOID *)&m_pQuery); } else { hr = WBEM_S_NO_ERROR; } [!else] hr = WBEM_E_NOT_SUPPORTED; [!endif] if(SUCCEEDED(hr)) { // Instance providers have the option of supporting query processing or relying on WMI // for that service. To support queries, an instance provider must be capable of parsing // simple Structured Query Language (SQL) statements, executing the requested query, // and delivering the result set objects to the requester's sink. [!if SUPPORTS_QUERY] // m_pQuery which is an IWbemQuery inteface pointer can be used to parse queries // Uncomment the following part to parse query //hr = m_pQuery->Parse(QueryLanguage,Query,0); //if(FAILED(hr) and (Provider does support Provider-defined queries)) //{ // // Do parsing of provider-defined queries //} // if(SUCCEEDED(hr) // { // // put your query processing code here // } [!else] //TODO: put your query processing code here [!endif] //hr = pResponseHandler->SetStatus(0, WBEM_S_NO_ERROR, NULL, NULL); } return hr; } [!if PROVIDE_METHODS ] ////////////////////////////////////////////////////////////////////////////// // [!output CLASS_NAME]::ExecMethodAsync() // Refer to MSDN documentation for IWbemServices::ExecMethodAsync() // for details about implementing this method STDMETHODIMP [!output CLASS_NAME]::ExecMethodAsync( const BSTR strObjectPath, const BSTR strMethodName, long lFlags, IWbemContext *pCtx, IWbemClassObject *pInParams, IWbemObjectSink *pResponseHandler) { CImpersonateClientHelper impersonateClient; HRESULT hr = WBEM_S_NO_ERROR; DWORD dwImpersonationLevel = 0; if(SUCCEEDED(hr = impersonateClient.GetCurrentImpersonationLevel(dwImpersonationLevel))) { // If the current thread has impersonation level set to RPC_C_IMP_LEVEL_IDENTIFY or RPC_C_IMP_LEVEL_ANONYMOUS if(dwImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE) { hr = WBEM_E_ACCESS_DENIED; // Do the access check yourself on the resources accessed based on security configuration for the provider // and change the return code hr appropriately } else { hr = impersonateClient.ImpersonateClient(); } } if(FAILED(hr)) { return hr; } [!if CLASS_SPECIFIED] [!if !HAS_IMPL_METHODS] //[!output WMICLASSNAME] has no implemented methods return WBEM_E_NOT_SUPPORTED; [!else] // don't need to check inParams because if the method has params it has already been checked for NULL if (NULL == pResponseHandler) { return WBEM_E_INVALID_PARAMETER; } [!output EXEC_METHOD_BODY] [!endif] [!else] ULONG ulPathTest = WBEMPATH_INFO_IS_INST_REF | WBEMPATH_INFO_IS_CLASS_REF; //Check that class name in the path is correct. BSTR bstrClassName=SysAllocString(s_pMyClassName); if (NULL == bstrClassName) { hr = WBEM_E_OUT_OF_MEMORY; return hr; } hr = m_pHelper->CheckInstancePath(m_pPathFactory, strObjectPath, bstrClassName, ulPathTest); SysFreeString(bstrClassName); bstrClassName = NULL; if(FAILED(hr)) { //syntax error in path or path incorrect for class provided return hr; } VARIANT var; VariantInit(&var); //Get input arguments: //TODO: if the method has input arguments, they will be passed as properties of //the pInParams object. The commented line below demonstrates how to extract these: //BSTR bstrInputArg=SysAllocString(L"InputArgument1"); //if (NULL == bstrInputArg) //{ // hr = WBEM_E_OUT_OF_MEMORY; // return hr; //} //hr = pInParams->Get(bstrInputArg, 0, &var, NULL, NULL); //SysFreeString(bstrInputArg); //bstrInputArg = NULL; //if (FAILED(hr)) //{ // return hr; //} //TODO: save input parameter value VariantClear(&var); //parse path to find instance for method execution: for a non-static method CComPtr pInstance; hr = GetInstanceByPath(strObjectPath,&pInstance); if(FAILED(hr)) { return hr; } //TODO: add code to execute the method here //get output parameters class to load the results of the method execution into CComPtr pOutClass; hr = m_pClass->GetMethod(strMethodName, 0, NULL, &pOutClass); if(SUCCEEDED(hr) && pOutClass != NULL) { CComPtr pOutParams; pOutClass->SpawnInstance(0, &pOutParams); //TODO: create output parameters by filling properties //of pOutParams class. For example: //var.vt = VT_BSTR; //var.bstrVal = // fill var with appropriate value //BSTR bstrOutputParam=SysAllocString(L"MyOutputParameter"); //if (NULL == bstrOutputParam) //{ // // depending on how value was created you may need to clean it up here // hr = WBEM_E_OUT_OF_MEMORY; // return hr; //} //hr = pOutParams->Put(bstrOutputParam, 0, &var, 0); //SysFreeString(bstrOutputParam); //bstrOutputParam = NULL; //// depending on how value was created you may need to clean it up here //if(FAILED(hr)) //{ // return hr; //} //var.vt = VT_I2; //var.iVal = //fill var with appropriate value //BSTR bstrReturnValue=SysAllocString(L"ReturnValue"); //if (NULL == bstrReturnValue) //{ // // depending on how value was created you may need to clean it up here // hr = WBEM_E_OUT_OF_MEMORY; // return hr; //} //hr = pOutParams->Put(bstrReturnValue, 0, &var, 0); //SysFreeString(bstrReturnValue); //bstrReturnValue = NULL; //// depending on how value was created you may need to clean it up here //if(FAILED(hr)) //{ // return hr; //} // Send the output object back to the client via the sink hr = pResponseHandler->Indicate(1, &(pOutParams.p)); hr = pResponseHandler->SetStatus(WBEM_STATUS_COMPLETE, hr, NULL, NULL); } return hr; [!endif] } [!endif] ////////////////////////////////////////////////////////////////////////////// // [!output CLASS_NAME]::GetInstanceByPath() parses the path to find out required key values, // then searhces the internal store for an object with matching key values. If such // an object is found, the method spawns a new instance, fills all properties and // returns it in ppInstance. If not, the method returns WBEM_E_NOT_FOUND. STDMETHODIMP [!output CLASS_NAME]::GetInstanceByPath ( /*in*/BSTR bstrPath, /*out*/IWbemClassObject ** ppInstance ) { HRESULT hr = WBEM_E_FAILED; [!if IS_COMPOUND_KEY] ULONG ulPathTest = WBEMPATH_INFO_IS_INST_REF | WBEMPATH_INFO_IS_COMPOUND; [!else] [!if IS_SINGLETON] ULONG ulPathTest = WBEMPATH_INFO_IS_INST_REF | WBEMPATH_INFO_CONTAINS_SINGLETON; [!else] ULONG ulPathTest = WBEMPATH_INFO_IS_INST_REF; [!endif] [!endif] BSTR bstrClassName=SysAllocString(s_pMyClassName); if (NULL == bstrClassName) { hr = WBEM_E_OUT_OF_MEMORY; return hr; } hr = m_pHelper->CheckInstancePath(m_pPathFactory, bstrPath, bstrClassName, ulPathTest); SysFreeString(bstrClassName); bstrClassName = NULL; if (FAILED(hr)) { //syntax error in path or path incorrect for class provided return WBEM_E_INVALID_PARAMETER; } [!if IS_SINGLETON] //[!output WMICLASSNAME] is a singleton object. No need to identify the instance. //NOTE: If the instance is not present, uncomment the following line to communicate this back to WMI: //return WBEM_E_NOT_FOUND; [!else] //Get path parser object: CComPtrpPath; hr = m_pPathFactory->CreateInstance(NULL, IID_IWbemPath, (void **) &pPath); if (FAILED(hr)) { return hr; } hr = pPath->SetText(WBEMPATH_CREATE_ACCEPT_ALL,bstrPath); if (FAILED(hr)) { return hr; } CComPtr pIKeyList; hr = pPath->GetKeyList(&pIKeyList); if (FAILED(hr)) { return hr; } unsigned long ulNumKeys; hr = pIKeyList->GetCount(&ulNumKeys); if(FAILED(hr)) { return hr; } //Get values of key properties BSTR bstrPathName=SysAllocString(bstrPath); if (NULL == bstrPathName) { hr = WBEM_E_OUT_OF_MEMORY; return hr; } unsigned int uKeyNameBufferSize = SysStringLen(bstrPathName); SysFreeString(bstrPathName); bstrPathName = NULL; if (uKeyNameBufferSize >= (unsigned long)(-1)) return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); WCHAR * wKeyName = new WCHAR[uKeyNameBufferSize+1]; if (NULL == wKeyName) { return WBEM_E_OUT_OF_MEMORY; } unsigned long ulKeyNameBufferSize = (unsigned long)uKeyNameBufferSize+1; VARIANT vValue; VariantInit(&vValue); unsigned long ulApparentCimType; for (unsigned long i = 0; i < ulNumKeys; i++) { hr = pIKeyList->GetKey2(i, 0L, &ulKeyNameBufferSize, wKeyName, &vValue, &ulApparentCimType); if (FAILED(hr)) { // save failed hr's and continue or just return an errror on the first failure return hr; } //TODO: save vValue for later use VariantClear(&vValue); } delete[] wKeyName; //TODO: search your internal data source to find the matching object. //If no objects with required key values can be found, //return WBEM_E_NOT_FOUND. [!endif] //spawn new instance CComPtr pNewInst; hr = m_pClass->SpawnInstance(0, &pNewInst); if(FAILED(hr)) { return hr; } VARIANT var; VariantInit(&var); //TODO: fill the properties of the new instance with those of the matching internal object [!if CLASS_SPECIFIED] [!output GET_INSTANCEBYPATH] [!else] //Example: //var.vt = VT_BSTR; //var.bstrVal = ; //put appropriate value here //BSTR bstrPropertyName=SysAllocString(pMyProperty); //if (NULL == bstrPropertyName) //{ // // depending on how the value was created you may need to clean it up here // hr = WBEM_E_OUT_OF_MEMORY; // return hr; //} //hr = pNewInst->Put(bstrPropertyName, 0, &var, 0); //SysFreeString(bstrPropertyName); //bstrPropertyName = NULL; //// depending on how the value was created you may need to clean it up here //if(FAILED(hr)) //{ // return hr; //} [!endif] // use the CComPtrBase.CopyTo method to copy pointer to the output parameter hr = pNewInst.CopyTo(ppInstance); return hr; }