Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

DMO manually created for graph with wrapper?

40 views
Skip to first unread message

mike

unread,
Sep 15, 2008, 7:01:13 PM9/15/08
to
I have a DMO that I've created that I would like to use in a graph in
a 2nd application. I would like to not have to register the DMO but
to create it while building the graph (create it internally to the 2nd
application). It looks like the DMOWrapper can't take an instantiated
COM object but a ref to a classid to create the object itself. Is
this the case? Is it possible to create an instance of a DMO that's
not registered and use it in a graph?

Thanks,

Mike

The March Hare [MVP]

unread,
Sep 15, 2008, 8:16:40 PM9/15/08
to
On Mon, 15 Sep 2008 16:01:13 -0700 (PDT), mike wrote:

> It looks like the DMOWrapper can't take an instantiated
> COM object but a ref to a classid to create the object itself. Is
> this the case? Is it possible to create an instance of a DMO that's
> not registered and use it in a graph?

AFAIK, you cannot make a DMO internal to an app. You can do it with a
regular filter (source, transform, inplace, renderer, etc.).

--
Please read this before replying:
1. Dshow & posting help: http://tmhare.mvps.org/help.htm
2. Trim & respond inline (please don't top post or snip everything)
3. Benefit others: follow up if you are helped or you found a solution

Alessandro Angeli

unread,
Sep 16, 2008, 6:54:40 AM9/16/08
to
From: "mike"

It's an undocomented behavior, but IDMOWrapperFilter::Init()
only uses the DMO's CLSID in CoCreateInstance() and ignores
the category GUID.

So you should be able to use
CoRegisterClassObject(CLSCTX_INPROC_SERVER,REGCLS_ MULTI_
SEPARATE) to register the DMO's IClassFactory internally,
without globally registering the inproc server.

You can implement the class factory yourself or re-use the
implementation in the BaseClasses or in ATL and so on.


--
// Alessandro Angeli
// MVP :: DirectShow / MediaFoundation
// mvpnews at riseoftheants dot com
// http://www.riseoftheants.com/mmx/faq.htm


Roman Ryl...

unread,
Sep 16, 2008, 12:32:09 PM9/16/08
to
Hi,

> It's an undocomented behavior, but IDMOWrapperFilter::Init()
> only uses the DMO's CLSID in CoCreateInstance() and ignores
> the category GUID.

I suppose it is quite OK to have a regular COM class registered but
not registered using DMORegister. And then use this CLSID as an
argument for IDMOWrapperFilter::Init. If the project is ATL based it
is simple, just recently I used this scenario and it worked well.

Roman

Alessandro Angeli

unread,
Sep 16, 2008, 12:53:08 PM9/16/08
to
From: "Roman Ryl..."

> I suppose it is quite OK to have a regular COM class
> registered but not registered using DMORegister. And then
> use this CLSID as an argument for
> IDMOWrapperFilter::Init. If the project is ATL based it
> is simple, just recently I used this scenario and it
> worked well.

Yes, it is OK. COM registration is required to use the
DMOWrapper while DMO registration is only required for the
SysDevEnum (which uses the DMOWrapper, so it implies COM
registration as well).

If you don't want any registraion at all and still want to
use the DMOWrapper, you need to use CoRegisterClassObject()
or hook the registry APIs.

mike

unread,
Sep 16, 2008, 4:02:17 PM9/16/08
to
On Sep 16, 9:53 am, "Alessandro Angeli" <nob...@nowhere.in.the.net>
wrote:

Thank you for the quick response on this. After reviewing the
solution and docs, I realize that I did not clearly state the layout
of my implementation for which I appologize.

We have a custom encoder and decoder for which we wrote DMOs for. We
did so for the decoder so that it would run under WMP. On encoder
machines that we build and ship, we take precautions so that the
registered DMO cannot be used on a machine we didn't ship it on.
However, as we now have an application that will require the encoder
to be distributied widely, I needed to come up with at way to protect
it as well. In reviewing the wmvmux sample, I saw how to create and
use a dshow filter internally without having to register it so it
could not be used in others apps. Before my original post, I had
modifed my code where we build our graph to use the DMO so that I
incorporated the .h/.cpp files from our DMO solution as I wanted to be
able to use it without shipping our DMO itself. This would keep the
two projects pointing to same source. What I didn't include was the
Dll functions that are used by the DMO for registration, etc.

However, where I mislead in my description is that the creation of the
dshow graph is housed in a dll (this was not originally any type of
COM object but a dll with exported classes/functions) which is used by
an ocx with the actual "application" being IE. Although I'm not that
familar with the CoRegisterClassObject, the docs lead me to believe
that I can't call this from within a dll. Is this correct? I've
tried to implement this functionality as described in the dll that
constructs the graph but without success:

this is declared in a header file in the dll project:

CComPtr<IBaseFilter> pVideoCompressor2;
CComObject<CSILKEncoderDMO>* pMyEncoder;

within a function of the .cpp I have:

hr = pVideoCompressor2.CoCreateInstance( CLSID_DMOWrapperFilter );
if (FAILED(hr))
{
svcParent->LogEventEx(
1
, TEXT( "ERROR -
pVideoCompressor2.CoCreateInstance( CLSID_DMOWrapperFilter )" )
, EVENTLOG_ERROR_TYPE
);
return hr;
}
CComQIPtr<IDMOWrapperFilter> pWrapperFilter( pVideoCompressor2 );

pMyEncoder = NULL;
hr = CComObject<CSILKEncoderDMO>::CreateInstance( &pMyEncoder );
if (FAILED(hr))
{
svcParent->LogEventEx(
1
, TEXT( "ERROR -
CComObject<CSILKEncoderDMO>::CreateInstance( &pMyEncoder )" )
, EVENTLOG_ERROR_TYPE
);
return hr;
}

IUnknown* punk =
reinterpret_cast<IUnknown*>( pMyEncoder );

hr = CoRegisterClassObject(
CLSID_SILKEncoderDMO
, punk
, CLSCTX_INPROC_SERVER
, REGCLS_MULTI_SEPARATE
, &m_dwRegFlag
);

hr = pWrapperFilter->Init( CLSID_SILKEncoderDMO,
DMOCATEGORY_VIDEO_ENCODER );


Up to the point of pWrapperFilter->Init, all hr return 0x00 and
m_dwRegFlag has a value. However, when pWrapperFilter->Init is
called, it returns a 0x80004002 (Interface not supported error.).

The class for the dmo is declared as:

class ATL_NO_VTABLE CSILKEncoderDMO :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CSILKEncoderDMO, &CLSID_SILKEncoderDMO>,
public IDispatchImpl<ISILKEncoderDMO, &IID_ISILKEncoderDMO,
&LIBID_dmoSILKEncLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public IMediaObjectImpl<CSILKEncoderDMO, 1, 1>

I've tried setting breakpoints in the dmo code and can see it being
created, however, I'm not sure what interface it is failing on.

Am I not implementing the described solution correctly? Originally I
had thought that I would need to create a dshow filter to get this to
work but I would like not to if possible.

Thank you for all of the help!

Mike

Roman Ryl...

unread,
Sep 16, 2008, 4:18:24 PM9/16/08
to
Mike,

As far as I can see you have ATL project. And you have a coclass
CSILKEncoderDMO with CLSID_SILKEncoderDMO. I assume this class has
its .rgs entry, corresponding OBJECT_ENTRY macro etc, so it is already
COM-instatiateable. In any event you don't need to
CComObject<CSILKEncoderDMO>::CreateInstance since you will have DMO
wrapper created an instance of the class internally as a part of Init
call. Note that your class will be created as aggregated so you must
not disable this functionality by ATL macro (I believe you don't).

As you already have a regular COM class and it has its OBJECT_ENTRY,
CoRegisterClassObject is already done for you by ATL base, you don't
need to call this API yourselves.

Certainly this allows third party to reuse your class similar way and
bypass the protection but it is required to know in advance correct
CLSID etc. If it is important to protect in a stricter way, you need
to remove OBJECT_ENTRY so that CoRegisterClassObject is not called
automatically for your class by ATL base. In this case you will need
to call it yourself before DMO Init call (as in your sample).

Roman

Alessandro Angeli

unread,
Sep 16, 2008, 5:11:25 PM9/16/08
to
From: "mike"

[...]


> Am I not implementing the described solution correctly?

No, you're not.

Here is a primer on how CoCreateInstance() (which is what
IDMOWrapperFilter::Init() uses) works for
CLSCTX_INPROC_SERVER:

1. it looks up an IClassFactory object associated to the
CLSID

2. if there is none, it loads the DLL server specified in
[HKCR\CLSID\<clsid>\InprocServer32]@ and calls its
DllGetClassObject() to retrieve an IClassFactory

3. once it has an IClassFactory object, it call
IClassFactory::CreateInstance() to create an instance of the
requested object

So you actually have to supply 2 COM objects for the given
CLSID: the class factory, which is used to create instances
of the actual object, and the actual object. The class
factory and the DllGetClassObject() export are usually
provided by the framework behind your back, so you don't
even notice.

In this case, since you don't want to package the DMO in a
DLL server and register it, you must use
CoRegisterClassObject() at runtime so that
CoCreateInstance() succeeds at step 1.

In you code, you passed an instance of the actual object to
CoRegisterClassObject() instead of an instance of the class
factory, hence the failure.

Frankly, I have no idea where ATL hides the class factory it
generates for you but here are a couple of ides:

- let ATL produce the DllGetClassObject() DLL export, but
remove it from the .def file, so that it is not really
exported then call it directly like you would call any other
internal function

- implement IClassFactory yourself since after all it is a
very stupid internal COM object (LockServer() can just
return S_OK or maybe even E_NOTIMPL and CreateInstance()
just returns an instance of the actual object)

Alessandro Angeli

unread,
Sep 16, 2008, 4:55:36 PM9/16/08
to
From: "Roman Ryl..."

> As you already have a regular COM class and it has its
> OBJECT_ENTRY, CoRegisterClassObject is already done for
> you by ATL base, you don't need to call this API
> yourselves.

I would be surprised if it did: when you build an inproc
server, there is no need to call CoRegisterClassObject() at
runtime, since standard inproc servers packaged in a DLL
(which is I believe what ATL produces by default) use the
registry entries and the DllGetClassObject() export to
locate the class factory.

mike

unread,
Sep 16, 2008, 6:23:57 PM9/16/08
to
On Sep 16, 2:11 pm, "Alessandro Angeli" <nob...@nowhere.in.the.net>
wrote:

Alessandro,

Excellent - Thank you for the help and patience...

modifications:

derived from CComClassFactory...

class ATL_NO_VTABLE CSILKEncoderDMO :
public CComClassFactory, //CComObjectRootEx<CComMultiThreadModel>,


public CComCoClass<CSILKEncoderDMO, &CLSID_SILKEncoderDMO>,
public IDispatchImpl<ISILKEncoderDMO, &IID_ISILKEncoderDMO,
&LIBID_dmoSILKEncLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public IMediaObjectImpl<CSILKEncoderDMO, 1, 1>

changed calling code as follows:

IUnknown* punk = NULL;
//reinterpret_cast<IUnknown*>( pMyEncoder );

DllGetClassObject(
CLSID_SILKEncoderDMO
, IID_IClassFactory//IID_ISILKEncoderDMO
, ( PVOID* )&punk

Roman Ryl...

unread,
Sep 17, 2008, 1:42:33 AM9/17/08
to
Hi,

> > As you already have a regular COM class and it has its
> > OBJECT_ENTRY, CoRegisterClassObject is already done for
> > you by ATL base, you don't need to call this API
> > yourselves.
>
> I would be surprised if it did: when you build an inproc
> server, there is no need to call CoRegisterClassObject() at
> runtime, since standard inproc servers packaged in a DLL
> (which is I believe what ATL produces by default) use the
> registry entries and the DllGetClassObject() export to
> locate the class factory.

OK, you are correct here, but things are still very much simpler as
they seem.

Given a regular COM class:

===
#pragma once

#include "ClassFactorySample01_i.h"

class ATL_NO_VTABLE CFoo :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CFoo, &CLSID_Foo>,
public IFoo
{
public:

DECLARE_REGISTRY_RESOURCEID(IDR_FOO)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CFoo)
COM_INTERFACE_ENTRY(IFoo)
END_COM_MAP()

public:
// CFoo
CFoo()
{
ATLTRACE2(atlTraceRefcount, 4, _T("this 0x%08x\n"), this);
}
};

OBJECT_ENTRY_AUTO(__uuidof(Foo), CFoo)

extern "C" inline __declspec(dllexport) INT STDMETHODCALLTYPE Test()
{
ATLVERIFY(SUCCEEDED(CoInitialize(NULL)));
{
CComPtr<IFoo> pFoo;
HRESULT nResult = pFoo.CoCreateInstance(CLSID_Foo);
}
CoUninitialize();
return 0;
}
===

Class factory is already here available through inheritance from
CComCoClass. No need to bother creating new one. To disable
registration of the COM class through registry it is required to (one
of the ways) comment out DECLARE_REGISTRY_RESOURCEID and provide empty
UpdateRegistry method. Class factory is accessible through
OBJECT_ENTRY_AUTO's created __objMap_CFoo variable. Here it goes:

===
#pragma once

#include "ClassFactorySample01_i.h"

class ATL_NO_VTABLE CFoo :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CFoo, &CLSID_Foo>,
public IFoo
{
public:

//DECLARE_REGISTRY_RESOURCEID(IDR_FOO)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CFoo)
COM_INTERFACE_ENTRY(IFoo)
END_COM_MAP()

public:
// CFoo
static HRESULT STDMETHODCALLTYPE UpdateRegistry(BOOL)
{
return S_OK;
}
CFoo()
{
ATLTRACE2(atlTraceRefcount, 4, _T("this 0x%08x\n"), this);
}
};

OBJECT_ENTRY_AUTO(__uuidof(Foo), CFoo)

extern "C" inline __declspec(dllexport) INT STDMETHODCALLTYPE Test()
{
ATLVERIFY(SUCCEEDED(CoInitialize(NULL)));
{

ATLVERIFY(SUCCEEDED(__objMap_CFoo.RegisterClassObject(CLSCTX_INPROC_SERVER,
REGCLS_MULTI_SEPARATE)));
CComPtr<IFoo> pFoo;
HRESULT nResult = pFoo.CoCreateInstance(CLSID_Foo);
ATLVERIFY(SUCCEEDED(__objMap_CFoo.RevokeClassObject()));
}
CoUninitialize();
return 0;
}
===

And that's it. If the code was created by older versions of Visual
Studio with OBJECT_ENTRY instead of OBJECT_ENTRY_AUTO, the structure
pointed to by __objMap_CFoo needs to be looked up from OBJECT_MAP.

Roman

Alessandro Angeli

unread,
Sep 17, 2008, 12:45:48 PM9/17/08
to
From: "mike"

> class ATL_NO_VTABLE CSILKEncoderDMO :
> public CComClassFactory,

The class factory and the actual object class should be 2
separate classes, not the same one! Granted that it may work
this way too if you really know what you are doing, but this
is not the right way in your case.

> IUnknown* punk = NULL;
> //reinterpret_cast<IUnknown*>( pMyEncoder );
>
> DllGetClassObject(
> CLSID_SILKEncoderDMO
> , IID_IClassFactory//IID_ISILKEncoderDMO
> , ( PVOID* )&punk
> );

punk should be of type IClassFactory*, not IUnknown*. Notice
that, since every COM interface inherits IUnknown, you pass
any interface wherever IUnknown is expected, without casts
or QueryInterface()s.

If you are using DllGetClassObject(), ATL will provide the
class factory for you and you do not need to implement it
yourself. Read Roman's latest post, who knows about ATL.

mike

unread,
Sep 17, 2008, 1:41:31 PM9/17/08
to
On Sep 17, 9:45 am, "Alessandro Angeli" <nob...@nowhere.in.the.net>
wrote:

Ok. I followed Roman's example and removed my mods and all works
great. Thanks everyone for the help!

Mike

0 new messages