Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1650 lines
41 KiB

/*++
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 <wbemidl.h>
#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;
}