/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: PROVAUTO.CPP Abstract: Defines the acutal "Put" and "Get" functions for the Automation provider. The syntax of the mapping string is; "[path],[classid]|property History: a-davj 3-13-96 Created. --*/ NOTE, THE ORIGINAL SERVER2.CPP HAS CODE TO INITIALIZE gpStorage and ghAutoMutex #include "precomp.h" #include "stdafx.h" #include #include "afxpriv.h" #include "provauto.h" #include "cvariant.h" #define NOTFOUND -1 extern TCHAR * gpStorageFile; extern CMyDLL theApp; ////////////////////////////////////////////////////////////////////////////// // Special automation version of CHandleCache which also stores a hidden window, // site and path information. The hidden window also contains a COleContainer // object. Note that this extra information is only used by OCXs. The various // values are freed up by the CImpAuto::Free routine and so a destructor isnt // needed for this class. //*************************************************************************** // // CAutoCache::CAutoCache // // DESCRIPTION: // // Constructor. // //*************************************************************************** CAutoCache::CAutoCache () { pSavePath = NULL; pSite = NULL; pCtlWnd = NULL; } //*************************************************************************** // // SCODE CImpAuto::DoCall // // DESCRIPTION: // // Makes an IDispath Invoke call. // // PARAMETERS: // // wOpt // ProvObj // iIndex // pDisp // vt // pData // pProp // // RETURN VALUE: // Return: True if OK. If there is a problem, then the error code is set // in pMo in addition to having a FALSE return code. // // //*************************************************************************** SCODE CImpAuto::DoCall( WORD wOpt, CProvObj & ProvObj, int iIndex, LPDISPATCH pDisp, VARTYPE vt, void * pData, WCHAR * pProp) { USES_CONVERSION; DISPID dispid; SCODE sc; COleDispatchDriver Disp; BYTE cArgTypes[MAX_ARGS+2]; // Extra for null terminator and for Put value void * Args[MAX_ARGS+1]; // Extra for put value int iNumArgs = 0,iExp; // Get the dispatch ID for property/method name OLECHAR * pArg; if(pProp == NULL) pArg = T2OLE(ProvObj.sGetToken(iIndex)); else pArg = pProp; sc = pDisp->GetIDsOfNames(IID_NULL,&pArg,1,LOCALE_SYSTEM_DEFAULT, &dispid); if (sc != S_OK) return sc; // Get the arguments, if any, ready for the call memset(cArgTypes,0,MAX_ARGS+2); iNumArgs = ProvObj.iGetNumExp(iIndex); if(iNumArgs > MAX_ARGS) { return WBEM_E_FAILED; // too many arguments } for(iExp = 0; iExp < iNumArgs; iExp++) { // if the argument is a string, then create a BSTR and point to it. If the // argument is an integer, just typecast it into the argument array if(ProvObj.IsExpString(iIndex,iExp)) { Args[iExp] = SysAllocString(T2OLE(ProvObj.sGetStringExp(iIndex,iExp))); if(Args[iExp] == NULL) { sc = WBEM_E_OUT_OF_MEMORY; goto DoInvokeCleanup; } cArgTypes[iExp] = VT_BSTR; } else { cArgTypes[iExp] = VT_I4; Args[iExp] = (void *)ProvObj.iGetIntExp(iIndex,iExp,0); } } // use the MFC automation driver to do the acual invoke Disp.AttachDispatch(pDisp); TRY { if((wOpt & DISPATCH_PROPERTYPUT) || (wOpt & DISPATCH_PROPERTYPUTREF)) { cArgTypes[iNumArgs] = (BYTE)vt; Args[iNumArgs] = pData; Disp.InvokeHelper(dispid,wOpt,VT_EMPTY,NULL,cArgTypes, Args[0],Args[1],Args[2],Args[3],Args[4],Args[5]); } else Disp.InvokeHelper(dispid,wOpt,vt,pData,cArgTypes, Args[0],Args[1],Args[2],Args[3],Args[4],Args[5]); sc = S_OK; } CATCH(CException, e) { sc = WBEM_E_FAILED; } END_CATCH Disp.DetachDispatch(); // This is used to clean up any BSTR strings that might have been allocated. This // can be skipped if it fails before any of the strings are allocated. DoInvokeCleanup: for(iExp = 0; iExp < iNumArgs; iExp++) if(cArgTypes[iExp] == VT_BSTR) SysFreeString((BSTR)Args[iExp]); return sc; } //*************************************************************************** // // SCODE CImpAuto::GetCFileStreamObj // // DESCRIPTION: // // Some OCXs use a persistent stream to load and save data. This // routine gets a COleStreamFile object for such proposes. // // PARAMETERS: // // pPath // ppStorage // **ppFile // *pCache // // RETURN VALUE: // // //*************************************************************************** SCODE CImpAuto::GetCFileStreamObj( const TCHAR * pPath, LPSTORAGE * ppStorage, COleStreamFile **ppFile, CAutoCache *pCache) { SCODE sc; USES_CONVERSION; *ppStorage = NULL; *ppFile = NULL; if(pPath == NULL) // Some OCXs dont need storage, this is OK. return S_OK; // Save Path in the cache ASSERT(pCache->pSavePath == NULL); pCache->pSavePath = new TCHAR[lstrlen(pPath) + 1]; lstrcpy(pCache->pSavePath,pPath); // During first run after install, the compond file will not exist. This // is not as a problem as it will be created during the write sc = StgIsStorageFile(T2OLE(gpStorageFile)); if(sc == STG_E_FILENOTFOUND) return S_OK; if(sc != S_OK) { // unusual failure! return sc; } // Open up the root storage. It should never fail since we just checked to make // sure the file is available. // TODO, might want to mutex control access to the storage sc = StgOpenStorage(T2OLE(gpStorageFile),NULL,STGM_READ|STGM_SHARE_DENY_WRITE, NULL,0l,ppStorage); if(FAILED(sc)) { return WBEM_E_FAILED; } // Create a new COleStreamFile object and set the stream using the storage object. *ppFile = new COleStreamFile(); if(*ppFile == NULL) { return WBEM_E_OUT_OF_MEMORY; } // Open a stream. Note that failure isnt unusual since this might be the // first time for the control. COleStreamFile * pFile = *ppFile; if(!pFile->OpenStream(*ppStorage,pPath,STGM_READ|STGM_SHARE_EXCLUSIVE)) { delete *ppFile; *ppFile = NULL; } return S_OK; } //*************************************************************************** // // BOOL CImpAuto::bIsControl // // DESCRIPTION: // // tests if the IUnknown is pointing to an object which is an OCX. // // PARAMETERS: // // lpTest // // RETURN VALUE: // // //*************************************************************************** BOOL CImpAuto::bIsControl( LPUNKNOWN lpTest) { LPOLECONTROL lpCont = NULL; SCODE sc = lpTest->QueryInterface(IID_IOleControl,(LPVOID *) & lpCont); if(FAILED(sc) || lpCont == NULL) return FALSE; // Have an OCX, free up the pointer for now since it will be retireved by // the COleControlSite object later on lpCont->Release(); return TRUE; } //*************************************************************************** // // SCODE CImpAuto::ParsePathClass // // DESCRIPTION: // // Takes in a string in the sMix argument which is of the same for // as the arguments to VB's GetObject and parses the string. This routine // retrieves the path and class strings and determines the type. This could be; // "path","class" (Type BOTH) or // "path" (Type PATH) or // "","class" (Type NEWCLASS) or // ,"class" (Type RUNNINGCLASS) // // PARAMETERS: // // sMix // sPath // sClass // type // // RETURN VALUE: // // //*************************************************************************** SCODE CImpAuto::ParsePathClass( const CString & sMix, CString & sPath, CString & sClass, OBJTYPE * type) { int iLen = sMix.GetLength(); int iFstst = NOTFOUND; // first quote of the first string int iFsten = NOTFOUND; // last quote of the last string int iSecst = NOTFOUND; // first quote of the second string int iSecen = NOTFOUND; // last quote of the second string int iCommaPos = NOTFOUND; // position of comma between strings int iIndex; int iNumSoFar = 0; // number of '"' found so far // Go through the string and find the starting and ending '"' of the path string // and possibly the class string. Note the location of the comma. for(iIndex = 0; iIndex < iLen; iIndex++) { if(sMix[iIndex] == DQUOTE) { iNumSoFar++; if(iFstst == NOTFOUND && iCommaPos == NOTFOUND) iFstst = iIndex; if(iCommaPos == NOTFOUND) iFsten = iIndex; if(iSecst == NOTFOUND && iCommaPos != NOTFOUND) iSecst = iIndex; if(iCommaPos != NOTFOUND) iSecen = iIndex; } if(sMix[iIndex] == SEPARATOR && (iNumSoFar%2 == 0) && iCommaPos == NOTFOUND) iCommaPos = iIndex; } // Verify that there were an even number of quotes. if(iNumSoFar%2 || iNumSoFar == 0) { return WBEM_E_FAILED; // odd number of quotes is bad } // Extract the strings. for(iIndex = iFstst+1; iIndex < iFsten; iIndex++) { sPath += sMix[iIndex]; // if there is a "" in the string, copy just the first if(sMix[iIndex] == DQUOTE) iIndex++; } for(iIndex = iSecst+1; iIndex < iSecen; iIndex++) { sClass += sMix[iIndex]; // if there is a "" in the string, copy just the first if(sMix[iIndex] == DQUOTE) iIndex++; } // make sure that something was retrieved! if(sPath.GetLength() < 1 && sClass.GetLength() < 1) { return WBEM_E_FAILED; // didnt get any data! } // figure out what type of request if(sPath.GetLength() > 0 && sClass.GetLength() > 0) *type = BOTH; else if(sPath.GetLength() > 0 && sClass.GetLength() == 0) *type = PATH; else if(sPath.GetLength() == 0 && sClass.GetLength() > 0 && iFstst != NOTFOUND) *type = NEWCLASS; else if(sPath.GetLength() == 0 && sClass.GetLength() > 0 && iFstst == NOTFOUND) *type = RUNNINGCLASS; else { return WBEM_E_FAILED; // got some sort of junk! } return S_OK; // all is well } //*************************************************************************** // // CImpAuto::CImpAuto // // DESCRIPTION: // // Constructor. // // PARAMETERS: // // ObjectPath // User // Password // //*************************************************************************** CImpAuto::CImpAuto() { wcscpy(wcCLSID,L"{DAC651D1-7CC7-11cf-A5B6-00AA00680C3F}"); } //*************************************************************************** // void CImpAuto::EndBatch // // DESCRIPTION: // // Called at the end of a batch of Refrest/Update Property calls. Free up // any cached handles and then delete the handle cache. // // PARAMETERS: // // lFlags // pClassInt // *pObj // bGet // // RETURN VALUE: // // //*************************************************************************** void CImpAuto::EndBatch( long lFlags, IWbemClassObject FAR * pClassInt, CObject *pObj, BOOL bGet) { if(pObj != NULL) { Free(0,(CAutoCache *)pObj); delete pObj; } } //*************************************************************************** // // void CImpAuto::Free // // DESCRIPTION: // // Frees up cached IDispatch interfaces starting with position // iStart till the end. After freeing handles, the cache object // member function is used to delete the cache entries. // // PARAMETERS: // // iStart // pCache // // RETURN VALUE: // // //*************************************************************************** void CImpAuto::Free( int iStart, CAutoCache * pCache) { int iCurr; LPOLEOBJECT pTemp = NULL; AFX_MANAGE_STATE(AfxGetStaticModuleState()) for(iCurr = pCache->lGetNumEntries()-1; iCurr >= iStart; iCurr--) { LPDISPATCH pDisp = (LPDISPATCH)pCache->hGetHandle(iCurr); if(pDisp != NULL) pDisp->Release(); } pCache->Delete(iStart); // get cache to delete the entries if(iStart == 0) { if(pCache->pSavePath) { if(pCache->pSite) { DWORD dwRet; dwRet = WaitForSingleObject(ghAutoMutex,MAX_AUTO_WAIT); if(dwRet == WAIT_ABANDONED || dwRet == WAIT_OBJECT_0) { try { StoreControl(pCache); } catch(...) {} ReleaseMutex(ghAutoMutex); } } delete pCache->pSavePath; pCache->pSavePath = NULL; } if(pCache->pCtlWnd != NULL) { // DONOT delete the Site object since the Control container does it!! // Note important HACK! There is a bug in the MFC that ships with VC 4.0. // The COleControlSite::CreateOrLoad routine has a QueryInterface call // to get an IPersistMemory interface, BUT DOES NOT RELEASE IT! So, to // release it, there is a bit of code here to call release until the count // goes to 0. if(pCache->pSite) { pTemp = pCache->pSite->m_pObject; pTemp->AddRef(); } pCache->pCtlWnd->DestroyWindow(); // note that DestroyWindow frees up the object pCache->pCtlWnd = NULL; // and gets rid of the container within // First release for the AddRef right above. Second possible release is // for the bug! if(pCache->pSite && pTemp) { DWORD dwRes = pTemp->Release(); if(dwRes > 0) pTemp->Release(); } } pCache->pSite = NULL; } } //*************************************************************************** // // SCODE CImpAuto::RefreshProperty // // DESCRIPTION: // // Gets the value of a single property from an automation server. // // PARAMETERS: // // lFlags // pClassInt // PropName // ProvObj // pPackage // pVar // // RETURN VALUE: // // //*************************************************************************** SCODE CImpAuto::RefreshProperty( long lFlags, IWbemClassObject FAR * pClassInt, BSTR PropName, CProvObj & ProvObj, CObject * pPackage, CVariant * pVar) { SCODE sc; USES_CONVERSION; CString sRet; LPDISPATCH pDisp = NULL; CAutoCache * pCache = (CAutoCache *)pPackage; AFX_MANAGE_STATE(AfxGetStaticModuleState()) // Do a second parse on the provider string. CProvObj ObjectPath(ProvObj.sGetFullToken(1),DOT); sc = ObjectPath.dwGetStatus(1); //todo, is 1 ok?? if(sc != S_OK) return sc; // Get the IDispatch interface and then do the "Get" int iDepth = ObjectPath.iGetNumTokens()-1; HINSTANCE hTest = afxCurrentInstanceHandle; pDisp = pGetDispatch(&sc,ObjectPath,ProvObj.sGetToken(0),pCache,iDepth); if(pDisp) { int iLast = ObjectPath.iGetNumTokens()-1; sc = DoCall(DISPATCH_PROPERTYGET,ObjectPath,iLast,pDisp,VT_BSTR,&sRet); if(sc == S_OK) { CVariant varAuto; sc = varAuto.SetData(T2OLE(sRet),VT_LPWSTR); if(sc != S_OK) return sc; sc = varAuto.DoPut(lFlags,pClassInt,PropName,pVar); } } return sc; } //*************************************************************************** // // LPDISPATCH CImpAuto::pGetBoth // // DESCRIPTION: // // Gets IDISPATCH interface to the server using path and class. // // PARAMETERS: // // psc // pPath // pClass // *pCache // // RETURN VALUE: // // //*************************************************************************** LPDISPATCH CImpAuto::pGetBoth( SCODE * psc, const TCHAR * pPath, const TCHAR * pClass, CAutoCache *pCache) { HRESULT sc; USES_CONVERSION; CLSID clsid; LPDISPATCH lpRetDispatch = NULL; LPUNKNOWN lpUnknown = NULL; LPPERSISTFILE lpPersist = NULL; sc = CLSIDFromProgID(T2OLE(pClass), &clsid); if (FAILED(sc)) goto BadBoth; // create an instance with the IUnknown sc = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID *)&lpUnknown); if (FAILED(sc)) goto BadBoth; OleRun(lpUnknown); // Some objects need this! // If the class is an OCX, then use special code to get its value if(bIsControl(lpUnknown)) { DWORD dwRet; dwRet = WaitForSingleObject(ghAutoMutex,MAX_AUTO_WAIT); if(dwRet == WAIT_ABANDONED || dwRet == WAIT_OBJECT_0) { try { lpRetDispatch = pGetOCX(psc,pPath,clsid,pCache,lpUnknown); //frees lpUnknown } catch(...) { lpRetDispatch = NULL; } ReleaseMutex(ghAutoMutex); } else *psc = WBEM_E_FAILED; return lpRetDispatch; } // query for persist file interface sc = lpUnknown->QueryInterface(IID_IPersistFile,(LPVOID *)&lpPersist); lpUnknown->Release(); if (FAILED(sc)) goto BadBoth; // do a sanity check, probably not necessary. ASSERT(lpPersist != NULL); if (lpPersist == NULL) { sc = WBEM_E_FAILED; goto BadBoth; } // Load up the desired file sc = lpPersist->Load(T2OLE(pPath),0); if (FAILED(sc)) goto BadBoth; sc = lpPersist->QueryInterface(IID_IDispatch,(LPVOID *)&lpRetDispatch); lpPersist->Release(); if (FAILED(sc)) goto BadBoth; ASSERT(lpRetDispatch != NULL); if (lpRetDispatch != NULL) return lpRetDispatch; BadBoth: if(lpPersist != NULL) lpPersist->Release(); *psc = sc; return NULL; } //*************************************************************************** // // LPDISPATCH CImpAuto::pGetDispatch // // DESCRIPTION: // // Gets get the IDispatch for gets and sets // // PARAMETERS: // // psc // ObjectPath // pPathClass // *pCache // iDepth // // RETURN VALUE: // // //*************************************************************************** LPDISPATCH CImpAuto::pGetDispatch( SCODE * psc, CProvObj & ObjectPath, LPCTSTR pPathClass, CAutoCache *pCache, int iDepth) { LPDISPATCH pRetDisp; int iNumSkip; int iIndex; // Get at least the application interface. If the object path is in common with // previous calls, then some of the subobject are already set and that is // indicated by the value in iNumSkip; pRetDisp = pGetDispatchRoot(psc,ObjectPath,pPathClass,pCache,iNumSkip); // For each subobject that wasnt in the cache, get its IDispatch, and add it // to the cache. for(iIndex = iNumSkip; iIndex < iDepth && pRetDisp; iIndex++) { LPDISPATCH pNewDisp; *psc = DoCall(DISPATCH_PROPERTYGET,ObjectPath,iIndex, pRetDisp,VT_DISPATCH,&pNewDisp); if(*psc != S_OK) return NULL; *psc = pCache->lAddToList(ObjectPath.sGetFullToken(iIndex),pNewDisp); if(*psc != S_OK) return NULL; pRetDisp = pNewDisp; } return pRetDisp; } //*************************************************************************** // // LPDISPATCH CImpAuto::pGetDispatchRoot // // DESCRIPTION: // // Gets the interface to the application object or some other starting point // such as a "document" object. // // PARAMETERS: // // psc // ObjectPath // pPathClass // *pCache // iNumSkip // // RETURN VALUE: // // //*************************************************************************** LPDISPATCH CImpAuto::pGetDispatchRoot( SCODE * psc, CProvObj & ObjectPath, LPCTSTR pPathClass, CAutoCache *pCache, int & iNumSkip) { iNumSkip = 0; LPDISPATCH pRetDisp = NULL; OBJTYPE type; HKEY hRoot = NULL; const TCHAR * pObject = ObjectPath.sGetFullToken(0); if(pPathClass == NULL || pObject == NULL) { *psc = WBEM_E_FAILED; // bad mapping string return NULL; } // If there are handles in the cache, then they may be used if and // only if the path/class matches. if(pCache->lGetNumEntries() > 0) { if(lstrcmpi(pCache->sGetString(0),pPathClass)) // The path/class has changed.free all the cached handles. Free(0,pCache); else { // The Path/class matches what is in the cache. Determine how much // else is in common, free what isnt in common, and return // the subkey share a common path. iNumSkip = pCache->lGetNumMatch(1,0,ObjectPath); Free(1+iNumSkip,pCache); return (LPDISPATCH )pCache->hGetHandle(iNumSkip); } } // Need to get the initial IDispatch handle. Start off by breaking up // path/class string. Note that TRY/CATCH is used since // bParsePathClass does CString allocations which can give exceptions. CString sPath,sClass; TRY { *psc = ParsePathClass(pPathClass,sPath,sClass,&type); if(*psc != S_OK) return NULL; // bad string, actual error set in bParsePathClass } CATCH(CException, e) { *psc = WBEM_E_OUT_OF_MEMORY; return NULL; } END_CATCH // Based on the path/class combination, call the correct routine to // actually hook up to the server. switch(type) { case BOTH: pRetDisp = pGetBoth(psc,sPath,sClass,pCache); break; case PATH: pRetDisp = pGetPath(psc,sPath); break; case NEWCLASS: pRetDisp = pGetNewClass(psc,sClass,pCache); break; case RUNNINGCLASS: pRetDisp = pGetRunningClass(psc,sClass,pCache); break; default: *psc = WBEM_E_FAILED; } if(pRetDisp == NULL) return NULL; // Got the initial IDispatch interface. Add it to the Cache *psc = pCache->lAddToList(pPathClass,pRetDisp); if(*psc != S_OK) { // error adding to cache, probably memory pRetDisp->Release(); return NULL; } return pRetDisp; // all is well! } //*************************************************************************** // // LPDISPATCH CImpAuto::pGetNewClass // // DESCRIPTION: // // Gets IDISPATCH interface to the server using the class id and always // creates a new object. // // PARAMETERS: // // psc // pClass // *pCache // // RETURN VALUE: // // //*************************************************************************** LPDISPATCH CImpAuto::pGetNewClass( SCODE * psc, const TCHAR * pClass, CAutoCache *pCache) { HRESULT sc; CLSID clsid; USES_CONVERSION; LPDISPATCH lpRetDispatch = NULL; LPUNKNOWN lpUnknown = NULL; sc = CLSIDFromProgID(T2OLE(pClass), &clsid); if (FAILED(sc)) goto BadNewClass; // create an instance with the IUnknown sc = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID *)&lpUnknown); if (FAILED(sc)) goto BadNewClass; OleRun(lpUnknown); // If the class is an OCX, then use special code to get its value if(bIsControl(lpUnknown)) { DWORD dwRet; dwRet = WaitForSingleObject(ghAutoMutex,MAX_AUTO_WAIT); if(dwRet == WAIT_ABANDONED || dwRet == WAIT_OBJECT_0) { try { lpRetDispatch = pGetOCX(psc,NULL,clsid,pCache,lpUnknown); //frees lpUnknown } catch(...) { lpRetDispatch = NULL; } ReleaseMutex(ghAutoMutex); } else *psc = WBEM_E_FAILED; return lpRetDispatch; } // query for IDispatch interface sc = lpUnknown->QueryInterface(IID_IDispatch,(LPVOID *)&lpRetDispatch); lpUnknown->Release(); if (FAILED(sc)) goto BadNewClass; ASSERT(lpRetDispatch != NULL); return lpRetDispatch; BadNewClass: *psc = sc; return NULL; } //*************************************************************************** // // LPDISPATCH CImpAuto::pGetOCX // // DESCRIPTION: // // Gets IDISPATCH interface to an OCX. Note that the lpUnk should be // released at the end of the routine so that the server will // continue running even as it is setup as a control! // // PARAMETERS: // // psc // pPath // clsid // *pCache // lpUnk // // RETURN VALUE: // // //*************************************************************************** LPDISPATCH CImpAuto::pGetOCX( SCODE * psc, const TCHAR * pPath, CLSID & clsid, CAutoCache *pCache, LPUNKNOWN lpUnk) { RECT rect; rect.left = 0; rect.right = 150; rect.top = 0; rect.bottom = 150; LPDISPATCH lpRetDispatch = NULL; COleControlContainer * pCont = NULL; BOOL bOK; SCODE sc; LPSTORAGE pStorage = NULL; COleStreamFile * pFileStream = NULL; // Possibly create a COleStreamFile object. This is done only if the pPath is // not NULL and if everything exists. This routine must release the pStorage // interface and the pFileStream objects if the are created! *psc = GetCFileStreamObj(pPath, &pStorage, &pFileStream,pCache); if(*psc != S_OK) goto EndpGetOCX; // Create the window that will contain the controls. The window's contstructor // creates the COleContainer object. pCache->pCtlWnd = new CCtlWnd(); if(pCache->pCtlWnd == NULL) { *psc = WBEM_E_OUT_OF_MEMORY; goto EndpGetOCX; } pCont = pCache->pCtlWnd->pGetCont(); // Do quick sanity check, shouldnt fail if(pCont == NULL) { *psc = WBEM_E_FAILED; goto EndpGetOCX; } // Use the control container to create the site and control in one call bOK = pCont->CreateControl(NULL, clsid, NULL,WS_CHILD, rect, 23, pFileStream, FALSE, NULL, &pCache->pSite); if(!bOK || pCache->pSite == NULL) { pCache->pSite = NULL; *psc = WBEM_E_FAILED; goto EndpGetOCX; } // query for IDispatch interface sc = pCache->pSite->m_pObject->QueryInterface(IID_IDispatch, (LPVOID *)&lpRetDispatch); if (FAILED(sc)) { *psc = sc; lpRetDispatch = NULL; } EndpGetOCX: if(pFileStream) { pFileStream->Close(); delete pFileStream; } if(pStorage) pStorage->Release(); if(lpUnk) lpUnk->Release(); return lpRetDispatch; } //*************************************************************************** // // LPDISPATCH CImpAuto::pGetPath // // DESCRIPTION: // // Gets IDISPATCH interface to the server using the path. // // PARAMETERS: // // psc // pPath // // RETURN VALUE: // // //*************************************************************************** LPDISPATCH CImpAuto::pGetPath( SCODE * psc, const TCHAR * pPath) { HRESULT sc; LPBC pbc=NULL; LPDISPATCH lpRetDispatch = NULL; USES_CONVERSION; LPMONIKER pmk; pmk=NULL; DWORD dwEat; // Get a bind context object. sc = CreateBindCtx(0, &pbc); if (FAILED(sc)) { *psc = sc; return NULL; } // Get a moniker sc = MkParseDisplayName(pbc, T2OLE(pPath), &dwEat, &pmk); if (FAILED(sc)) goto BadPath; // Bind the moniker sc = (pmk)->BindToObject(pbc, NULL, IID_IDispatch , (void **)&lpRetDispatch); pmk->Release(); if(FAILED(sc)) goto BadPath; ASSERT(lpRetDispatch != NULL); // If the class is an OCX, then something is wrong here. if(bIsControl(lpRetDispatch)) { *psc = WBEM_E_FAILED; lpRetDispatch->Release(); return NULL; } if(lpRetDispatch) { // should always be true at this point! pbc->Release(); return lpRetDispatch; } sc = WBEM_E_FAILED; BadPath: if(pbc) pbc->Release(); *psc = sc; return NULL; } //*************************************************************************** // // LPDISPATCH CImpAuto::pGetRunningClass // // DESCRIPTION: // // Gets IDISPATCH interface to the server using the class id and always // returns only running objects. // // PARAMETERS: // // psc // pClass // *pCache // // RETURN VALUE: // // //*************************************************************************** LPDISPATCH CImpAuto::pGetRunningClass( SCODE * psc, const TCHAR * pClass, CAutoCache *pCache) { HRESULT sc; CLSID clsid; LPDISPATCH lpRetDispatch = NULL; USES_CONVERSION; LPUNKNOWN lpUnknown = NULL; sc = CLSIDFromProgID(T2OLE(pClass), &clsid); if (FAILED(sc)) goto BadRunningClass; // create an instance with the IUnknown sc = GetActiveObject(clsid,NULL,&lpUnknown); if (FAILED(sc)) goto BadRunningClass; // query for IDispatch interface sc = lpUnknown->QueryInterface(IID_IDispatch,(LPVOID *)&lpRetDispatch); lpUnknown->Release(); if (FAILED(sc)) goto BadRunningClass; // If the class is an OCX, then use special code to get its value if(bIsControl(lpUnknown)) return pGetOCX(psc,NULL,clsid,pCache,lpUnknown); //frees lpUnknown ASSERT(lpRetDispatch != NULL); if (lpRetDispatch != NULL) return lpRetDispatch; BadRunningClass: *psc = sc; return NULL; } //*************************************************************************** // // SCODE CImpAuto::UpdateProperty // // DESCRIPTION: // // Writes the value of a single property to an automation server. // // PARAMETERS: // // lFlags // pClassInt // PropName // ProvObj // pPackage // pVar // // RETURN VALUE: // // //*************************************************************************** SCODE CImpAuto::UpdateProperty( long lFlags, IWbemClassObject FAR * pClassInt, BSTR PropName, CProvObj & ProvObj, CObject * pPackage, CVariant * pVar) { CVariant vIn; SCODE sc; AFX_MANAGE_STATE(AfxGetStaticModuleState()) // Get the property value if(pClassInt) { sc = pClassInt->Get(PropName,lFlags,vIn.GetVarPtr(),NULL,NULL); if(sc != S_OK) return sc; sc = vIn.ChangeType(VT_BSTR); } else if(pVar) { sc =OMSVariantChangeType(vIn.GetVarPtr(), pVar->GetVarPtr(),0, VT_BSTR); } else sc = WBEM_E_FAILED; if(sc != S_OK) return sc; LPDISPATCH pDisp = NULL; CAutoCache * pCache = (CAutoCache *)pPackage; // Do a second parse on the provider string. CProvObj ObjectPath(ProvObj.sGetFullToken(1),DOT); sc = ObjectPath.dwGetStatus(1); if(sc != S_OK) return sc; // Get the IDispatch interface and then do the "Get" int iDepth = ObjectPath.iGetNumTokens()-1; pDisp = pGetDispatch(&sc,ObjectPath,ProvObj.sGetToken(0),pCache,iDepth); if(pDisp) { int iLast = ObjectPath.iGetNumTokens()-1; sc = DoCall(DISPATCH_PROPERTYPUT,ObjectPath,iLast,pDisp,VT_BSTR,vIn.GetBstr()); } return sc; } //*************************************************************************** // // SCODE CImpAuto::StartBatch // // DESCRIPTION: // // Called at the start of a batch of Refrest/Update Property calls. Initialize // the handle cache. // // PARAMETERS: // // lFlags // pClassInt // **pObj // bGet // // RETURN VALUE: // // //*************************************************************************** SCODE CImpAuto::StartBatch( long lFlags, IWbemClassObject FAR * pClassInt, CObject **pObj, BOOL bGet) { *pObj = new CAutoCache; return (pObj != NULL) ? S_OK : WBEM_E_OUT_OF_MEMORY; } //*************************************************************************** // // void CImpAuto::StoreControl // // DESCRIPTION: // // Used when an OCX is no longer being used and the OCX uses // a stream to store its data. // // PARAMETERS: // // *pCache // //*************************************************************************** void CImpAuto::StoreControl( CAutoCache *pCache) { SCODE sc; LPSTORAGE pIStorage = NULL; LPSTREAM pIStream = NULL; LPPERSISTSTREAMINIT pPersStm = NULL; USES_CONVERSION; // Open up the storage file, create it if necessary sc = StgIsStorageFile(T2OLE(gpStorageFile)); if(sc == S_OK) sc = StgOpenStorage(T2OLE(gpStorageFile),NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL,0L, &pIStorage); else if(sc == STG_E_FILENOTFOUND) sc = StgCreateDocfile(T2OLE(gpStorageFile), STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE, 0L, &pIStorage); if(sc != S_OK) return; //todo, error handling????? // Open/Create the stream sc = pIStorage->OpenStream(T2OLE(pCache->pSavePath),NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0,&pIStream); if(sc == STG_E_FILENOTFOUND) sc = pIStorage->CreateStream(T2OLE(pCache->pSavePath), STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_FAILIFTHERE, 0,0,&pIStream); if(sc != S_OK) { goto cleanupStoreControl; } sc = pCache->pSite->m_pObject->QueryInterface(IID_IPersistStreamInit, (void **)&pPersStm); if(!FAILED(sc)) pPersStm->Save(pIStream,TRUE); cleanupStoreControl: if(pPersStm) pPersStm->Release(); if(pIStream) pIStream->Release(); if(pIStorage) pIStorage->Release(); } ///////////////////////////////////////////////////////////////////////////////// // Special version of CWnd which is used to hold OCXs. It automatically sets // itself up as an OCX container and it also has a function to return pointer // to the control container. //*************************************************************************** // // CCtlWnd::CCtlWnd // // DESCRIPTION: // // Constructor. // //*************************************************************************** CCtlWnd::CCtlWnd() { RECT rect; rect.left = 0; rect.right = 50; rect.bottom = 100; rect.top = 0; AfxEnableControlContainer(); HANDLE hh = afxCurrentInstanceHandle; hh = AfxGetInstanceHandle(); Create(NULL,""); InitControlContainer(); } //*************************************************************************** // // SCODE CImpAuto::MakeEnum // // DESCRIPTION: // // Creates a CEnumPerfInfo object which can be used for enumeration // // PARAMETERS: // // pClass // ProvObj // ppInfo // // RETURN VALUE: // // //*************************************************************************** SCODE CImpAuto::MakeEnum( IWbemClassObject * pClass, CProvObj & ProvObj, CEnumInfo ** ppInfo) { SCODE sc; *ppInfo = NULL; LPDISPATCH pDisp = NULL; CAutoCache Cache; AFX_MANAGE_STATE(AfxGetStaticModuleState()) // Do a second parse on the provider string. CProvObj ObjectPath(ProvObj.sGetFullToken(1),DOT); sc = ObjectPath.dwGetStatus(1); //todo, is 1 ok?? if(sc != S_OK) return sc; // Get the IDispatch interface and then get thedo the "Get" int iDepth = ObjectPath.iGetNumTokens(); pDisp = pGetDispatch(&sc,ObjectPath,ProvObj.sGetToken(0),&Cache,iDepth); if(pDisp) { int iCount; sc = DoCall(DISPATCH_PROPERTYGET,ObjectPath,0,pDisp,VT_I4, &iCount,L"Count"); if(sc == S_OK) { // Create a new CEnumAutoInfo object. CEnumAutoInfo * pInfo = new CEnumAutoInfo(iCount); if(pInfo == NULL) { sc = WBEM_E_OUT_OF_MEMORY; } else *ppInfo = pInfo; } } return sc; } //*************************************************************************** // // SCODE CImpAuto::GetKey // // DESCRIPTION: // // // PARAMETERS: // // pInfo // iIndex // ppKey // // RETURN VALUE: // // //*************************************************************************** SCODE CImpAuto::GetKey( CEnumInfo * pInfo, int iIndex, LPWSTR * ppKey) { *ppKey = new WCHAR[10]; CEnumAutoInfo * pAuto = (CEnumAutoInfo *)pInfo; if(iIndex >= pAuto->GetCount()) return WBEM_E_FAILED; if(*ppKey == NULL) return WBEM_E_OUT_OF_MEMORY; _itow(iIndex + 1,*ppKey,10); return S_OK; } //*************************************************************************** // // SCODE CImpAuto::MergeStrings // // DESCRIPTION: // // Combines the Class Context, Key, and Property Context strings. // // PARAMETERS: // // ppOut // pClassContext // pKey // pPropContext // // RETURN VALUE: // // //*************************************************************************** SCODE CImpAuto::MergeStrings( LPWSTR * ppOut, LPWSTR pClassContext, LPWSTR pKey, LPWSTR pPropContext) { // Allocate space for output int iLen = 6; if(pClassContext) iLen += wcslen(pClassContext); if(pKey) iLen += wcslen(pKey); if(pPropContext) iLen += wcslen(pPropContext); else return WBEM_E_FAILED; // should always have this! *ppOut = new WCHAR[iLen]; if(*ppOut == NULL) return WBEM_E_OUT_OF_MEMORY; // simple case is that everything is in the property context. That would // be the case when the provider is being used as a simple dynamic // property provider if(pClassContext == NULL || pKey == NULL) { wcscpy(*ppOut,pPropContext); return S_OK; } // Copy the class context, property, and finally the key wcscpy(*ppOut,pClassContext); wcscat(*ppOut,L"("); wcscat(*ppOut,pKey); wcscat(*ppOut,L")."); wcscat(*ppOut,pPropContext); return S_OK; } //*************************************************************************** // // CEnumAutoInfo::CEnumAutoInfo // // DESCRIPTION: // // Constructor. // // PARAMETERS: // // iCount // //*************************************************************************** CEnumAutoInfo::CEnumAutoInfo( int iCount) { m_iCount = iCount; } //*************************************************************************** // // CEnumAutoInfo::~CEnumAutoInfo // // DESCRIPTION: // // Destructor. // //*************************************************************************** CEnumAutoInfo::~CEnumAutoInfo() { } //*************************************************************************** // // CImpAutoProp::CImpAutoProp // // DESCRIPTION: // // Constructor. // //*************************************************************************** CImpAutoProp::CImpAutoProp() { m_pImpDynProv = new CImpAuto(); } //*************************************************************************** // // CImpAutoProp::~CImpAutoProp // // DESCRIPTION: // // Destructor. // //*************************************************************************** CImpAutoProp::~CImpAutoProp() { if(m_pImpDynProv) delete m_pImpDynProv; }