But I'm trying to figure out what the right way to initialize COM is.
The DirectShow SDK docs say I *may* use COINIT_MULTITHREADED, but offer no
other advice. In the DirectPlay docs, it suggests that COINIT_MULTITHREADED
is the desirable option, and that problems might arise with
COINIT_APARTMENTTHREADED.
In Google Groups, I see that there's been exactly one post here saying that
COINIT_MULTITHREADED is the right way to go.
So all of this fits with what I would have assumed anyway, but so many of
the samples use COINIT_APARTMENTTHREADED (either explicitly or by default)
that I'm still not sure.
Since DirectShow is all about multithreading, and it is constantly
emphasized how DirectShow components need to ensure proper threading
behavior, it seems clear that no harm would be done by using
COINIT_MULTITHREADED if the only COM objects in the application are
DirectShow objects, and in fact performance could be significantly enhanced.
Is that right?
More generally (yes, I realize this isn't strictly DirectShow-only at this
point), are there other COM areas where I would run into problems using
COINIT_MULTITHREADED that are likely to come up while writing a
DirectShow-aware application? Either other DirectX APIs, or non-DirectX
APIs?
Am I being silly to even worry about this?
Pete
--
Where am I going?
And why am I in this handbasket?
No, this is a valid concern. Lots of stuff stops working if you use
COINIT_MULTITHREADED, such as ActiveX controls, Shell functions, etc.
My applications are client/server based, so I typically use
COINIT_MULTITHREADED on the server (since I don't use anything else that
may fail), and COINIT_APARTMENTTHREADED on the client side. I haven't
had any problems, but to be honest I'm not an expert on what goes on
behind the scenes with the various threading models.
Keep in mind that COM is initialized per thread, so you can have
different threading models by putting things in different threads.
--
New to newsgroups? Read: http://dev.6581.com/newsgroups.html
> I know very little about this, but I've always just used CoInitialize(NULL)
> in dshow, and it works just fine. I've also written filters, my apps are
> heavily threaded, etc--and I don't see any problems.
I also use CoInitialize(NULL) with no issues.
--
1. If there are questions above, please answer them in-line. In any case,
don't top post. (Otherwise, don't expect a followup) See
http://tinyurl.com/22ubu
2. Remove this signature from your response if your newsreader isn't
intelligent enough to do it.
3. Common courtesy is to followup if someone helped you or if you found a
solution on your own.
Huh?
I guess I need to look harder on MSDN for information about how COM works,
and how this "marshalling" stuff works. In particular, with your comment
about using different CoInitializeEx calls on different threads, now I'm
wondering what happens for threads for which COM isn't explicitly
initialized at all (e.g. those created by existing COM objects). Or are
there rules about how a thread created from within an existing COM object
must be created?
I'm a bit surprised that this isn't a more common topic of discussion, here
and in the SDK docs, given that it sounds like it *isn't* a trivial answer.
I'm also a little confused as to which function calls are marshalled. Is
EVERY function call to a COM interface marshalled? Or does that only happen
for particular well-defined interfaces? When I was stepping through various
filter functions, I didn't notice any extra code in between function calls
and their targets, which seems to imply that the marshalling doesn't always
happen.
Oh well...I think I'll stick to APARTMENTTHREADED for now, until a) I
understand the issues better, and b) I run into some sort of performance
bottleneck that appears to be caused by the COM interface marshalling.
Thanks,
Pete
>> >Am I being silly to even worry about this?
>> No, this is a valid concern. Lots of stuff stops working if you use
>> COINIT_MULTITHREADED, such as ActiveX controls, Shell functions, etc.
>> [...]
>> Keep in mind that COM is initialized per thread, so you can have
>> different threading models by putting things in different threads.
>Huh?
>
>I guess I need to look harder on MSDN for information about how COM works,
>and how this "marshalling" stuff works. In particular, with your comment
>about using different CoInitializeEx calls on different threads, now I'm
>wondering what happens for threads for which COM isn't explicitly
>initialized at all (e.g. those created by existing COM objects). Or are
>there rules about how a thread created from within an existing COM object
>must be created?
I'm no COM expert, but I do see that e.g. CAMThread in the DS base
classes initializes COM when it starts up, so it seems like you have to
do it explicitly. I think you only have to initialize COM if you create
COM objects, but don't trust my word on that.
>I'm a bit surprised that this isn't a more common topic of discussion, here
>and in the SDK docs, given that it sounds like it *isn't* a trivial answer.
Most people, like me, probably don't fully understand how COM works, and
probably see no need to understand it fully. It's certainly not a
trivial subject. Personally, I find COM to be extremely inelegant, and I
avoid it as much as I can.
And as someone once said, only people with beards and glasses understand
COM:
http://images.amazon.com/images/P/1861000111.01.LZZZZZZZ.gif
And I have neither. :)
>I'm also a little confused as to which function calls are marshalled. Is
>EVERY function call to a COM interface marshalled? Or does that only happen
>for particular well-defined interfaces? When I was stepping through various
>filter functions, I didn't notice any extra code in between function calls
>and their targets, which seems to imply that the marshalling doesn't always
>happen.
Indeed, and I've wondered the same thing. I've never seen any
marshalling in the debugger.
I'm having my eyes checked and not shaving from now on. It's final.
> And as someone once said, only people with beards and glasses understand
> COM:
ROFL!
I like the idea of COM, of being able to instantiate what amounts to C++
classes the code for which lives elsewhere. Of course, I suppose one could
argue that COM doesn't provide much more in that respect than DLLs do. But
I'm guessing that a COM expert would reply that it's the parts we don't
understand (like marshalling and remote execution) that make COM truly
useful. Also, I do think it's useful that COM object implementations can be
changed without having to use the exact same DLL name.
I agree that the general implementation of COM seems sort of clunky. But
then, that's pretty much par for the course for OS APIs that evolve, rather
than being created from scratch by someone who's already an expert from
doing in wrong for years. :)
> > [...] When I was stepping through various
> >filter functions, I didn't notice any extra code in between function
calls
> >and their targets, which seems to imply that the marshalling doesn't
always
> >happen.
>
> Indeed, and I've wondered the same thing. I've never seen any
> marshalling in the debugger.
Well, I'm too curious about that to just leave the question unanswered. If
I manage to find any documentation that explains it reasonably well, I'll
post back with the answer. I don't have a beard, and I wear contacts most
of the time, but maybe that means I can at least gain a partial
understanding of COM.
Pete
>> Most people, like me, probably don't fully understand how COM works, and
>> probably see no need to understand it fully. It's certainly not a
>> trivial subject. Personally, I find COM to be extremely inelegant, and I
>> avoid it as much as I can.
>I like the idea of COM, of being able to instantiate what amounts to C++
>classes the code for which lives elsewhere. Of course, I suppose one could
>argue that COM doesn't provide much more in that respect than DLLs do. But
>I'm guessing that a COM expert would reply that it's the parts we don't
>understand (like marshalling and remote execution) that make COM truly
>useful. Also, I do think it's useful that COM object implementations can be
>changed without having to use the exact same DLL name.
>
>I agree that the general implementation of COM seems sort of clunky. But
>then, that's pretty much par for the course for OS APIs that evolve, rather
>than being created from scratch by someone who's already an expert from
>doing in wrong for years. :)
It just seems like it's way too complex for what it's trying to achieve.
It's a good idea, but it's a pain to do complicated stuff with it. Not
unlike DirectShow. :) Very few developers have the capability or the
time to truly come to grips with complex technologies such as these.
>> > [...] When I was stepping through various
>> >filter functions, I didn't notice any extra code in between function calls
>> >and their targets, which seems to imply that the marshalling doesn't always
>> >happen.
>> Indeed, and I've wondered the same thing. I've never seen any
>> marshalling in the debugger.
>Well, I'm too curious about that to just leave the question unanswered. If
>I manage to find any documentation that explains it reasonably well, I'll
>post back with the answer. I don't have a beard, and I wear contacts most
>of the time, but maybe that means I can at least gain a partial
>understanding of COM.
Sounds good. :) Please let me know if you find out something, because
I'm interested in learning the details.
> And as someone once said, only people with beards and
> glasses understand COM:
I have neither, but here it is how I understand it...
CoInitialize(NULL) is the same as
CoInitializeEx(NULL,COINIT_APARTMENTTHREADED) which is not
the default of CoInitializeEx(), since
CoInitializeEx(NULL,0) is the same as
CoInitializeEx(NULL,COINIT_MULTITHREADED). On a side note,
OleInitialize(NULL) calls
CoInitializeEx(NULL,COINIT_APARTMENTTHREADED) so it is
equivalent to CoInitialize(NULL).
You need to initialize COM on each and every thread that is
going to use the OLE32.DLL facilities directly or
indirectly. This is not the same as just using COM objects.
For example, nothing prevents you to use a coclass on your
own by loading its server and accessing its factory without
initializing COM but, since many coclasses will then use
OLE32 internally or have threading issues, it is safer to
always initialize COM when the implementation details of the
coclass are not known and access it through OLE32.
Here is some background information on STAs and MTAs:
http://msdn.microsoft.com/library/en-us/dnesscom/html/c5interfaceimplementationrevisited.asp
http://msdn.microsoft.com/library/en-us/dnesscom/html/objectsinterfacesapartments.asp
--
/**
* Alessandro Angeli
*
* MVP :: Digital Media
*
* a dot angeli at biosys dot net
*/
Interesting. When I first read the docs for CoInitializeEx, I saw this: "If
neither concurrency model is specified by the dwCoInit parameter, the
default is COINIT_APARTMENTTHREADED". But then I read your post, and went
and looked more closely; sure enough, COINIT_MULTITHREADED is defined as 0
(i.e. no bit set).
Thanks for your informative post. It's unfortunate that the docs are not
only complicated by the fact that the API is complicated, but that they are
complicated by the fact that they don't make sense. :(
Pete
For what it's worth, fair use doctrine would generally dictate that you
could quote relatively small portions of the book without any problem at
all.
> CoInitialize(NULL) and CoInitializeEx (with the COINIT_APARTMENTTHREADED
> parameter set) do the same thing – they create a COM object that is in a
> single-threaded apartment. The COM framework insures that any object in
> this single-threaded apartment is safe from concurrent thread access. If
the
> apartment contains multiple objects only one of the objects can execute at
> any given time. If other threads wish to communicate with an object within
> this apartment they must go through COM.
Why? Does the book say what mechanism exists to stop one thread from
calling a method that exists on a COM object created in a different thread?
The method would, of course, execute in the calling thread, not the creating
thread. But the creating thread could still have stuff going on with the
created object as well, creating the concurrency issue.
I'm not aware of any mechanism in Windows that isolates code in this way.
The more I hear about it, the more I wonder if this marshalling stuff
applies only to some sort of specific inter-object communications paradigm,
and not to cross-object method calls generally. If COM implements an
inter-object communications paradigm unique to COM, I can see how it could
control access in this way. Otherwise, I don't.
Other than, of course, sticking itself into the vtable for every COM object
created and intercepting all of the function calls. But I've seen no
evidence in the debugger that this happens.
Pete
> Why? Does the book say what mechanism exists to stop one thread from
> calling a method that exists on a COM object created in a different thread?
> The method would, of course, execute in the calling thread, not the creating
> thread. But the creating thread could still have stuff going on with the
> created object as well, creating the concurrency issue.
>
> I'm not aware of any mechanism in Windows that isolates code in this way.
> The more I hear about it, the more I wonder if this marshalling stuff
> applies only to some sort of specific inter-object communications paradigm,
> and not to cross-object method calls generally. If COM implements an
> inter-object communications paradigm unique to COM, I can see how it could
> control access in this way. Otherwise, I don't.
>
> Other than, of course, sticking itself into the vtable for every COM object
> created and intercepting all of the function calls. But I've seen no
> evidence in the debugger that this happens.
>
Now you are making my head hurt again. The following is quoted from the
MSDN Library docs - I think it may be your answer:
Apartment-threading, the default model for earlier versions of Windows
NT, while allowing for multiple threads of execution, serializes all
incoming calls by requiring that calls to methods of objects created by
this thread always run on the same thread – the apartment/thread that
created them. In addition, calls can arrive only at message-queue
boundaries (i.e., only during a PeekMessage, SendMessage,
DispatchMessage, etc.). Because of this serialization, it is not
typically necessary to write concurrency control into the code for the
object, other than to avoid calls to PeekMessage and SendMessage during
processing that must not be interrupted by other method invocations or
calls to other objects in the same apartment/thread.
Yeah, I've seen that. However, I've also stepped through my filter's code
in the debugger, and have seen nothing that would suggest COM is actually
doing that. Stepping into a COM object function looks just like I'd expect
it to, without any intermediate COM code that might implement what the docs
say it does.
Some possibilities that occur to me:
* COM only does this serialization for certain kinds of COM
interfaces/methods, ones that COM is involved in the call in the first place
* COM does not bother to override the vtable (which it would have to do
in order to intercept all function calls to a COM object) until either a) a
new thread is created, or b) a different thread attempts to call into a COM
object created in a previously created thread.
The former seems wrong, since the docs would (should?) be more explicit
about this. They seem to be saying the serialization happens for all COM
methods, not just some subset.
The latter also seems wrong, because the vtable doesn't appear to be
overridden from the outset, and because COM doesn't control creation of new
threads (i.e. how would it know to go fix up all the vtables when a new
thread is created), and because COM has no way to know if a different thread
tries to call into a COM object created in a previously created thread.
Anyway, so far I've been better at saying why it doesn't work a certain way.
I'll admit that's not a very productive approach. :) I've got a little
free time right now, so maybe I'll be able to turn something up in the MSDN
docs that I didn't notice before.
Pete
Well, I finally took some time to read through what appear to be the
relevant MSDN documentation. Here's the executive summary: :)
* As mentioned before, each thread that uses the COM library (for
example, needs to call CoCreateInstance) needs to call
CoInitialize/CoInitializeEx.
* The parameter passed to CoInitialize/Ex defines the "apartment" model
for that thread.
* A COM client application always has at least one apartment. It may
have zero or more single-threaded apartments, and it may have zero or one
(no more than one) multi-threaded apartments.
* Any thread that calls CoInitializeEx with the COINIT_MULTITHREADED
option is put into the single multi-threaded apartment allowed for a
process. Any thread that calls CoInitializeEx with the
COINIT_APARTMENTTHREADED option causes a new apartment to be created, and
that thread winds up being the only thread in that apartment.
* For an in-proc COM server, the registry specifies what kind of
threading model the server will support. The apartment in which that COM
object is created needs to match the registry entry (if the entry is "Both",
then either kind of apartment can create the object).
* As near as I can tell, BY CONVENTION ONLY (i.e. COM has no way to
enforce this, other than telling you that you have to do it this way), for
any given COM object, all execution of the code for that COM object's
implementation MUST occur on a single thread, the thread where that object
was created, if that object is a single-threaded apartment object.
* "Marshalling" (for those not familiar with the term) means that
function parameters are packaged up and transmitted by proxy to the actual
COM server (which may or may not be in the same process).
According to the COM documentation, marshalling normally happens by default.
However, as near as I can tell, this is only true for the not-in-proc server
case (the documentation treats in-proc servers as an atypical special case,
even though in DirectShow and many other uses of COM, in-proc servers are
almost all you ever see). In particular, a COM client is required to call a
couple of specific COM functions when passing an interface pointer from one
apartment to another: CoMarshallInterThreadInterfaceInStream is used to
package up the interface, and CoGetInterfaceAndReleaseStream is used to
unwrap it for use in a different apartment.
It's up to the client application to keep track of which threads are in
which apartments, though of course the fact that every thread -- except
those residing in the single multi-threaded apartment -- is in its own
apartment does simplify this. You only need to keep track of the threads
that are in the multi-threaded apartment.
Once an interface has been marshalled with the above-mentioned functions,
further marshalling happens automatically.
And yes, marshalling is implemented by messing with the vtable. The
object's default vtable is replaced by a "proxy" on the client side and a
"stub" on the server side, the two of which talk to each other to copy
function call parameters and return values back and forth.
As far as performance goes, the marshalling is always done using window
messages. So each apartment is required to have its own message loop
(implemented by the client, not by COM). COM then creates a hidden window
that takes advantage of the message loop to handle the messages used for
marshalling. This is how synchronization between objects occurs.
Of course, it becomes clear that any time parameters need to be marshalled,
performance will suffer. The synchronization alone could be a minor issue
in DirectShow, but if one was passing actual sample data (rather than just
pointers to sample data, as it appears normally happens), that's a lot of
extra data copying.
With out-of-proc servers, there's no way for the client to call into the
object directly, so the marshalling happens transparently, implemented at
the same time that the inter-process/inter-computer communications is set
up. I suppose there's no technical reason one couldn't implement DirectShow
objects as out-of-proc servers. For example, you might have one computer
handle all of the graphical display and streaming, while another deals with
UI and content control, using COM to interface with the display and
streaming server.
Finally, with respect to what option a DirectShow application should use, it
*appears* to me that it's actually more appropriate to use the
COINIT_MULTITHREADED option. I seem to recall something in the DirectShow
documentation that discussed executing a given object only on a single
thread anyway (something about the graph and UI having one thread, and the
streaming with filters being on another, with a special DirectShow version
of messaging uses to communicate between the threads), but there are also
admonishments to make sure each DirectShow object properly implements
inter-thread synchronization when they might be called from different
threads.
Several articles in the COM documentation are useful, but the ones that
wound up being most significant were these two:
http://msdn.microsoft.com/library/en-us/com/htm/aptnthrd_68s3.asp?frame=true
http://msdn.microsoft.com/library/en-us/com/htm/aptnthrd_1ik3.asp?frame=true
(tiny URLs: http://tinyurl.com/69bvn and http://tinyurl.com/3s4yc)
They have the most detail on this specific issue.
(Note: I did my research using an old version of MSDN, that comes with VS
6.0...when I went to grab the online links above, I discovered that there's
now a "more convenient" way to get marshalled interfaces from other
apartments, using a new interface called IGlobalInterfaceTable. This is
only barely documented in the CD-based MSDN I was looking at, which is why
it didn't make it into the general discussion I was reading. It appears one
could use this rather than ).
As far as using other types of COM objects that require the single-threaded
apartment model, it appears to me that as long as their registry entry is
set correctly (and it ought to be), you would not be able to create an
"Apartment" model COM object in a multi-threaded apartment, nor create a
"Free" model COM object in a single-threaded apartment. So if you're going
to use those kinds of objects, it's important to set your thread correctly
in the CoInitializeEx call, but it sounds like what would happen is that the
object would not get created, rather than you running into some
hard-to-debug thread issues later. To use those kinds of COM objects with
DirectShow, I think it would work best to put all the DirectShow stuff in
COINIT_MULTITHREADED threads, and the single-threaded objects in their own
COINIT_APARTMENTTHREADED threads.
Caveat: all of the above was gleaned from a few hours of reading the MSDN
documentation. I am NOT a COM expert by any means, and I could have this
all horribly wrong. It's my sincere hope that if there's anyone else out
there that knows more than I now know, they will speak out and provide their
comments, ESPECIALLY if I've gotten some or all of this wrong.
My apologies to anyone whose brain I made hurt. If it makes you feel
better, I will probably be recovering all evening from the process of trying
to decipher the COM documentation. :)
Pete
> Well, I finally took some time to read through what appear to be the
> relevant MSDN documentation. Here's the executive summary: :)
Good summary Pete (my brain was a little fried before reading it, after
...).
Thanks. Let's just hope there's a semblance of accuracy there...I did my
best to understand the issues, but I just never really know until I actually
have to code around them. :)
Pete
> Keep in mind that COM is initialized per thread, so you can have
> different threading models by putting things in different threads.
BTW, what about multi processor machines or hyperthreading?
I seem to have sporadic failures ("CoInitialize has not been called") in
a media player type of program. This went away after deactivating
hyperthreading in the BIOS. Can anyone confirm this?
Ulf
>> Keep in mind that COM is initialized per thread, so you can have
>> different threading models by putting things in different threads.
>BTW, what about multi processor machines or hyperthreading?
Shouldn't make a difference as long as you initialize COM in each
thread. My applications run on multi-processor machines and machines
with hyperthreading, and I've never had a problem.
the statement that Windows automatically marshals is false. At least in
practical terms.
If you (for example) try and call back into an STA thread (such as VB) with
an event from a different thread, it will NOT work reliably. You must cause
the callback to be raised by the original thread, which you can do by a
method whose name is improbably long (CoMarshalInterThreadInterfaceInStream)
or be posting to a message loop running in the original thread.
So if you make an ActiveX control which is to run under VB, which create's
its own threads that raise events from the control, the control itself has
to have a message loop which you should post to.
I doubt this is your issue, but who knows. There are a couple of MSDN
articles on this.
Most DS internal stuff is free threaded (I think) which means it doesn't
have to be marshalled, but you can get concurrency issues.
Iain
"Peter Duniho" <NpOeS...@NnOwSlPiAnMk.com> wrote in message
news:10ge5te...@corp.supernews.com...
I don't know where you saw that statement. I specifically said that the
application is responsible for properly setting up marshalling for an
in-proc server, and I even mentioned the very function you describe.
> Most DS internal stuff is free threaded (I think) which means it doesn't
> have to be marshalled, but you can get concurrency issues.
That's the conclusion I came to as well. See my post.
I'm all for corrections, but at the very least, I'd like people correcting
me to read my post first. Thanks...
Pete
Obviously I misread what you posted in my rush.
Iain
"Peter Duniho" <NpOeS...@NnOwSlPiAnMk.com> wrote in message
news:10gksul...@corp.supernews.com...