From: Zebediah Figura <zfigura(a)codeweavers.com>
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/dsdmo/Makefile.in | 4 +
dlls/dsdmo/dsdmo.idl | 27 +++
dlls/dsdmo/main.c | 429 +++++++++++++++++++++++++++++++++++++--
dlls/dsdmo/tests/dsdmo.c | 42 ++--
4 files changed, 462 insertions(+), 40 deletions(-)
create mode 100644 dlls/dsdmo/dsdmo.idl
diff --git a/dlls/dsdmo/Makefile.in b/dlls/dsdmo/Makefile.in
index 99816ae0c08..3787415abd6 100644
--- a/dlls/dsdmo/Makefile.in
+++ b/dlls/dsdmo/Makefile.in
@@ -1,6 +1,10 @@
MODULE = dsdmo.dll
+IMPORTS = dmoguids uuid
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
main.c
+
+IDL_SRCS = \
+ dsdmo.idl
diff --git a/dlls/dsdmo/dsdmo.idl b/dlls/dsdmo/dsdmo.idl
new file mode 100644
index 00000000000..a967186af3f
--- /dev/null
+++ b/dlls/dsdmo/dsdmo.idl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 Zebediah Figura
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#pragma makedep register
+
+[
+ threading(both),
+ progid("Microsoft.DirectSoundWavesReverbDMO.1"),
+ vi_progid("Microsoft.DirectSoundWavesReverbDMO"),
+ uuid(87fc0268-9a55-4360-95aa-004a1d9de26c)
+]
+coclass DirectSoundWavesReverbDMO {}
diff --git a/dlls/dsdmo/main.c b/dlls/dsdmo/main.c
index 788381b7a45..44915ca8566 100644
--- a/dlls/dsdmo/main.c
+++ b/dlls/dsdmo/main.c
@@ -15,9 +15,13 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#define COBJMACROS
-#include "ole2.h"
+#define COBJMACROS
+#include "dmo.h"
+#include "mmreg.h"
+#include "mmsystem.h"
+#include "initguid.h"
+#include "dsound.h"
#include "rpcproxy.h"
#include "wine/debug.h"
@@ -26,19 +30,318 @@ WINE_DEFAULT_DEBUG_CHANNEL(dsdmo);
static HINSTANCE dsdmo_instance;
-/******************************************************************
- * DllMain
- */
-BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved)
+struct effect
+{
+ IMediaObject IMediaObject_iface;
+ IUnknown IUnknown_inner;
+ IUnknown *outer_unk;
+ LONG refcount;
+
+ const struct effect_ops *ops;
+};
+
+struct effect_ops
+{
+ void *(*query_interface)(struct effect *effect, REFIID iid);
+ void (*destroy)(struct effect *effect);
+};
+
+static struct effect *impl_from_IUnknown(IUnknown *iface)
+{
+ return CONTAINING_RECORD(iface, struct effect, IUnknown_inner);
+}
+
+static HRESULT WINAPI effect_inner_QueryInterface(IUnknown *iface, REFIID iid, void **out)
+{
+ struct effect *effect = impl_from_IUnknown(iface);
+
+ TRACE("effect %p, iid %s, out %p.\n", effect, debugstr_guid(iid), out);
+
+ if (IsEqualGUID(iid, &IID_IUnknown))
+ *out = iface;
+ else if (IsEqualGUID(iid, &IID_IMediaObject))
+ *out = &effect->IMediaObject_iface;
+ else if (!(*out = effect->ops->query_interface(effect, iid)))
+ {
+ WARN("%s not implemented; returning E_NOINTERFACE.\n", debugstr_guid(iid));
+ return E_NOINTERFACE;
+ }
+
+ IUnknown_AddRef((IUnknown *)*out);
+ return S_OK;
+}
+
+static ULONG WINAPI effect_inner_AddRef(IUnknown *iface)
+{
+ struct effect *effect = impl_from_IUnknown(iface);
+ ULONG refcount = InterlockedIncrement(&effect->refcount);
+
+ TRACE("%p increasing refcount to %u.\n", effect, refcount);
+ return refcount;
+}
+
+static ULONG WINAPI effect_inner_Release(IUnknown *iface)
{
- TRACE("(%p %d %p)\n", inst, reason, reserved);
+ struct effect *effect = impl_from_IUnknown(iface);
+ ULONG refcount = InterlockedDecrement(&effect->refcount);
+
+ TRACE("%p decreasing refcount to %u.\n", effect, refcount);
+
+ if (!refcount)
+ {
+ effect->ops->destroy(effect);
+ }
+ return refcount;
+}
+
+static const IUnknownVtbl effect_inner_vtbl =
+{
+ effect_inner_QueryInterface,
+ effect_inner_AddRef,
+ effect_inner_Release,
+};
+
+static struct effect *impl_from_IMediaObject(IMediaObject *iface)
+{
+ return CONTAINING_RECORD(iface, struct effect, IMediaObject_iface);
+}
+
+static HRESULT WINAPI effect_QueryInterface(IMediaObject *iface, REFIID iid, void **out)
+{
+ struct effect *effect = impl_from_IMediaObject(iface);
+ return IUnknown_QueryInterface(effect->outer_unk, iid, out);
+}
+
+static ULONG WINAPI effect_AddRef(IMediaObject *iface)
+{
+ struct effect *effect = impl_from_IMediaObject(iface);
+ return IUnknown_AddRef(effect->outer_unk);
+}
+
+static ULONG WINAPI effect_Release(IMediaObject *iface)
+{
+ struct effect *effect = impl_from_IMediaObject(iface);
+ return IUnknown_Release(effect->outer_unk);
+}
+
+static HRESULT WINAPI effect_GetStreamCount(IMediaObject *iface, DWORD *input, DWORD *output)
+{
+ FIXME("iface %p, input %p, output %p, stub!\n", iface, input, output);
+ return E_NOTIMPL;
+}
+static HRESULT WINAPI effect_GetInputStreamInfo(IMediaObject *iface, DWORD index, DWORD *flags)
+{
+ FIXME("iface %p, index %u, flags %p, stub!\n", iface, index, flags);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_GetOutputStreamInfo(IMediaObject *iface, DWORD index, DWORD *flags)
+{
+ FIXME("iface %p, index %u, flags %p, stub!\n", iface, index, flags);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_GetInputType(IMediaObject *iface, DWORD index, DWORD type_index, DMO_MEDIA_TYPE *type)
+{
+ FIXME("iface %p, index %u, type_index %u, type %p, stub!\n", iface, index, type_index, type);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_GetOutputType(IMediaObject *iface, DWORD index, DWORD type_index, DMO_MEDIA_TYPE *type)
+{
+ FIXME("iface %p, index %u, type_index %u, type %p, stub!\n", iface, index, type_index, type);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_SetInputType(IMediaObject *iface, DWORD index, const DMO_MEDIA_TYPE *type, DWORD flags)
+{
+ FIXME("iface %p, index %u, type %p, flags %#x, stub!\n", iface, index, type, flags);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_SetOutputType(IMediaObject *iface, DWORD index, const DMO_MEDIA_TYPE *type, DWORD flags)
+{
+ FIXME("iface %p, index %u, type %p, flags %#x, stub!\n", iface, index, type, flags);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_GetInputCurrentType(IMediaObject *iface, DWORD index, DMO_MEDIA_TYPE *type)
+{
+ FIXME("iface %p, index %u, type %p, stub!\n", iface, index, type);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_GetOutputCurrentType(IMediaObject *iface, DWORD index, DMO_MEDIA_TYPE *type)
+{
+ FIXME("iface %p, index %u, type %p, stub!\n", iface, index, type);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_GetInputSizeInfo(IMediaObject *iface, DWORD index,
+ DWORD *size, DWORD *lookahead, DWORD *alignment)
+{
+ FIXME("iface %p, index %u, size %p, lookahead %p, alignment %p, stub!\n", iface, index, size, lookahead, alignment);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_GetOutputSizeInfo(IMediaObject *iface, DWORD index, DWORD *size, DWORD *alignment)
+{
+ FIXME("iface %p, index %u, size %p, alignment %p, stub!\n", iface, index, size, alignment);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_GetInputMaxLatency(IMediaObject *iface, DWORD index, REFERENCE_TIME *latency)
+{
+ FIXME("iface %p, index %u, latency %p, stub!\n", iface, index, latency);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_SetInputMaxLatency(IMediaObject *iface, DWORD index, REFERENCE_TIME latency)
+{
+ FIXME("iface %p, index %u, latency %s, stub!\n", iface, index, wine_dbgstr_longlong(latency));
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_Flush(IMediaObject *iface)
+{
+ FIXME("iface %p, stub!\n", iface);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_Discontinuity(IMediaObject *iface, DWORD index)
+{
+ FIXME("iface %p, index %u, stub!\n", iface, index);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_AllocateStreamingResources(IMediaObject *iface)
+{
+ FIXME("iface %p, stub!\n", iface);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_FreeStreamingResources(IMediaObject *iface)
+{
+ FIXME("iface %p, stub!\n", iface);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_GetInputStatus(IMediaObject *iface, DWORD index, DWORD *flags)
+{
+ FIXME("iface %p, index %u, flags %p, stub!\n", iface, index, flags);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_ProcessInput(IMediaObject *iface, DWORD index,
+ IMediaBuffer *buffer, DWORD flags, REFERENCE_TIME timestamp, REFERENCE_TIME timelength)
+{
+ FIXME("iface %p, index %u, buffer %p, flags %#x, timestamp %s, timelength %s, stub!\n",
+ iface, index, buffer, flags, wine_dbgstr_longlong(timestamp), wine_dbgstr_longlong(timelength));
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_ProcessOutput(IMediaObject *iface, DWORD flags,
+ DWORD count, DMO_OUTPUT_DATA_BUFFER *buffers, DWORD *status)
+{
+ FIXME("iface %p, flags %#x, count %u, buffers %p, status %p, stub!\n", iface, flags, count, buffers, status);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI effect_Lock(IMediaObject *iface, LONG lock)
+{
+ FIXME("iface %p, lock %d, stub!\n", iface, lock);
+ return E_NOTIMPL;
+}
+
+static const IMediaObjectVtbl effect_vtbl =
+{
+ effect_QueryInterface,
+ effect_AddRef,
+ effect_Release,
+ effect_GetStreamCount,
+ effect_GetInputStreamInfo,
+ effect_GetOutputStreamInfo,
+ effect_GetInputType,
+ effect_GetOutputType,
+ effect_SetInputType,
+ effect_SetOutputType,
+ effect_GetInputCurrentType,
+ effect_GetOutputCurrentType,
+ effect_GetInputSizeInfo,
+ effect_GetOutputSizeInfo,
+ effect_GetInputMaxLatency,
+ effect_SetInputMaxLatency,
+ effect_Flush,
+ effect_Discontinuity,
+ effect_AllocateStreamingResources,
+ effect_FreeStreamingResources,
+ effect_GetInputStatus,
+ effect_ProcessInput,
+ effect_ProcessOutput,
+ effect_Lock,
+};
+
+static void effect_init(struct effect *effect, IUnknown *outer, const struct effect_ops *ops)
+{
+ effect->outer_unk = outer ? outer : &effect->IUnknown_inner;
+ effect->refcount = 1;
+ effect->IUnknown_inner.lpVtbl = &effect_inner_vtbl;
+ effect->IMediaObject_iface.lpVtbl = &effect_vtbl;
+
+ effect->ops = ops;
+}
+
+struct waves_reverb
+{
+ struct effect effect;
+};
+
+static struct waves_reverb *impl_waves_reverb_from_effect(struct effect *iface)
+{
+ return CONTAINING_RECORD(iface, struct waves_reverb, effect);
+}
+
+static void *waves_reverb_query_interface(struct effect *iface, REFIID iid)
+{
+ return NULL;
+}
+
+static void waves_reverb_destroy(struct effect *iface)
+{
+ struct waves_reverb *effect = impl_waves_reverb_from_effect(iface);
+
+ free(effect);
+}
+
+static const struct effect_ops waves_reverb_ops =
+{
+ .destroy = waves_reverb_destroy,
+ .query_interface = waves_reverb_query_interface,
+};
+
+static HRESULT waves_reverb_create(IUnknown *outer, IUnknown **out)
+{
+ struct waves_reverb *object;
+
+ if (!(object = calloc(1, sizeof(*object))))
+ return E_OUTOFMEMORY;
+
+ effect_init(&object->effect, outer, &waves_reverb_ops);
+
+ TRACE("Created waves reverb effect %p.\n", object);
+ *out = &object->effect.IUnknown_inner;
+ return S_OK;
+}
+
+BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved)
+{
switch(reason)
{
case DLL_WINE_PREATTACH:
return FALSE; /* prefer native version */
case DLL_PROCESS_ATTACH:
- dsdmo_instance = inst;
+ dsdmo_instance = instance;
DisableThreadLibraryCalls(dsdmo_instance);
break;
}
@@ -46,35 +349,117 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved)
return TRUE;
}
-/***********************************************************************
- * DllGetClassObject
- */
-HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+struct class_factory
{
- FIXME("%s %s %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+ IClassFactory IClassFactory_iface;
+ HRESULT (*create_instance)(IUnknown *outer, IUnknown **out);
+};
+
+static struct class_factory *impl_from_IClassFactory(IClassFactory *iface)
+{
+ return CONTAINING_RECORD(iface, struct class_factory, IClassFactory_iface);
+}
+
+static HRESULT WINAPI class_factory_QueryInterface(IClassFactory *iface, REFIID iid, void **out)
+{
+ TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
+
+ if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IClassFactory))
+ {
+ IClassFactory_AddRef(iface);
+ *out = iface;
+ return S_OK;
+ }
+
+ *out = NULL;
+ WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(iid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI class_factory_AddRef(IClassFactory *iface)
+{
+ return 2;
+}
+
+static ULONG WINAPI class_factory_Release(IClassFactory *iface)
+{
+ return 1;
+}
+
+static HRESULT WINAPI class_factory_CreateInstance(IClassFactory *iface,
+ IUnknown *outer, REFIID iid, void **out)
+{
+ struct class_factory *factory = impl_from_IClassFactory(iface);
+ IUnknown *unk;
+ HRESULT hr;
+
+ TRACE("iface %p, outer %p, iid %s, out %p.\n", iface, outer, debugstr_guid(iid), out);
+
+ *out = NULL;
+
+ if (outer && !IsEqualGUID(iid, &IID_IUnknown))
+ return E_NOINTERFACE;
+
+ if (SUCCEEDED(hr = factory->create_instance(outer, &unk)))
+ {
+ hr = IUnknown_QueryInterface(unk, iid, out);
+ IUnknown_Release(unk);
+ }
+ return hr;
+}
+
+static HRESULT WINAPI class_factory_LockServer(IClassFactory *iface, BOOL lock)
+{
+ FIXME("lock %d, stub!\n", lock);
+ return S_OK;
+}
+
+static const IClassFactoryVtbl class_factory_vtbl =
+{
+ class_factory_QueryInterface,
+ class_factory_AddRef,
+ class_factory_Release,
+ class_factory_CreateInstance,
+ class_factory_LockServer,
+};
+
+static struct
+{
+ const GUID *clsid;
+ struct class_factory factory;
+}
+class_factories[] =
+{
+ {&GUID_DSFX_WAVES_REVERB, {{&class_factory_vtbl}, waves_reverb_create}},
+};
+
+HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out)
+{
+ unsigned int i;
+
+ TRACE("clsid %s, iid %s, out %p.\n", debugstr_guid(clsid), debugstr_guid(iid), out);
+
+ for (i = 0; i < ARRAY_SIZE(class_factories); ++i)
+ {
+ if (IsEqualGUID(clsid, class_factories[i].clsid))
+ return IClassFactory_QueryInterface(&class_factories[i].factory.IClassFactory_iface, iid, out);
+ }
+
+ FIXME("%s not available, returning CLASS_E_CLASSNOTAVAILABLE.\n", debugstr_guid(clsid));
return CLASS_E_CLASSNOTAVAILABLE;
}
-/***********************************************************************
- * DllCanUnloadNow
- */
HRESULT WINAPI DllCanUnloadNow(void)
{
return S_FALSE;
}
-/***********************************************************************
- * DllRegisterServer
- */
HRESULT WINAPI DllRegisterServer(void)
{
TRACE("()\n");
return __wine_register_resources(dsdmo_instance);
}
-/***********************************************************************
- * DllUnregisterServer
- */
HRESULT WINAPI DllUnregisterServer(void)
{
TRACE("()\n");
diff --git a/dlls/dsdmo/tests/dsdmo.c b/dlls/dsdmo/tests/dsdmo.c
index b8f0932790b..f236c95d43c 100644
--- a/dlls/dsdmo/tests/dsdmo.c
+++ b/dlls/dsdmo/tests/dsdmo.c
@@ -80,9 +80,7 @@ static void test_aggregation(const GUID *clsid)
dmo = (IMediaObject *)0xdeadbeef;
hr = CoCreateInstance(clsid, &test_outer, CLSCTX_INPROC_SERVER,
&IID_IMediaObject, (void **)&dmo);
- todo_wine ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
- if (hr != E_NOINTERFACE)
- return;
+ ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr);
ok(!dmo, "Got interface %p.\n", dmo);
hr = CoCreateInstance(clsid, &test_outer, CLSCTX_INPROC_SERVER,
@@ -138,9 +136,7 @@ static void test_interfaces(const GUID *clsid, const GUID *iid)
ULONG ref;
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, (void **)&unk);
- todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
- if (hr != S_OK)
- return;
+ ok(hr == S_OK, "Got hr %#x.\n", hr);
hr = IUnknown_QueryInterface(unk, &IID_IMediaObject, (void **)&unk2);
ok(hr == S_OK, "Got hr %#x.\n", hr);
@@ -204,9 +200,7 @@ static void test_media_types(const GUID *clsid)
};
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo);
- todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
- if (hr != S_OK)
- return;
+ ok(hr == S_OK, "Got hr %#x.\n", hr);
build_pcm_format(&wfx, WAVE_FORMAT_PCM, 16, 44100, 2);
@@ -508,18 +502,19 @@ START_TEST(dsdmo)
{
const GUID *clsid;
const GUID *iid;
+ BOOL todo;
}
tests[] =
{
- {&GUID_DSFX_STANDARD_CHORUS, &IID_IDirectSoundFXChorus},
- {&GUID_DSFX_STANDARD_COMPRESSOR, &IID_IDirectSoundFXCompressor},
- {&GUID_DSFX_STANDARD_DISTORTION, &IID_IDirectSoundFXDistortion},
- {&GUID_DSFX_STANDARD_ECHO, &IID_IDirectSoundFXEcho},
- {&GUID_DSFX_STANDARD_FLANGER, &IID_IDirectSoundFXFlanger},
- {&GUID_DSFX_STANDARD_GARGLE, &IID_IDirectSoundFXGargle},
- {&GUID_DSFX_STANDARD_I3DL2REVERB, &IID_IDirectSoundFXI3DL2Reverb},
- {&GUID_DSFX_STANDARD_PARAMEQ, &IID_IDirectSoundFXParamEq},
- {&GUID_DSFX_WAVES_REVERB, &IID_IDirectSoundFXWavesReverb},
+ {&GUID_DSFX_STANDARD_CHORUS, &IID_IDirectSoundFXChorus, TRUE},
+ {&GUID_DSFX_STANDARD_COMPRESSOR, &IID_IDirectSoundFXCompressor, TRUE},
+ {&GUID_DSFX_STANDARD_DISTORTION, &IID_IDirectSoundFXDistortion, TRUE},
+ {&GUID_DSFX_STANDARD_ECHO, &IID_IDirectSoundFXEcho, TRUE},
+ {&GUID_DSFX_STANDARD_FLANGER, &IID_IDirectSoundFXFlanger, TRUE},
+ {&GUID_DSFX_STANDARD_GARGLE, &IID_IDirectSoundFXGargle, TRUE},
+ {&GUID_DSFX_STANDARD_I3DL2REVERB, &IID_IDirectSoundFXI3DL2Reverb, TRUE},
+ {&GUID_DSFX_STANDARD_PARAMEQ, &IID_IDirectSoundFXParamEq, TRUE},
+ {&GUID_DSFX_WAVES_REVERB, &IID_IDirectSoundFXWavesReverb, TRUE},
};
unsigned int i;
@@ -527,6 +522,17 @@ START_TEST(dsdmo)
for (i = 0; i < ARRAY_SIZE(tests); ++i)
{
+ IUnknown *unk;
+ HRESULT hr;
+
+ hr = CoCreateInstance(tests[i].clsid, NULL, CLSCTX_INPROC_SERVER, tests[i].iid, (void **)&unk);
+ todo_wine_if(tests[i].todo) ok(hr == S_OK, "Failed to create %s, hr %#x.\n",
+ debugstr_guid(tests[i].clsid), hr);
+ if (hr == S_OK)
+ IUnknown_Release(unk);
+ else
+ continue;
+
test_aggregation(tests[i].clsid);
test_interfaces(tests[i].clsid, tests[i].iid);
test_media_types(tests[i].clsid);
--
2.27.0