// Copyright (c) 1999  Microsoft Corporation.  All Rights Reserved.
//
// regenum.cpp - registration/enumeration part of DMO runtime
//
#include <windows.h>
#include <tchar.h>
#include "dmoreg.h"
#include "guidenum.h"
#include "shlwapi.h"
#include "dmoutils.h"

#define DMO_REGISTRY_HIVE HKEY_CLASSES_ROOT
#define DMO_REGISTRY_PATH TEXT("DirectShow\\MediaObjects")

#define INPUT_TYPES_STR   "InputTypes"
#define OUTPUT_TYPES_STR  "OutputTypes"
#define SUBTYPES_STR      "Subtypes"
#define KEYED_STR         "Keyed"
#define CATEGORIES_STR    "Categories"

#ifndef CHARS_IN_GUID
#define CHARS_IN_GUID 39
#endif


//  Helper copied from shwapi

/*----------------------------------------------------------
Purpose: Recursively delete the key, including all child values
         and keys.  Mimics what RegDeleteKey does in Win95.

Returns:
Cond:    --
*/
DWORD
DeleteKeyRecursively(
    IN HKEY   hkey,
    IN LPCTSTR pszSubKey)
{
    DWORD dwRet;
    HKEY hkSubKey;

    // Open the subkey so we can enumerate any children
    dwRet = RegOpenKey(hkey, pszSubKey, &hkSubKey);
    if (ERROR_SUCCESS == dwRet)
    {
        DWORD   dwIndex;
        TCHAR   szSubKeyName[MAX_PATH + 1];
        DWORD   cchSubKeyName = sizeof(szSubKeyName) / sizeof(szSubKeyName[0]);
        TCHAR   szClass[MAX_PATH];
        DWORD   cbClass = sizeof(szClass) / sizeof(szClass[0]);

        // I can't just call RegEnumKey with an ever-increasing index, because
        // I'm deleting the subkeys as I go, which alters the indices of the
        // remaining subkeys in an implementation-dependent way.  In order to
        // be safe, I have to count backwards while deleting the subkeys.

        // Find out how many subkeys there are
        dwRet = RegQueryInfoKey(hkSubKey,
                                szClass,
                                &cbClass,
                                NULL,
                                &dwIndex, // The # of subkeys -- all we need
                                NULL,
                                NULL,
                                NULL,
                                NULL,
                                NULL,
                                NULL,
                                NULL);

        if (NO_ERROR == dwRet && dwIndex > 0)
        {
            // dwIndex is now the count of subkeys, but it needs to be
            // zero-based for RegEnumKey, so I'll pre-decrement, rather
            // than post-decrement.
            while (ERROR_SUCCESS == RegEnumKey(hkSubKey, --dwIndex, szSubKeyName, cchSubKeyName))
            {
                DeleteKeyRecursively(hkSubKey, szSubKeyName);
            }
        }

        RegCloseKey(hkSubKey);

        dwRet = RegDeleteKey(hkey, pszSubKey);
    }

    return dwRet;
}

// Automatically calls RegCloseKey when leaving scope
class CAutoHKey {
public:
   CAutoHKey() : m_hKey(NULL) {}
   ~CAutoHKey() {
       if (m_hKey)
           RegCloseKey(m_hKey);
   }
   LRESULT Create(HKEY hKey, LPCTSTR szSubKey)
   {
       return RegCreateKey(hKey, szSubKey, &m_hKey);
   }
   LRESULT Open(HKEY hKey, LPCTSTR szSubKey)
   {
       return RegOpenKey(hKey, szSubKey, &m_hKey);
   }
   void Close()
   {
       if (m_hKey) {
           RegCloseKey(m_hKey);
       }
       m_hKey = NULL;
   }
   HKEY m_hKey;
   HKEY Key() const { return m_hKey; }
};

// Automatically calls RegCloseKey when leaving scope
class CAutoCreateHKey {
public:
   CAutoCreateHKey(HKEY hKey, TCHAR* szSubKey, HKEY *phKey) {
      if (RegCreateKeyEx(hKey,
                         szSubKey,
                         0,
                         TEXT(""),
                         REG_OPTION_NON_VOLATILE,
                         MAXIMUM_ALLOWED,
                         NULL,
                         phKey,
                         NULL) != ERROR_SUCCESS)
         m_hKey = *phKey = NULL;
      else
         m_hKey = *phKey;
   }
   ~CAutoCreateHKey() {
      if (m_hKey)
         RegCloseKey(m_hKey);
   }
   HKEY m_hKey;
};

