mfplat: Implement MFTEnumEx().

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
feature/deterministic
Nikolay Sivov 2020-03-03 12:22:42 +03:00 committed by Alexandre Julliard
parent be08f2c203
commit c0b1d7b593
4 changed files with 396 additions and 91 deletions

View File

@ -85,7 +85,7 @@ static CRITICAL_SECTION local_handlers_section = { NULL, -1, 0, 0, 0, 0 };
static struct list local_scheme_handlers = LIST_INIT(local_scheme_handlers);
static struct list local_bytestream_handlers = LIST_INIT(local_bytestream_handlers);
struct local_mft
struct mft_registration
{
struct list entry;
IClassFactory *factory;
@ -107,6 +107,7 @@ struct transform_activate
{
struct attributes attributes;
IMFActivate IMFActivate_iface;
IClassFactory *factory;
};
struct system_clock
@ -194,6 +195,8 @@ static ULONG WINAPI transform_activate_Release(IMFActivate *iface)
if (!refcount)
{
clear_attributes_object(&activate->attributes);
if (activate->factory)
IClassFactory_Release(activate->factory);
heap_free(activate);
}
@ -537,13 +540,11 @@ static const IMFActivateVtbl transform_activate_vtbl =
transform_activate_DetachObject,
};
HRESULT WINAPI MFCreateTransformActivate(IMFActivate **activate)
static HRESULT create_transform_activate(IClassFactory *factory, IMFActivate **activate)
{
struct transform_activate *object;
HRESULT hr;
TRACE("%p.\n", activate);
object = heap_alloc_zero(sizeof(*object));
if (!object)
return E_OUTOFMEMORY;
@ -555,12 +556,22 @@ HRESULT WINAPI MFCreateTransformActivate(IMFActivate **activate)
}
object->IMFActivate_iface.lpVtbl = &transform_activate_vtbl;
object->factory = factory;
if (object->factory)
IClassFactory_AddRef(object->factory);
*activate = &object->IMFActivate_iface;
return S_OK;
}
HRESULT WINAPI MFCreateTransformActivate(IMFActivate **activate)
{
TRACE("%p.\n", activate);
return create_transform_activate(NULL, activate);
}
static const WCHAR transform_keyW[] = {'M','e','d','i','a','F','o','u','n','d','a','t','i','o','n','\\',
'T','r','a','n','s','f','o','r','m','s',0};
static const WCHAR categories_keyW[] = {'M','e','d','i','a','F','o','u','n','d','a','t','i','o','n','\\',
@ -648,7 +659,7 @@ static BOOL GUIDFromString(LPCWSTR s, GUID *id)
id->Data4[(i-19)/2] = guid_conv_table[s[i]] << 4 | guid_conv_table[s[i+1]];
}
if (!s[37]) return TRUE;
if (!s[36]) return TRUE;
return FALSE;
}
@ -774,7 +785,7 @@ HRESULT WINAPI MFTRegister(CLSID clsid, GUID category, LPWSTR name, UINT32 flags
return hr;
}
static void release_local_mft(struct local_mft *mft)
static void release_mft_registration(struct mft_registration *mft)
{
if (mft->factory)
IClassFactory_Release(mft->factory);
@ -788,7 +799,7 @@ static HRESULT mft_register_local(IClassFactory *factory, REFCLSID clsid, REFGUI
UINT32 input_count, const MFT_REGISTER_TYPE_INFO *input_types, UINT32 output_count,
const MFT_REGISTER_TYPE_INFO *output_types)
{
struct local_mft *mft, *cur, *unreg_mft = NULL;
struct mft_registration *mft, *cur, *unreg_mft = NULL;
HRESULT hr;
if (!factory && !clsid)
@ -807,7 +818,7 @@ static HRESULT mft_register_local(IClassFactory *factory, REFCLSID clsid, REFGUI
if (clsid)
mft->clsid = *clsid;
mft->category = *category;
mft->flags = flags;
mft->flags = flags | MFT_ENUM_FLAG_LOCALMFT;
if (FAILED(hr = heap_strdupW(name, &mft->name)))
goto failed;
@ -835,7 +846,7 @@ static HRESULT mft_register_local(IClassFactory *factory, REFCLSID clsid, REFGUI
EnterCriticalSection(&local_mfts_section);
LIST_FOR_EACH_ENTRY(cur, &local_mfts, struct local_mft, entry)
LIST_FOR_EACH_ENTRY(cur, &local_mfts, struct mft_registration, entry)
{
if (cur->factory == factory)
{
@ -849,11 +860,11 @@ static HRESULT mft_register_local(IClassFactory *factory, REFCLSID clsid, REFGUI
LeaveCriticalSection(&local_mfts_section);
if (unreg_mft)
release_local_mft(unreg_mft);
release_mft_registration(unreg_mft);
failed:
if (FAILED(hr))
release_local_mft(mft);
release_mft_registration(mft);
return hr;
}
@ -880,7 +891,7 @@ HRESULT WINAPI MFTRegisterLocalByCLSID(REFCLSID clsid, REFGUID category, LPCWSTR
static HRESULT mft_unregister_local(IClassFactory *factory, REFCLSID clsid)
{
struct local_mft *cur, *cur2;
struct mft_registration *cur, *cur2;
BOOL unregister_all = !factory && !clsid;
struct list unreg;
@ -888,7 +899,7 @@ static HRESULT mft_unregister_local(IClassFactory *factory, REFCLSID clsid)
EnterCriticalSection(&local_mfts_section);
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &local_mfts, struct local_mft, entry)
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &local_mfts, struct mft_registration, entry)
{
if (!unregister_all)
{
@ -911,10 +922,10 @@ static HRESULT mft_unregister_local(IClassFactory *factory, REFCLSID clsid)
if (!unregister_all && list_empty(&unreg))
return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &unreg, struct local_mft, entry)
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &unreg, struct mft_registration, entry)
{
list_remove(&cur->entry);
release_local_mft(cur);
release_mft_registration(cur);
}
return S_OK;
@ -945,135 +956,411 @@ MFTIME WINAPI MFGetSystemTime(void)
return mf;
}
static BOOL match_type(const WCHAR *clsid_str, const WCHAR *type_str, MFT_REGISTER_TYPE_INFO *type)
static BOOL mft_is_type_info_match(struct mft_registration *mft, const GUID *category, UINT32 flags,
IMFPluginControl *plugin_control, const MFT_REGISTER_TYPE_INFO *input_type,
const MFT_REGISTER_TYPE_INFO *output_type)
{
HKEY htransform, hfilter;
DWORD reg_type, size;
LONG ret = FALSE;
MFT_REGISTER_TYPE_INFO *info = NULL;
BOOL matching = TRUE;
DWORD model;
int i;
if (RegOpenKeyW(HKEY_CLASSES_ROOT, transform_keyW, &htransform))
if (!IsEqualGUID(category, &mft->category))
return FALSE;
if (RegOpenKeyW(htransform, clsid_str, &hfilter))
/* Default model is synchronous. */
model = mft->flags & (MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT | MFT_ENUM_FLAG_HARDWARE);
if (!model)
model = MFT_ENUM_FLAG_SYNCMFT;
if (!(model & flags & (MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT | MFT_ENUM_FLAG_HARDWARE)))
return FALSE;
/* These flags should be explicitly enabled. */
if (mft->flags & ~flags & (MFT_ENUM_FLAG_FIELDOFUSE | MFT_ENUM_FLAG_TRANSCODE_ONLY))
return FALSE;
if (flags & MFT_ENUM_FLAG_SORTANDFILTER && !mft->factory && plugin_control
&& IMFPluginControl_IsDisabled(plugin_control, MF_Plugin_Type_MFT, &mft->clsid) == S_OK)
{
RegCloseKey(htransform);
return FALSE;
}
if (RegQueryValueExW(hfilter, type_str, NULL, &reg_type, NULL, &size) != ERROR_SUCCESS)
if (input_type)
{
for (i = 0, matching = FALSE; input_type && i < mft->input_types_count; ++i)
{
if (!memcmp(&mft->input_types[i], input_type, sizeof(*input_type)))
{
matching = TRUE;
break;
}
}
}
if (output_type && matching)
{
for (i = 0, matching = FALSE; i < mft->output_types_count; ++i)
{
if (!memcmp(&mft->output_types[i], output_type, sizeof(*output_type)))
{
matching = TRUE;
break;
}
}
}
return matching;
}
static void mft_get_reg_type_info(const WCHAR *clsidW, const WCHAR *typeW, MFT_REGISTER_TYPE_INFO **type,
UINT32 *count)
{
HKEY htransform, hfilter;
DWORD reg_type, size;
*type = NULL;
*count = 0;
if (RegOpenKeyW(HKEY_CLASSES_ROOT, transform_keyW, &htransform))
return;
if (RegOpenKeyW(htransform, clsidW, &hfilter))
{
RegCloseKey(htransform);
return;
}
if (RegQueryValueExW(hfilter, typeW, NULL, &reg_type, NULL, &size))
goto out;
if (reg_type != REG_BINARY)
goto out;
if (!size || size % (sizeof(MFT_REGISTER_TYPE_INFO)) != 0)
if (!size || size % sizeof(**type))
goto out;
info = HeapAlloc(GetProcessHeap(), 0, size);
if (!info)
if (!(*type = heap_alloc(size)))
goto out;
if (RegQueryValueExW(hfilter, type_str, NULL, &reg_type, (LPBYTE)info, &size) != ERROR_SUCCESS)
goto out;
*count = size / sizeof(**type);
for (i = 0; i < size / sizeof(MFT_REGISTER_TYPE_INFO); i++)
if (RegQueryValueExW(hfilter, typeW, NULL, &reg_type, (BYTE *)*type, &size))
{
if (IsEqualGUID(&info[i].guidMajorType, &type->guidMajorType) &&
IsEqualGUID(&info[i].guidSubtype, &type->guidSubtype))
{
ret = TRUE;
break;
}
heap_free(*type);
*type = NULL;
*count = 0;
}
out:
HeapFree(GetProcessHeap(), 0, info);
RegCloseKey(hfilter);
RegCloseKey(htransform);
return ret;
}
static void mft_get_reg_flags(const WCHAR *clsidW, const WCHAR *nameW, DWORD *flags)
{
DWORD ret, reg_type, size;
HKEY hroot, hmft;
*flags = 0;
if (RegOpenKeyW(HKEY_CLASSES_ROOT, transform_keyW, &hroot))
return;
ret = RegOpenKeyW(hroot, clsidW, &hmft);
RegCloseKey(hroot);
if (ret)
return;
reg_type = 0;
if (!RegQueryValueExW(hmft, nameW, NULL, &reg_type, NULL, &size) && reg_type == REG_DWORD)
RegQueryValueExW(hmft, nameW, NULL, &reg_type, (BYTE *)flags, &size);
RegCloseKey(hmft);
}
static HRESULT mft_collect_machine_reg(struct list *mfts, const GUID *category, UINT32 flags,
IMFPluginControl *plugin_control, const MFT_REGISTER_TYPE_INFO *input_type,
const MFT_REGISTER_TYPE_INFO *output_type)
{
struct mft_registration mft, *cur;
HKEY hcategory, hlist;
WCHAR clsidW[64];
DWORD ret, size;
int index = 0;
if (RegOpenKeyW(HKEY_CLASSES_ROOT, categories_keyW, &hcategory))
return E_FAIL;
GUIDToString(clsidW, category);
ret = RegOpenKeyW(hcategory, clsidW, &hlist);
RegCloseKey(hcategory);
if (ret)
return E_FAIL;
size = ARRAY_SIZE(clsidW);
while (!RegEnumKeyExW(hlist, index, clsidW, &size, NULL, NULL, NULL, NULL))
{
memset(&mft, 0, sizeof(mft));
mft.category = *category;
if (!GUIDFromString(clsidW, &mft.clsid))
goto next;
mft_get_reg_flags(clsidW, mftflagsW, &mft.flags);
if (output_type)
mft_get_reg_type_info(clsidW, outputtypesW, &mft.output_types, &mft.output_types_count);
if (input_type)
mft_get_reg_type_info(clsidW, inputtypesW, &mft.input_types, &mft.input_types_count);
if (!mft_is_type_info_match(&mft, category, flags, plugin_control, input_type, output_type))
{
heap_free(mft.input_types);
heap_free(mft.output_types);
goto next;
}
cur = heap_alloc(sizeof(*cur));
/* Reuse allocated type arrays. */
*cur = mft;
list_add_tail(mfts, &cur->entry);
next:
size = ARRAY_SIZE(clsidW);
index++;
}
return S_OK;
}
static BOOL mft_is_preferred(IMFPluginControl *plugin_control, const CLSID *clsid)
{
CLSID preferred;
WCHAR *selector;
int index = 0;
while (SUCCEEDED(IMFPluginControl_GetPreferredClsidByIndex(plugin_control, MF_Plugin_Type_MFT, index++, &selector,
&preferred)))
{
CoTaskMemFree(selector);
if (IsEqualGUID(&preferred, clsid))
return TRUE;
}
return FALSE;
}
static HRESULT mft_enum(GUID category, UINT32 flags, const MFT_REGISTER_TYPE_INFO *input_type,
const MFT_REGISTER_TYPE_INFO *output_type, IMFAttributes *attributes, IMFActivate ***activate, UINT32 *count)
{
IMFPluginControl *plugin_control = NULL;
struct list mfts, mfts_sorted, *result = &mfts;
struct mft_registration *mft, *mft2;
unsigned int obj_count;
HRESULT hr;
*count = 0;
*activate = NULL;
if (!flags)
flags = MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER;
/* Synchronous processing is default. */
if (!(flags & (MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT | MFT_ENUM_FLAG_HARDWARE)))
flags |= MFT_ENUM_FLAG_SYNCMFT;
if (FAILED(hr = MFGetPluginControl(&plugin_control)))
{
WARN("Failed to get plugin control instance, hr %#x.\n", hr);
return hr;
}
list_init(&mfts);
/* Collect from registry */
mft_collect_machine_reg(&mfts, &category, flags, plugin_control, input_type, output_type);
/* Collect locally registered ones. */
if (flags & MFT_ENUM_FLAG_LOCALMFT)
{
struct mft_registration *local;
EnterCriticalSection(&local_mfts_section);
LIST_FOR_EACH_ENTRY(local, &local_mfts, struct mft_registration, entry)
{
if (mft_is_type_info_match(local, &category, flags, plugin_control, input_type, output_type))
{
mft = heap_alloc_zero(sizeof(*mft));
mft->clsid = local->clsid;
mft->factory = local->factory;
if (mft->factory)
IClassFactory_AddRef(mft->factory);
mft->flags = local->flags;
list_add_tail(&mfts, &mft->entry);
}
}
LeaveCriticalSection(&local_mfts_section);
}
list_init(&mfts_sorted);
if (flags & MFT_ENUM_FLAG_SORTANDFILTER)
{
/* Local registrations. */
LIST_FOR_EACH_ENTRY_SAFE(mft, mft2, &mfts, struct mft_registration, entry)
{
if (mft->flags & MFT_ENUM_FLAG_LOCALMFT)
{
list_remove(&mft->entry);
list_add_tail(&mfts_sorted, &mft->entry);
}
}
/* FIXME: Sort by merit value, for the ones that got it. Currently not handled. */
/* Preferred transforms. */
LIST_FOR_EACH_ENTRY_SAFE(mft, mft2, &mfts, struct mft_registration, entry)
{
if (!mft->factory && mft_is_preferred(plugin_control, &mft->clsid))
{
list_remove(&mft->entry);
list_add_tail(&mfts_sorted, &mft->entry);
}
}
/* Append the rest. */
LIST_FOR_EACH_ENTRY_SAFE(mft, mft2, &mfts, struct mft_registration, entry)
{
list_remove(&mft->entry);
list_add_tail(&mfts_sorted, &mft->entry);
}
result = &mfts;
}
IMFPluginControl_Release(plugin_control);
/* Create activation objects from CLSID/IClassFactory. */
obj_count = list_count(result);
if (obj_count)
{
if (!(*activate = CoTaskMemAlloc(obj_count * sizeof(**activate))))
hr = E_OUTOFMEMORY;
obj_count = 0;
LIST_FOR_EACH_ENTRY_SAFE(mft, mft2, result, struct mft_registration, entry)
{
IMFActivate *mft_activate;
if (*activate)
{
if (SUCCEEDED(create_transform_activate(mft->factory, &mft_activate)))
{
(*activate)[obj_count] = mft_activate;
/* FIXME: set some attributes */
obj_count++;
}
}
list_remove(&mft->entry);
release_mft_registration(mft);
}
}
if (!obj_count)
{
CoTaskMemFree(*activate);
*activate = NULL;
}
*count = obj_count;
return hr;
}
/***********************************************************************
* MFTEnum (mfplat.@)
*/
HRESULT WINAPI MFTEnum(GUID category, UINT32 flags, MFT_REGISTER_TYPE_INFO *input_type,
MFT_REGISTER_TYPE_INFO *output_type, IMFAttributes *attributes,
CLSID **pclsids, UINT32 *pcount)
MFT_REGISTER_TYPE_INFO *output_type, IMFAttributes *attributes, CLSID **clsids, UINT32 *count)
{
WCHAR buffer[64], clsid_str[MAX_PATH] = {0};
HKEY hcategory, hlist;
DWORD index = 0;
DWORD size = MAX_PATH;
CLSID *clsids = NULL;
UINT32 count = 0;
LONG ret;
struct mft_registration *mft, *mft2;
unsigned int mft_count;
struct list mfts;
HRESULT hr;
TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_guid(&category), flags, input_type,
output_type, attributes, pclsids, pcount);
TRACE("%s, %#x, %p, %p, %p, %p, %p.\n", debugstr_guid(&category), flags, input_type, output_type, attributes,
clsids, count);
if (!pclsids || !pcount)
if (!clsids || !count)
return E_INVALIDARG;
if (RegOpenKeyW(HKEY_CLASSES_ROOT, categories_keyW, &hcategory))
return E_FAIL;
*count = 0;
GUIDToString(buffer, &category);
list_init(&mfts);
ret = RegOpenKeyW(hcategory, buffer, &hlist);
RegCloseKey(hcategory);
if (ret) return E_FAIL;
if (FAILED(hr = mft_collect_machine_reg(&mfts, &category, MFT_ENUM_FLAG_SYNCMFT, NULL, input_type, output_type)))
return hr;
while (RegEnumKeyExW(hlist, index, clsid_str, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
mft_count = list_count(&mfts);
if (mft_count)
{
GUID clsid;
void *tmp;
if (!(*clsids = CoTaskMemAlloc(mft_count * sizeof(**clsids))))
hr = E_OUTOFMEMORY;
if (!GUIDFromString(clsid_str, &clsid))
goto next;
if (output_type && !match_type(clsid_str, outputtypesW, output_type))
goto next;
if (input_type && !match_type(clsid_str, inputtypesW, input_type))
goto next;
tmp = CoTaskMemRealloc(clsids, (count + 1) * sizeof(GUID));
if (!tmp)
mft_count = 0;
LIST_FOR_EACH_ENTRY_SAFE(mft, mft2, &mfts, struct mft_registration, entry)
{
CoTaskMemFree(clsids);
RegCloseKey(hlist);
return E_OUTOFMEMORY;
if (*clsids)
(*clsids)[mft_count++] = mft->clsid;
list_remove(&mft->entry);
release_mft_registration(mft);
}
clsids = tmp;
clsids[count++] = clsid;
next:
size = MAX_PATH;
index++;
}
*pclsids = clsids;
*pcount = count;
if (!mft_count)
{
CoTaskMemFree(*clsids);
*clsids = NULL;
}
*count = mft_count;
RegCloseKey(hlist);
return S_OK;
return hr;
}
/***********************************************************************
* MFTEnumEx (mfplat.@)
*/
HRESULT WINAPI MFTEnumEx(GUID category, UINT32 flags, const MFT_REGISTER_TYPE_INFO *input_type,
const MFT_REGISTER_TYPE_INFO *output_type, IMFActivate ***activate,
UINT32 *pcount)
const MFT_REGISTER_TYPE_INFO *output_type, IMFActivate ***activate, UINT32 *count)
{
FIXME("(%s, %x, %p, %p, %p, %p): stub\n", debugstr_guid(&category), flags, input_type,
output_type, activate, pcount);
TRACE("%s, %#x, %p, %p, %p, %p.\n", debugstr_guid(&category), flags, input_type, output_type, activate, count);
*pcount = 0;
return S_OK;
return mft_enum(category, flags, input_type, output_type, NULL, activate, count);
}
/***********************************************************************
* MFTEnum2 (mfplat.@)
*/
HRESULT WINAPI MFTEnum2(GUID category, UINT32 flags, const MFT_REGISTER_TYPE_INFO *input_type,
const MFT_REGISTER_TYPE_INFO *output_type, IMFAttributes *attributes, IMFActivate ***activate, UINT32 *count)
{
TRACE("%s, %#x, %p, %p, %p, %p, %p.\n", debugstr_guid(&category), flags, input_type, output_type, attributes,
activate, count);
if (attributes)
FIXME("Ignoring attributes.\n");
return mft_enum(category, flags, input_type, output_type, attributes, activate, count);
}
/***********************************************************************

View File

@ -144,6 +144,7 @@
@ stdcall MFStartup(long long)
@ stub MFStreamDescriptorProtectMediaType
@ stdcall MFTEnum(int128 long ptr ptr ptr ptr ptr)
@ stdcall MFTEnum2(int128 long ptr ptr ptr ptr ptr)
@ stdcall MFTEnumEx(int128 long ptr ptr ptr ptr)
@ stub MFTGetInfo
@ stdcall MFTRegister(int128 int128 wstr long long ptr long ptr ptr)

View File

@ -89,6 +89,8 @@ static HRESULT (WINAPI *pMFTRegisterLocalByCLSID)(REFCLSID clsid, REFGUID catego
static HRESULT (WINAPI *pMFTUnregisterLocal)(IClassFactory *factory);
static HRESULT (WINAPI *pMFTUnregisterLocalByCLSID)(CLSID clsid);
static HRESULT (WINAPI *pMFAllocateWorkQueueEx)(MFASYNC_WORKQUEUE_TYPE queue_type, DWORD *queue);
static HRESULT (WINAPI *pMFTEnumEx)(GUID category, UINT32 flags, const MFT_REGISTER_TYPE_INFO *input_type,
const MFT_REGISTER_TYPE_INFO *output_type, IMFActivate ***activate, UINT32 *count);
static const WCHAR fileschemeW[] = L"file://";
@ -659,13 +661,14 @@ static void init_functions(void)
X(MFCreateDXGIDeviceManager);
X(MFCreateSourceResolver);
X(MFCreateMFByteStreamOnStream);
X(MFCreateTransformActivate);
X(MFHeapAlloc);
X(MFHeapFree);
X(MFPutWaitingWorkItem);
X(MFRegisterLocalByteStreamHandler);
X(MFRegisterLocalSchemeHandler);
X(MFRemovePeriodicCallback);
X(MFCreateTransformActivate);
X(MFTEnumEx);
X(MFTRegisterLocal);
X(MFTRegisterLocalByCLSID);
X(MFTUnregisterLocal);
@ -4055,6 +4058,8 @@ static void test_MFTRegisterLocal(void)
{
IClassFactory test_factory = { &test_mft_factory_vtbl };
MFT_REGISTER_TYPE_INFO input_types[1];
IMFActivate **activate;
UINT32 count, count2;
HRESULT hr;
if (!pMFTRegisterLocal)
@ -4071,9 +4076,19 @@ static void test_MFTRegisterLocal(void)
hr = pMFTRegisterLocal(&test_factory, &MFT_CATEGORY_OTHER, L"Local MFT name", 0, 1, input_types, 0, NULL);
ok(hr == S_OK, "Failed to register MFT, hr %#x.\n", hr);
hr = pMFTEnumEx(MFT_CATEGORY_OTHER, MFT_ENUM_FLAG_LOCALMFT, NULL, NULL, &activate, &count);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
ok(count > 0, "Unexpected count %u.\n", count);
CoTaskMemFree(activate);
hr = pMFTUnregisterLocal(&test_factory);
ok(hr == S_OK, "Failed to unregister MFT, hr %#x.\n", hr);
hr = pMFTEnumEx(MFT_CATEGORY_OTHER, MFT_ENUM_FLAG_LOCALMFT, NULL, NULL, &activate, &count2);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
ok(count2 < count, "Unexpected count %u.\n", count2);
CoTaskMemFree(activate);
hr = pMFTUnregisterLocal(&test_factory);
ok(hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "Unexpected hr %#x.\n", hr);

View File

@ -422,6 +422,8 @@ HRESULT WINAPI MFGetTimerPeriodicity(DWORD *periodicity);
HRESULT WINAPI MFTEnum(GUID category, UINT32 flags, MFT_REGISTER_TYPE_INFO *input_type,
MFT_REGISTER_TYPE_INFO *output_type, IMFAttributes *attributes,
CLSID **pclsids, UINT32 *pcount);
HRESULT WINAPI MFTEnum2(GUID category, UINT32 flags, const MFT_REGISTER_TYPE_INFO *input_type,
const MFT_REGISTER_TYPE_INFO *output_type, IMFAttributes *attributes, IMFActivate ***activate, UINT32 *count);
HRESULT WINAPI MFTEnumEx(GUID category, UINT32 flags, const MFT_REGISTER_TYPE_INFO *input_type,
const MFT_REGISTER_TYPE_INFO *output_type, IMFActivate ***activate,
UINT32 *pcount);