windows-nt/Source/XPSP1/NT/multimedia/dshow/vidctl/msvidctl/createregbag.cpp
2020-09-26 16:20:57 +08:00

679 lines
21 KiB
C++

/////////////////////////////////////////////////////////////////////////////
//
// CreateRegBag.cpp : Implementation of CCreateRegBag
// Copyright (c) Microsoft Corporation 1999.
//
// some code copied from DShow device moniker devmon.cpp
//
#include "stdafx.h"
#include "Regbag.h"
#include "CreateRegBag.h"
DEFINE_EXTERN_OBJECT_ENTRY(CLSID_CreatePropBagOnRegKey, CCreateRegBag)
// REV2: support for more data types
// REG_MULTI_SZ could be supported via VT_BSTR | VT_ARRAY
// REG_BINARY blobs could be vt_unknown that's an istream
// subkeys could be vt_unknown that's an IPropertyBag2 if default val isn't clsid
//
/////////////////////////////////////////////////////////////////////////////
// CCreateRegBag
HRESULT CRegBagBase::DeleteSubKey(CRegKey& hk, LPCOLESTR pszPropName) {
// the registry will allow a peer subkey and value to have the same name
// this doesn't match property bag semantics. so we force this to never occur.
// after we write a primitive value then we check for a subkey of the
// same name and recursively delete it if it exists. from the property bag
// perspective this amounts to changing the type of the property by writing
// a new type to the same name.
// however, if pszpropname is empty(the default value) the current key will
// get deleted which we don't want
ASSERT(hk.m_hKey != NULL && pszPropName);
if (!wcslen(pszPropName)) {
return NOERROR;
}
USES_CONVERSION;
DWORD hr = hk.RecurseDeleteKey(OLE2CT(pszPropName));
switch (hr) {
case ERROR_BADKEY:
case ERROR_CANTOPEN:
case ERROR_KEY_DELETED:
case ERROR_FILE_NOT_FOUND:
return S_FALSE;
default:
return HRESULT_FROM_WIN32(hr);
}
}
HRESULT CRegBagBase::DeleteValue(CRegKey& hk, LPCOLESTR pszPropName) {
ASSERT(hk.m_hKey && pszPropName);
// this is the inverse of delete duplicate key name
USES_CONVERSION;
DWORD hr = hk.DeleteValue(OLE2CT(pszPropName));
switch (hr) {
case ERROR_FILE_NOT_FOUND:
#if 0
??? what else does reg return for missing value
case ??? missing value
#endif
return S_FALSE;
default:
return HRESULT_FROM_WIN32(hr);
}
}
HRESULT CRegBagBase::RegConvertToVARIANT(VARIANT *pVar, DWORD dwType, LPBYTE pbData, DWORD cbSize) {
ASSERT(pVar && pbData);
USES_CONVERSION;
switch (dwType) {
case REG_DWORD:
if (pVar->vt != VT_UI4) {
HRESULT hr = VariantChangeType(pVar, pVar, 0, VT_UI4);
if (FAILED(hr)) {
return E_INVALIDARG;
}
}
ASSERT(pVar->vt == VT_UI4);
ASSERT(pbData);
pVar->ulVal = *(reinterpret_cast<ULONG *>(pbData));
break;
case REG_QWORD:
if (pVar->vt != VT_UI8) {
HRESULT hr = VariantChangeType(pVar, pVar, 0, VT_UI8);
if (FAILED(hr)) {
return E_INVALIDARG;
}
}
ASSERT(pVar->vt == VT_UI8);
ASSERT(pbData);
pVar->ullVal = *(reinterpret_cast<ULONGLONG *>(pbData));
break;
case REG_SZ:
switch(pVar->vt) {
case VT_EMPTY:
case VT_NULL:
pVar->vt = VT_BSTR;
pVar->bstrVal = NULL;
break;
case VT_BSTR:
break;
default:
HRESULT hr = VariantChangeType(pVar, pVar, 0, VT_BSTR);
if (FAILED(hr)) {
return E_INVALIDARG;
}
}
ASSERT(pVar->vt == VT_BSTR);
if (pVar->bstrVal) {
::SysFreeString(pVar->bstrVal);
}
if (cbSize) {
ASSERT(pbData);
pVar->bstrVal = ::SysAllocString(T2OLE(LPTSTR(pbData)));
} else {
pVar->bstrVal = NULL; // empty string
}
break;
case REG_MULTI_SZ:
switch(pVar->vt) {
case VT_EMPTY:
case VT_NULL:
pVar->vt = VT_BSTR_BLOB;
break;
case VT_VECTOR | VT_BSTR:
case VT_BSTR:
if (pVar->bstrVal) {
::SysFreeString(pVar->bstrVal);
}
pVar->vt = VT_BSTR_BLOB;
break;
default:
pVar->vt = VT_BSTR_BLOB;
}
if (cbSize) {
pVar->bstrVal = ::SysAllocStringByteLen(NULL, cbSize);
if (!pVar->bstrVal) {
return E_OUTOFMEMORY;
}
if (pbData) {
memcpy(pVar->bstrVal, pbData, cbSize);
}
}
break;
default: // binary
switch (pVar->vt) {
case VT_BSTR_BLOB:
case VT_BSTR:
if (pVar->bstrVal) {
::SysFreeString(pVar->bstrVal);
}
pVar->bstrVal = ::SysAllocStringByteLen(NULL, cbSize);
if (!pVar->bstrVal) {
return E_OUTOFMEMORY;
}
if (pbData) {
memcpy(pVar->bstrVal, pbData, cbSize);
}
break;
default:
if (pVar->vt != (VT_UI1 | VT_ARRAY)) {
HRESULT hr = VariantChangeType(pVar, pVar, 0, VT_UI1 | VT_ARRAY);
if (FAILED(hr)) {
return E_INVALIDARG;
}
}
ASSERT(pVar->vt == (VT_UI1 | VT_ARRAY));
SAFEARRAY * psa = NULL;
if (cbSize) {
ASSERT(pbData);
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = cbSize;
psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
if(!psa) {
return E_OUTOFMEMORY;
}
BYTE *pbArray;
HRESULT hr = SafeArrayAccessData(psa, reinterpret_cast<LPVOID *>(&pbArray));
if (hr != S_OK) {
return E_FAIL;
}
memcpy(pbArray, pbData, cbSize);
hr = SafeArrayUnaccessData(psa);
if (hr != S_OK) {
return E_FAIL;
}
}
pVar->parray = psa;
}
}
return NOERROR;
}
HRESULT CRegBagBase::SaveObject(CRegKey& hk, LPCOLESTR pszPropName, VARIANT* pV) {
ASSERT(hk.m_hKey && pszPropName && pV);
if (pV->vt != VT_UNKNOWN) {
return E_UNEXPECTED;
}
HRESULT hr = NOERROR;
USES_CONVERSION;
if (!pV->punkVal) {
hk.DeleteValue(OLE2CT(pszPropName));
hk.RecurseDeleteKey(OLE2CT(pszPropName));
} else {
PQPersistPropertyBag2 p2(pV->punkVal);
if (p2) {
CRegKey sk;
DWORD rc = sk.Create(m_hk, OLE2CT(pszPropName), NULL, 0, KEY_READ | KEY_WRITE, NULL, NULL);
if (rc != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(rc);
}
CLSID cl;
hr = p2->GetClassID(&cl);
if (FAILED(hr)) {
return E_UNEXPECTED;
}
OLECHAR szClsid[64];
rc = StringFromGUID2(cl, szClsid, sizeof(szClsid)/sizeof(OLECHAR));
if (!rc) {
return E_UNEXPECTED;
}
rc = RegSetValue(sk, NULL, REG_SZ, OLE2T(szClsid), _tcslen(OLE2T(szClsid)) * sizeof(TCHAR));
if (rc != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(rc);
}
try {
PQPropertyBag2 pBag2(new CRegBag(sk, NULL, 0, KEY_READ | KEY_WRITE));
if (pBag2) {
hr = p2->Save(pBag2, false, true);
} else {
hr = E_OUTOFMEMORY;
}
} CATCHCOM();
} else {
PQPersistPropertyBag p(pV->punkVal);
if (p) {
CRegKey sk;
DWORD rc = sk.Create(m_hk, OLE2CT(pszPropName), NULL, 0, KEY_READ | KEY_WRITE, NULL, NULL);
if (rc != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(rc);
}
CLSID cl;
hr = p->GetClassID(&cl);
if (FAILED(hr)) {
return E_UNEXPECTED;
}
OLECHAR szClsid[64];
rc = StringFromGUID2(cl, szClsid, sizeof(szClsid)/sizeof(OLECHAR));
if (!rc) {
return E_UNEXPECTED;
}
rc = RegSetValue(sk, NULL, REG_SZ, OLE2T(szClsid), _tcslen(OLE2T(szClsid)) * sizeof(TCHAR));
if (rc != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(rc);
}
try {
PQPropertyBag pBag(new CRegBag(sk, NULL, 0, KEY_READ | KEY_WRITE));
if (pBag) {
hr = p->Save(pBag, false, true);
} else {
hr = E_OUTOFMEMORY;
}
} CATCHCOM();
}
}
// rev2: support other persistence interfaces, esp stream via shopenregstream()
}
return hr;
}
// IPropertyBag
STDMETHODIMP CRegBagBase::Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog) {
if (!pszPropName || !pVar) {
return E_POINTER;
}
ATL_LOCK();
DWORD dwType, cbSize = 64;
BYTE data[64];
LPBYTE pbData = data;
USES_CONVERSION;
HRESULT hr = RegQueryValueEx(m_hk, OLE2CT(pszPropName), NULL, &dwType, pbData, &cbSize);
if (hr == ERROR_SUCCESS) {
return RegConvertToVARIANT(pVar, dwType, pbData, cbSize);
} else if (hr == ERROR_MORE_DATA) {
cbSize += sizeof(TCHAR);
pbData = new BYTE[cbSize];
hr = RegQueryValueEx(m_hk, OLE2CT(pszPropName), NULL, &dwType, pbData, &cbSize);
if (hr == ERROR_SUCCESS) {
hr = RegConvertToVARIANT(pVar, dwType, pbData, cbSize);
delete[] pbData;
return hr;
}
delete[] pbData;
}
// must be a key, so try the object
CRegKey sk;
hr = sk.Open(m_hk, OLE2CT(pszPropName), KEY_READ);
if (hr != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(hr);
}
TCHAR pszclsid[80 * sizeof(TCHAR)];
LONG dwSize = sizeof(pszclsid);
hr = RegQueryValue(sk, NULL, pszclsid, &dwSize);
if (hr != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(hr);
}
GUID clsid;
hr = CLSIDFromString(T2OLE(pszclsid), &clsid);
if (FAILED(hr)) {
return E_FAIL;
}
switch (pVar->vt) {
case VT_EMPTY:
case VT_NULL:
//DISPATCH is preferred if object supports it. if not we'll convert back
// to unknown down below.
pVar->vt = VT_DISPATCH;
pVar->pdispVal = NULL;
break;
case VT_DISPATCH:
case VT_UNKNOWN:
break;
default:
hr = VariantChangeType(pVar, pVar, 0, VT_DISPATCH);
if (FAILED(hr)) {
hr = VariantChangeType(pVar, pVar, 0, VT_UNKNOWN);
if (FAILED(hr)) {
return E_INVALIDARG;
}
}
}
PQPersistPropertyBag pPersistObj(((pVar->vt == VT_UNKNOWN) ? pVar->punkVal : pVar->pdispVal));
hr = LoadPersistedObject<PQPropertyBag, PQPersistPropertyBag> (pPersistObj, clsid, pVar, m_hk, pszPropName, pErrorLog);
if (FAILED(hr)) {
PQPersistPropertyBag2 pPersistObj2(((pVar->vt == VT_UNKNOWN) ? pVar->punkVal : pVar->pdispVal));
hr = LoadPersistedObject<PQPropertyBag2, PQPersistPropertyBag2> (pPersistObj2, clsid, pVar, m_hk, pszPropName, pErrorLog);
}
return hr;
}
STDMETHODIMP CRegBagBase::Write(LPCOLESTR pszPropName, VARIANT *pVar) {
if (!pszPropName || !pVar) {
return E_POINTER;
}
ATL_LOCK();
HRESULT hrc;
if (pVar->vt & VT_BYREF) {
hrc = VariantChangeType(pVar, pVar, 0, pVar->vt & ~VT_BYREF);
if (FAILED(hrc)) {
return E_INVALIDARG;
}
}
USES_CONVERSION;
hrc = NOERROR;
switch(pVar->vt) {
case VT_I1: //fall thru
case VT_I2: //fall thru
case VT_I4: //fall thru
case VT_UI1: //fall thru
case VT_UI2: //change type and fall thru
case VT_INT: //change type and fall thru
case VT_UINT: //change type and fall thru
case VT_BOOL: //change type and fall thru
hrc = VariantChangeType(pVar, pVar, 0, VT_UI4);
if (FAILED(hrc)) {
return E_INVALIDARG;
}
case VT_UI4: //REG_DWORD
hrc = RegSetValueEx(
m_hk,
OLE2CT(pszPropName),
0, // dwReserved
REG_DWORD,
reinterpret_cast<LPBYTE>(&pVar->ulVal),
sizeof(pVar->ulVal));
if (hrc != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(hrc);
}
// make sure no old object exists
DeleteSubKey(m_hk, pszPropName);
break;
case VT_BSTR: { //REG_SZ
hrc = ERROR_SUCCESS;
LPTSTR val = OLE2T(pVar->bstrVal);
if (val) {
UINT len = ::SysStringByteLen(pVar->bstrVal);
hrc = RegSetValueEx(
m_hk,
OLE2CT(pszPropName),
0, // dwReserved
REG_SZ,
reinterpret_cast<LPBYTE>(val),
len);
}
if (hrc == ERROR_SUCCESS) {
// make sure no old object exists
DeleteSubKey(m_hk, pszPropName);
}
} break;
#if 0
// we're not actually going to enable this since REG_MULTI_SZ only exists on NT
// if we had REG_MULTI_SZ on 9x then we'd have to loop over the hole block skipping embedded nulls and unicode/ansi convert the
// entire vector of strings
// instead we're just going to treat vectors of bstrs as binary blobs
case VT_VECTOR | VT_BSTR: { //REG_MULTI_SZ
hrc = ERROR_SUCCESS;
LPTSTR val = OLE2T(pVar->bstrVal);
if (val) {
UINT len = ::SysStringByteLen(pVar->bstrVal);
hrc = RegSetValueEx(
m_hk,
OLE2CT(pszPropName),
0, // dwReserved
REG_MULTI_SZ,
reinterpret_cast<LPBYTE>(val),
len);
}
if (hrc == ERROR_SUCCESS) {
// make sure no old object exists
DeleteSubKey(m_hk, pszPropName);
}
} break;
#else
case VT_VECTOR | VT_BSTR: // fall-thru to array(REG_BINARY)
#endif
case VT_BSTR_BLOB: { //REG_BINARY
SIZE_T len = 0;
LPBYTE pData = reinterpret_cast<LPBYTE>(pVar->bstrVal);
if (pData) {
len = ::SysStringByteLen(pVar->bstrVal);
hrc = RegSetValueEx(
m_hk,
OLE2CT(pszPropName),
0, // dwReserved
REG_BINARY,
pData,
len);
if (hrc == ERROR_SUCCESS) {
// make sure no old object exists
DeleteSubKey(m_hk, pszPropName);
}
}
} break;
case VT_ARRAY | VT_UI1: { //REG_BINARY
LPBYTE pData = NULL;
SIZE_T len = 0;
if (pVar->parray) {
HRESULT hr = SafeArrayAccessData(pVar->parray, reinterpret_cast<LPVOID *>(&pData));
if (FAILED(hr)) {
return hr;
}
for (int i = pVar->parray->cDims; i--;) {
len += pVar->parray->rgsabound[i].cElements;
}
len *= pVar->parray->cbElements;
hrc = RegSetValueEx(
m_hk,
OLE2CT(pszPropName),
0, // dwReserved
REG_BINARY,
pData,
len);
if (hrc == ERROR_SUCCESS) {
// make sure no old object exists
DeleteSubKey(m_hk, pszPropName);
}
SafeArrayUnaccessData(pVar->parray);
}
} break;
case VT_I8://change type and fall thru
hrc = VariantChangeType(pVar, pVar, 0, VT_UI8);
if (FAILED(hrc)) {
return E_INVALIDARG;
}
case VT_UI8: //REG_QWORD
hrc = RegSetValueEx(
m_hk,
OLE2CT(pszPropName),
0, // dwReserved
REG_QWORD,
reinterpret_cast<LPBYTE>(&pVar->ullVal),
sizeof(pVar->ullVal));
if (hrc == ERROR_SUCCESS) {
// make sure no old object exists
DeleteSubKey(m_hk, pszPropName);
}
break;
case VT_DISPATCH:
hrc = VariantChangeType(pVar, pVar, 0, VT_UNKNOWN);
if (FAILED(hrc)) {
return E_UNEXPECTED;
}
case VT_UNKNOWN:
DeleteValue(m_hk, pszPropName);
hrc = SaveObject(m_hk, pszPropName, pVar);
break;
case VT_EMPTY:
case VT_NULL:
// remove from registry
DeleteValue(m_hk, pszPropName);
DeleteSubKey(m_hk, pszPropName);
hrc = NOERROR;
break;
default:
hrc = E_INVALIDARG;
}
return hrc;
}
STDMETHODIMP CRegBag::CountProperties(ULONG * pcProperties) {
if (!pcProperties) {
return E_POINTER;
}
ATL_LOCK();
if (!pcProperties) {
return E_POINTER;
}
DWORD cKeys, cValues;
DWORD rc = RegQueryInfoKey(m_hk,
NULL, NULL, NULL,
&cKeys, NULL, NULL,
&cValues, NULL, NULL,
NULL, NULL);
if (rc != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(rc);
}
*pcProperties = cKeys + cValues;
return NOERROR;
}
STDMETHODIMP CRegBag::GetPropertyInfo(ULONG iProperty, ULONG cProperties, PROPBAG2* pPropBag, ULONG * pcProperties) {
if (!pPropBag || !pcProperties) {
return E_POINTER;
}
ATL_LOCK();
memset(pPropBag, 0, sizeof(*pPropBag) * cProperties);
// NOTE: since the registry functions don't provide a unified enumeration
// of subkeys and values, we're just going to establish values as coming
// before subkeys by definition.
DWORD cKeys, cValues, cbMaxKeyName, cbMaxValueName, cbMaxValue;
DWORD rc = RegQueryInfoKey(m_hk,
NULL, NULL, NULL,
&cKeys, &cbMaxKeyName, NULL,
&cValues, &cbMaxValueName, &cbMaxValue,
NULL, NULL);
if (rc != ERROR_SUCCESS) {
return HRESULT_FROM_WIN32(rc);
}
// nt doesn't return enough room for the terminating character
// but these are still char counts not byte counts yet.
++cbMaxKeyName;
++cbMaxValueName;
cbMaxKeyName *= sizeof(TCHAR);
cbMaxValueName *= sizeof(TCHAR);
// now they're real byte counts
DWORD dwValIndex = 0, dwBagIndex = 0;
USES_CONVERSION;
if (iProperty < cValues) {
LPTSTR pszName = new TCHAR[cbMaxValueName + 1];
// we're starting with values
for (;dwValIndex < cProperties; ++dwValIndex) {
DWORD Type;
DWORD cbName = cbMaxValueName + 1;
rc = RegEnumValue(m_hk, dwValIndex, pszName, &cbName, NULL, &Type, NULL, NULL);
if (rc != ERROR_SUCCESS) {
break;
}
if (dwValIndex < iProperty) {
continue; // skip until we get to first requested
}
switch (Type) {
case REG_DWORD:
pPropBag[dwBagIndex].dwType = PROPBAG2_TYPE_DATA;
pPropBag[dwBagIndex].vt = VT_UI4;
break;
case REG_QWORD:
pPropBag[dwBagIndex].dwType = PROPBAG2_TYPE_DATA;
pPropBag[dwBagIndex].vt = VT_UI8;
break;
case REG_SZ:
pPropBag[dwBagIndex].dwType = PROPBAG2_TYPE_DATA;
pPropBag[dwBagIndex].vt = VT_BSTR;
pPropBag[dwBagIndex].cfType = CF_TEXT;
break;
default: // binary
pPropBag[dwBagIndex].dwType = PROPBAG2_TYPE_DATA;
pPropBag[dwBagIndex].vt = VT_UI1 | VT_ARRAY;
break;
}
int len = sizeof(OLECHAR) * (_tcsclen(pszName) + 1);
pPropBag[dwBagIndex].pstrName = reinterpret_cast<LPOLESTR>(CoTaskMemAlloc(len + 1));
if (!pPropBag[dwBagIndex].pstrName) {
delete[] pszName;
return E_OUTOFMEMORY;
}
(void)StringCchCopy(pPropBag[dwBagIndex].pstrName, len + 1, T2OLE(pszName));
++dwBagIndex;
}
delete[] pszName;
}
DWORD dwKeyIndex = 0;
if (iProperty < cKeys + cValues) {
LPTSTR pszName = new TCHAR[cbMaxKeyName + 1];
for (; (dwKeyIndex + dwValIndex) < cProperties; ++dwKeyIndex) {
DWORD cbName = cbMaxKeyName + 1;
rc = RegEnumKeyEx(m_hk, dwKeyIndex, pszName, &cbName, NULL, NULL, NULL, NULL);
if (rc != ERROR_SUCCESS) {
break;
}
if ((dwValIndex + dwKeyIndex) < iProperty) {
continue;
}
pPropBag[dwBagIndex].dwType = PROPBAG2_TYPE_OBJECT;
pPropBag[dwBagIndex].vt = VT_UNKNOWN;
int len = sizeof(OLECHAR) * (_tcsclen(pszName) + 1);
pPropBag[dwBagIndex].pstrName = reinterpret_cast<LPOLESTR>(CoTaskMemAlloc(len + 1));
if (!pPropBag[dwBagIndex].pstrName) {
delete[] pszName;
return E_OUTOFMEMORY;
}
(void)StringCchCopy(pPropBag[dwBagIndex].pstrName, len + 1, T2OLE(pszName));
++dwBagIndex;
}
delete[] pszName;
}
*pcProperties = dwBagIndex;
return NOERROR;
}
STDMETHODIMP CRegBag::LoadObject(LPCOLESTR pstrName, ULONG dwHint, IUnknown * pUnkObject, IErrorLog * pErrLog) {
if (!pstrName || !pUnkObject) {
return E_POINTER;
}
VARIANT v; // don't clear the variant, we're guaranteed nested lifetimes and
// we're not addref'ing
v.vt = VT_UNKNOWN;
v.punkVal = pUnkObject;
return Read(pstrName, &v, pErrLog);
}
STDMETHODIMP CCreateRegBag::Create(HKEY hkey, LPCOLESTR subkey, DWORD options, DWORD sam, REFIID iid, LPVOID* ppBag) {
if (!ppBag) {
return E_POINTER;
}
ATL_LOCK();
if (ppBag == NULL)
return E_POINTER;
try {
USES_CONVERSION;
if (iid == __uuidof(IPropertyBag)) {
PQPropertyBag temp = new CRegBag(hkey, OLE2CT(subkey), options, sam);
*ppBag = temp.Detach();
} else if (iid == __uuidof(IPropertyBag2)) {
PQPropertyBag2 temp = new CRegBag(hkey, OLE2CT(subkey), options, sam);
*ppBag = temp.Detach();
} else {
return E_NOTIMPL;
}
if (!*ppBag) return E_OUTOFMEMORY;
return NOERROR;
} CATCHCOM();
}