class CAutoOpenHKey {
public:
   CAutoOpenHKey(HKEY hKey, TCHAR* szSubKey, HKEY *phKey, REGSAM samDesired = KEY_READ) {
      if (RegOpenKeyEx(hKey,
                       szSubKey,
                       0,
                       samDesired,
                       phKey) != ERROR_SUCCESS)
         m_hKey = *phKey = NULL;
      else
         m_hKey = *phKey;
   }
   ~CAutoOpenHKey() {
      if (m_hKey)
         RegCloseKey(m_hKey);
   }
   HKEY m_hKey;
};


HRESULT ReadTypesFromKeys(HKEY hkDMO, LPCTSTR pszTypes, DWORD *pcbData, PVOID *ppvData)
{
    //  Collect all the types into 1 value - need to enumerate
    //  keys and subkeys
    LPVOID pMem = CoTaskMemAlloc(0);
    unsigned int nEntries = 0;
    DWORD dwTypeIndex;
    BOOL bSuccess = TRUE;
    DMO_PARTIAL_MEDIATYPE Type;
    CAutoHKey hkSrc;
    if (NOERROR != hkSrc.Open(hkDMO, pszTypes)) {
        bSuccess = FALSE;
    }
    for (dwTypeIndex = 0; bSuccess; dwTypeIndex++) {
        TCHAR szType[MAX_PATH];
        LONG lResult = RegEnumKey(hkSrc.Key(), dwTypeIndex, szType, MAX_PATH);
        if (NOERROR != lResult) {
            if (ERROR_NO_MORE_ITEMS != lResult) {
                bSuccess = FALSE;
            }
            break;
        }
        if (DMOStrToGuid(szType, &Type.type)) {
            CAutoHKey kType;
            kType.Open(hkSrc.Key(), szType);
            if (NULL == kType.Key()) {
                bSuccess = FALSE;
            } else {
                DWORD dwSubtypeIndex;
                for (dwSubtypeIndex = 0; bSuccess; dwSubtypeIndex++) {
                    TCHAR szSubtype[MAX_PATH];
                    lResult = RegEnumKey(kType.Key(), dwSubtypeIndex, szSubtype, MAX_PATH);
                    if (NOERROR != lResult) {
                        if (ERROR_NO_MORE_ITEMS != lResult) {
                            bSuccess = FALSE;
                        }
                        break;
                    }
                    if (DMOStrToGuid(szSubtype, &Type.subtype)) {
                        //  Add to our list
                        LPVOID pMemNew = CoTaskMemRealloc(pMem,
                                            (nEntries + 1) * sizeof(DMO_PARTIAL_MEDIATYPE));
                        if (NULL == pMemNew) {
                            bSuccess = FALSE;
                        } else {
                            pMem = pMemNew;
                            CopyMemory((LPBYTE)pMem +
                                        nEntries * sizeof(DMO_PARTIAL_MEDIATYPE),
                                        &Type,
                                        sizeof(DMO_PARTIAL_MEDIATYPE));
                            nEntries++;
                        }
                    }
                }
            }
        }
    }
    if (bSuccess && nEntries != 0) {
        *ppvData = pMem;
        *pcbData = nEntries * sizeof(DMO_PARTIAL_MEDIATYPE);
        return S_OK;
    } else {
        CoTaskMemFree(pMem);
        return S_FALSE;
    }
}

HRESULT ReadTypes(HKEY hkDMO, LPCTSTR pszTypes, DWORD *pcbData, PVOID *ppvData)
{
    *pcbData = 0;

    //  Try reading the value first
    DWORD cbData;
    if (NOERROR != RegQueryValueEx(hkDMO, pszTypes, NULL, NULL, NULL, &cbData)) {
        return ReadTypesFromKeys(hkDMO, pszTypes, pcbData, ppvData);
    }
    if (cbData == 0) {
        return S_OK;
    }
    PVOID pvData = (PBYTE)CoTaskMemAlloc(cbData);
    if (NULL == pvData) {
        return E_OUTOFMEMORY;
    }
    if (NOERROR == RegQueryValueEx(hkDMO, pszTypes, NULL, NULL, (PBYTE)pvData, &cbData)) {
        *ppvData = pvData;
        *pcbData = cbData;
        return S_OK;
    } else {
        CoTaskMemFree(pvData);
        return E_OUTOFMEMORY;
    }
}



/////////////////////////////////////////////////////////////////////////////
//
// DMO Registration code
//

// Registration helper
void CreateObjectGuidKey(HKEY hKey, REFCLSID clsidDMO) {
   TCHAR szSubkeyName[80];

   HKEY hObjectGuidKey;
   DMOGuidToStr(szSubkeyName, clsidDMO);
   CAutoCreateHKey kGuid(hKey, szSubkeyName, &hObjectGuidKey);
}

// Registration helper
// Registers types\subtypes underneath the object's key
void RegisterTypes(HKEY hObjectKey,
                   TCHAR* szInputOrOutput,
                   ULONG ulTypes,
                   const DMO_PARTIAL_MEDIATYPE* pTypes) {
    RegSetValueEx(hObjectKey, szInputOrOutput, 0, REG_BINARY,
                  (const BYTE *)pTypes,
                  ulTypes * sizeof(DMO_PARTIAL_MEDIATYPE));
}

//
// Public entry point
//
STDAPI DMORegister(
   LPCWSTR szName,
   REFCLSID clsidDMO,
   REFGUID guidCategory,
   DWORD dwFlags, // DMO_REGISTERF_XXX
   unsigned long ulInTypes,
   const DMO_PARTIAL_MEDIATYPE *pInTypes,
   unsigned long ulOutTypes,
   const DMO_PARTIAL_MEDIATYPE *pOutTypes
) {
   TCHAR szSubkeyName[80];
   if ((clsidDMO == GUID_NULL) || (guidCategory == GUID_NULL))
      return E_INVALIDARG;

   // Create/open the main DMO key
   HKEY hMainKey;
   CAutoCreateHKey kMain(DMO_REGISTRY_HIVE, DMO_REGISTRY_PATH, &hMainKey);
   if (hMainKey == NULL)
      return E_FAIL;

   HKEY hCategoriesKey;
   CAutoCreateHKey kCats(hMainKey, TEXT(CATEGORIES_STR), &hCategoriesKey);
   if (hCategoriesKey == NULL)
      return E_FAIL;

   // Create/open the category specific subkey underneath the main key
   DMOGuidToStr(szSubkeyName, guidCategory);
   HKEY hCategoryKey;
   CAutoCreateHKey kCat(hCategoriesKey, szSubkeyName, &hCategoryKey);
   if (hCategoryKey == NULL)
      return E_FAIL;

   //  Deletet the redundant old types keys
   DeleteKeyRecursively(hCategoryKey, TEXT(INPUT_TYPES_STR));
   DeleteKeyRecursively(hCategoryKey, TEXT(OUTPUT_TYPES_STR));

   // If the category key does not have a name yet, add one
   DWORD cbName;
   DWORD dwType;

   if ((RegQueryValueEx(hCategoryKey, NULL, NULL, &dwType, NULL, &cbName) != ERROR_SUCCESS) ||
       (cbName <= sizeof(TCHAR)) || (REG_SZ != dwType)) {
      TCHAR* szName;
      if (guidCategory == DMOCATEGORY_AUDIO_DECODER)
         szName = TEXT("Audio decoders");
      else if (guidCategory == DMOCATEGORY_AUDIO_ENCODER)
         szName = TEXT("Audio encoders");
      else if (guidCategory == DMOCATEGORY_VIDEO_DECODER)
         szName = TEXT("Video decoders");
      else if (guidCategory == DMOCATEGORY_VIDEO_ENCODER)
         szName = TEXT("Video encoders");
      else if (guidCategory == DMOCATEGORY_AUDIO_EFFECT)
         szName = TEXT("Audio effects");
      else if (guidCategory == DMOCATEGORY_VIDEO_EFFECT)
         szName = TEXT("Video effects");
      else if (guidCategory == DMOCATEGORY_AUDIO_CAPTURE_EFFECT)
         szName = TEXT("Audio capture effects");
     else if (guidCategory == DMOCATEGORY_ACOUSTIC_ECHO_CANCEL)
         szName = TEXT("Acoustic Echo Canceller");
      else if (guidCategory == DMOCATEGORY_AUDIO_NOISE_SUPPRESS)
         szName = TEXT("Audio Noise Suppressor");
      else if (guidCategory == DMOCATEGORY_AGC)
         szName = TEXT("Automatic Gain Control");
      else
         szName = TEXT("Unknown DMO category");
      RegSetValue(hCategoryKey, NULL, REG_SZ, szName, lstrlen(szName) * sizeof(TCHAR));
   }

   // Create/open the object specific key underneath the category key
   DMOGuidToStr(szSubkeyName, clsidDMO);

   //  Remove the old one
   DeleteKeyRecursively(hMainKey, szSubkeyName);

   HKEY hObjKey;
   CAutoCreateHKey kObj(hCategoryKey, szSubkeyName, &hObjKey);
   if (hObjKey == NULL)
      return E_FAIL;

   // Create/open the object specific key underneath the main key
   DMOGuidToStr(szSubkeyName, clsidDMO); // BUGBUG: redundant
   HKEY hObjectKey;
   CAutoCreateHKey kObject(hMainKey, szSubkeyName, &hObjectKey);
   if (hObjectKey == NULL)
      return E_FAIL;

   // set the default value of the object key to the name of the DMO
#ifdef UNICODE
   LPCWSTR sz = szName;
#else
   char sz[80];
   WideCharToMultiByte(0,0,szName,-1,sz,80,NULL,NULL);
#endif
   if (RegSetValue(hObjectKey, NULL, REG_SZ, sz, lstrlen(sz) * sizeof(TCHAR))
        != ERROR_SUCCESS)
      return E_FAIL;

   // If the object is keyed, add a registry value indicating so
   if (dwFlags & DMO_REGISTERF_IS_KEYED) {
      if (RegSetValue(hObjectKey, TEXT(KEYED_STR), REG_SZ, TEXT(""), 0)
            != ERROR_SUCCESS)
         return E_FAIL;
   }

   // Register types
   if (ulInTypes) {
      RegisterTypes(hObjectKey,   TEXT(INPUT_TYPES_STR), ulInTypes, pInTypes);
   }

   if (ulOutTypes) {
      RegisterTypes(hObjectKey,   TEXT(OUTPUT_TYPES_STR),ulOutTypes,pOutTypes);
   }

   // Nuke the DShow filter cache
   DeleteKeyRecursively(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Multimedia\\ActiveMovie\\Filter Cache"));

   return NOERROR;
}

// helper
void MakeSubkeyName (TCHAR* szSubkeyName,
                     REFGUID guidCategory,
                     REFCLSID clsidDMO) {
   DMOGuidToStr(szSubkeyName, guidCategory);
   _tcscat(szSubkeyName, TEXT("\\"));
   DMOGuidToStr(szSubkeyName + lstrlen(szSubkeyName), clsidDMO);
}


//
// Public entry point
//
STDAPI DMOUnregister(
   REFCLSID clsidDMO,
   REFGUID guidCategory
) {
   HRESULT hr;

   // open the root DMO key
   HKEY hMainKey;
   CAutoOpenHKey kMain(DMO_REGISTRY_HIVE, DMO_REGISTRY_PATH, &hMainKey, MAXIMUM_ALLOWED);
   if (hMainKey == NULL)
      return E_FAIL;

   // open the "Categories" key underneath the root key
   HKEY hCategoriesKey;
   CAutoOpenHKey kCats(hMainKey, TEXT(CATEGORIES_STR), &hCategoriesKey, MAXIMUM_ALLOWED);
   if (hCategoriesKey == NULL)
      return E_FAIL;

   // Iterate through all categories attempting to delete from each one
   TCHAR szCategory[80];
   DWORD dwIndex = 0;
   BOOL bDeletedAnything = FALSE;
   BOOL bDeletedAll = TRUE;
   DMOGuidToStr(szCategory, guidCategory);

   while (RegEnumKey(hCategoriesKey, dwIndex, szCategory, 80) == ERROR_SUCCESS) {

      // process the subkey only if it resembles a category GUID
      GUID guid;
      if (DMOStrToGuid(szCategory, &guid)) {

         // Try to delete from this category
         TCHAR szSubkeyName[256];
         MakeSubkeyName(szSubkeyName, guid, clsidDMO);
         if (guidCategory == GUID_NULL || guid == guidCategory) {
         if (DeleteKeyRecursively(hCategoriesKey, szSubkeyName) == ERROR_SUCCESS)
            bDeletedAnything = TRUE;
         } else {
             CAutoHKey hk;
             if (ERROR_FILE_NOT_FOUND != hk.Open(hCategoriesKey, szSubkeyName)) {
                 bDeletedAll = FALSE;
             }
         }
      }
      dwIndex++;
   }

   if (bDeletedAnything) {
      hr = S_OK;
      if (bDeletedAll) {
         // Now delete this object's key from underneath the root DMO key
         TCHAR szGuid[CHARS_IN_GUID];
         DMOGuidToStr(szGuid, clsidDMO);
         if (DeleteKeyRecursively(hMainKey, szGuid) != ERROR_SUCCESS) {
             hr = S_FALSE;
         }
      }
   }
   else
      hr = S_FALSE;

   return hr;

}
//
// End DMO Registration code
//
/////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////
//
// DMO Enumeration code
// Some of it leaves room for future improvements in terms of speed
//

// helper
HRESULT ReadName(HKEY hDMOKey, WCHAR szName[80]) {
   LONG cbSize = 80;
#ifdef UNICODE
   if (RegQueryValue(hDMOKey, NULL, szName, &cbSize) == ERROR_SUCCESS)
      return S_OK;
#else
   char szTmp[80];
   if (RegQueryValue(hDMOKey, NULL, szTmp, &cbSize) == ERROR_SUCCESS) {
      MultiByteToWideChar(0,0,szTmp,-1,szName,80);
      return S_OK;
   }
#endif
   else {
      szName[0] = L'\0'; // no name - corrupt registry ?
      return S_FALSE;
   }
}

// Enumeration helper, does what the name says
void LookupNameAndAddToEnum(HKEY hObjectKey,
                            TCHAR* szGuid,
                            DWORD dwFlags,
                            REFCLSID clsidDMO,
                            CEnumDMOCLSID* pEnum) {
   // Skip keyed DMOs unless explicitly asked to include them
   if (!(dwFlags & DMO_ENUMF_INCLUDE_KEYED)) {
      // open the DMO's registry key
      LONG cbValue;
      if (RegQueryValue(hObjectKey, TEXT(KEYED_STR), NULL, &cbValue)
           == ERROR_SUCCESS)
         return; // DMO is keyed - skip
   }

   WCHAR szName[80];
   if (FAILED(ReadName(hObjectKey, szName)))
      szName[0] = L'\0';

   pEnum->Add(clsidDMO, szName);
}


//  Check if any of the requested types match
//  If no requested types are specified then this is treated
//  as a match
BOOL CompareTypes(HKEY hkDMO,
                  unsigned long ulTypes,
                  const DMO_PARTIAL_MEDIATYPE *pTypes,
                  LPCTSTR pszTypesValue)
{
    if (ulTypes == 0) {
        return TRUE;
    }
    DWORD cbData;
    PVOID pvDMOTypes = NULL;
    if (S_OK == ReadTypes(hkDMO, pszTypesValue, &cbData, &pvDMOTypes)) {
        for (unsigned long ulType = 0; ulType < ulTypes; ulType++) {
            DMO_PARTIAL_MEDIATYPE *pDMOTypes = (DMO_PARTIAL_MEDIATYPE *)pvDMOTypes;
            while ((PBYTE)(pDMOTypes + 1) <= (PBYTE)pvDMOTypes + cbData) {
                if (pDMOTypes->type == pTypes[ulType].type ||
                    pDMOTypes->type == GUID_NULL ||
                    pTypes[ulType].type == GUID_NULL) {
                    if (pTypes[ulType].subtype == GUID_NULL ||
                        pDMOTypes->subtype == GUID_NULL ||
                        pTypes[ulType].subtype == pDMOTypes->subtype) {
                        CoTaskMemFree(pvDMOTypes);
                        return TRUE;
                    }
                }
                pDMOTypes++;
            }
        }
    }
    CoTaskMemFree(pvDMOTypes);
    return FALSE;
}

// Enumeration helper
HRESULT EnumerateDMOs(HKEY hMainKey,
                      HKEY hCatKey,
                      DWORD dwFlags,
                      unsigned long ulInputTypes,
                      const DMO_PARTIAL_MEDIATYPE *pInputTypes,
                      unsigned long ulOutputTypes,
                      const DMO_PARTIAL_MEDIATYPE *pOutputTypes,
                      CEnumDMOCLSID *pEnum) {
    DWORD dwIndex = 0;
    TCHAR szSubkey[80];
    while (RegEnumKey(hCatKey, dwIndex, szSubkey, 80) == ERROR_SUCCESS) {
        // Does this look like an object CLSID ?
        CLSID clsidDMO;
        if (DMOStrToGuid(szSubkey, &clsidDMO)) {
            // Do the type match?
            CAutoHKey hkDMO;
            if (NOERROR == hkDMO.Open(hMainKey, szSubkey)) {
                if (CompareTypes(hkDMO.Key(), ulInputTypes, pInputTypes, TEXT(INPUT_TYPES_STR)) &&
                    CompareTypes(hkDMO.Key(), ulOutputTypes, pOutputTypes, TEXT(OUTPUT_TYPES_STR))) {
                    LookupNameAndAddToEnum(hkDMO.Key(), szSubkey, dwFlags, clsidDMO, pEnum);
                }
            }
        }
        dwIndex++;
    }
    return S_OK;
}
//
// Public entry point
//
STDAPI DMOEnum(
   REFGUID guidCategory, // GUID_NULL for "all"
   DWORD dwFlags, // DMO_ENUMF_XXX
   unsigned long ulInTypes,
   const DMO_PARTIAL_MEDIATYPE *pInTypes, // can be NULL only of ulInTypes = 0
   unsigned long ulOutTypes,
   const DMO_PARTIAL_MEDIATYPE *pOutTypes,// can be NULL only of ulOutTypes = 0
   IEnumDMO **ppEnum
) {
    if (ppEnum == NULL) {
        return E_POINTER;
    }
    if (ulInTypes > 0 && pInTypes == NULL ||
        ulOutTypes > 0 && pOutTypes == NULL) {
        return E_INVALIDARG;
    }

    *ppEnum = NULL;

    // open the root key
    CAutoHKey kMain;
    kMain.Open(DMO_REGISTRY_HIVE, DMO_REGISTRY_PATH);
    if (kMain.Key() == NULL)
        return E_FAIL;

    CEnumDMOCLSID *pEnum = new CEnumDMOCLSID();
    if (!pEnum)
        return E_OUTOFMEMORY;

    HRESULT hr = S_OK;

    if (guidCategory == GUID_NULL) {

        hr = EnumerateDMOs(kMain.Key(),
                           kMain.Key(),
                           dwFlags,
                           ulInTypes,
                           pInTypes,
                           ulOutTypes,
                           pOutTypes,
                           pEnum);
    } else {

        // open the subkey for the specified category and enumerate its subkeys
        TCHAR szCategory[CHARS_IN_GUID];
        TCHAR szCategoryPath[MAX_PATH];
        DMOGuidToStr(szCategory, guidCategory);
        wsprintf(szCategoryPath, TEXT(CATEGORIES_STR) TEXT("\\%s"), szCategory);
        CAutoHKey key2;
        key2.Open(kMain.Key(), szCategoryPath);
        if (key2.Key()) {
            hr = EnumerateDMOs(kMain.Key(),
                               key2.Key(),
                               dwFlags,
                               ulInTypes,
                               pInTypes,
                               ulOutTypes,
                               pOutTypes,
                               pEnum);
        }
    }

    if (SUCCEEDED(hr)) {
        *ppEnum = (IEnumDMO*) pEnum;
        hr = S_OK;
    } else {
        delete pEnum;
    }
    return hr;
}
//  Copy the type information
HRESULT FetchTypeInfo(HKEY hObjKey, LPCTSTR pszTypesValue,
                      unsigned long ulTypesRequested,
                      unsigned long *pulTypesSupplied,
                      DMO_PARTIAL_MEDIATYPE *pTypes)
{
    DWORD cbData;
    unsigned long ulTypesCopied = 0;
    PVOID pvData;
    if (S_OK == ReadTypes(hObjKey, pszTypesValue, &cbData, &pvData)) {
        ulTypesCopied =
                min(ulTypesRequested, cbData / sizeof(DMO_PARTIAL_MEDIATYPE));
        CopyMemory(pTypes, pvData,
                   ulTypesCopied * sizeof(DMO_PARTIAL_MEDIATYPE));
        CoTaskMemFree(pvData);
    }
    *pulTypesSupplied = ulTypesCopied;
    return ulTypesCopied != 0 ? S_OK : S_FALSE;
}

// Mediatype helper
HRESULT FetchMediatypeInfo(HKEY hObjKey,
                           unsigned long ulInputTypesRequested,
                           unsigned long *pulInputTypesSupplied,
                           DMO_PARTIAL_MEDIATYPE *pInputTypes,
                           unsigned long ulOutputTypesRequested,
                           unsigned long *pulOutputTypesSupplied,
                           DMO_PARTIAL_MEDIATYPE *pOutputTypes) {

   HRESULT hr1 = S_OK;
   if (ulInputTypesRequested) {
      hr1 = FetchTypeInfo(hObjKey,
                          TEXT(INPUT_TYPES_STR),
                          ulInputTypesRequested,
                          pulInputTypesSupplied,
                          pInputTypes);
   } else {
       *pulInputTypesSupplied = 0;
   }
   HRESULT hr2 = S_OK;
   if (ulOutputTypesRequested) {
      hr2 = FetchTypeInfo(hObjKey,
                          TEXT(OUTPUT_TYPES_STR),
                          ulOutputTypesRequested,
                          pulOutputTypesSupplied,
                          pOutputTypes);
   } else {
       *pulOutputTypesSupplied = 0;
   }
   if ((hr1 == S_OK) && (hr2 == S_OK))
      return S_OK;
   else
      return S_FALSE;
}

//
// Public entry point
//
STDAPI DMOGetTypes(
   REFCLSID clsidDMO,
   unsigned long ulInputTypesRequested,
   unsigned long *pulInputTypesSupplied,
   DMO_PARTIAL_MEDIATYPE *pInputTypes,
   unsigned long ulOutputTypesRequested,
   unsigned long *pulOutputTypesSupplied,
   DMO_PARTIAL_MEDIATYPE *pOutputTypes
) {
   // open the DMO root registry key
   HKEY hMainKey;
   CAutoOpenHKey kMain(DMO_REGISTRY_HIVE, DMO_REGISTRY_PATH, &hMainKey);
   if (hMainKey == NULL)
      return E_FAIL;

   // open the object specific guid key
   TCHAR szGuid[80];
   DMOGuidToStr(szGuid, clsidDMO);
   HKEY hObjKey;
   CAutoOpenHKey kObj(hMainKey, szGuid, &hObjKey);
   if (!hObjKey)
      return E_FAIL;

   return FetchMediatypeInfo(hObjKey,
                             ulInputTypesRequested,
                             pulInputTypesSupplied,
                             pInputTypes,
                             ulOutputTypesRequested,
                             pulOutputTypesSupplied,
                             pOutputTypes);
}


STDAPI DMOGetName(REFCLSID clsidDMO, WCHAR szName[80]) {
   // open the DMO root registry key
   HKEY hMainKey;
   CAutoOpenHKey kMain(DMO_REGISTRY_HIVE, DMO_REGISTRY_PATH, &hMainKey);
   if (hMainKey == NULL)
      return E_FAIL;

   // open the object specific guid key
   TCHAR szGuid[80];
   DMOGuidToStr(szGuid, clsidDMO);
   HKEY hObjKey;
   CAutoOpenHKey kObj(hMainKey, szGuid, &hObjKey);
   if (!hObjKey)
      return E_FAIL;

   return ReadName(hObjKey, szName);
}

//
// End DMO Enumeration code
//
/////////////////////////////////////////////////////////////////////////////