COM Interop - switching to STA Threading - how to?

S

Simon Woods

Hi

I am working on a web service whose business layer has a hybrid of COM
and .Net business objects. I can get the data from the .net business
objects fine. I have tests which prove that the COM business objects
work fine when accessing them from VS's test suite. However, when I try
and access them via thew web service, an error is generated. (Exception
from HResult: 0x800A0062). I managed to replicate this problem using
various testing scenarios and got rid of the problem by explicitly
setting the threading to STA.

So I figured that, when invoking any of my COM methods, I first need to
switch to an STA Thread.

I am struggling to know how to do this. I have tried 2 methods.

a) Getting hold of the current thread and setting its apartment state
to STA, but that generates an error on the development server
b) Explicitly trying to create a new STA thread. Here's my code ... but
be warned I'm a threading newbie!


public class STATaskExecutor
{

static Thread _thread;
static MyCOMDLL.CFactory _factory;
static MyCOMDLLTypes.IMyCOMType _myCOMObject;
static string _var1;
static string _var2;
static string _var3;
static bool _created;

public static MyCOMDLLTypes.IMyCOMType
CreateCOMObject(MyCOMDLL.CFactory p_factory, string p_param1, string
p_param2, string p_param3)
{
_factory = p_factory;
_var1 = p_param1;
_var2 = p_param2;
_var3 = p_param3;

_thread = new Thread(CreateCOMObjectInstance);
_thread.SetApartmentState(ApartmentState.STA);
_thread.Start();

while (!_created){ }

return _COMObject;
}

[STAThread]
private static void CreateCOMObjectInstance()
{
try
{
_COMObject = _factory.CreateIMyInterface(_var1, _var2,
_var3;
}
catch (Exception ex)
{

}
finally
{
_created = true;
_thread = null;
}
}

I am finding that the CreateCOMObjectInstance executes, but it is
failing with the same error.

a) Could it be failing for some other reason?
b) Where am I going wrong in relation to the implementation of creating
an new STA thread
(finally but non-essentially since it doesn't work)
c) Is there a more elegant ways of passing around the COM function (e.g.
as a first class function). I don't know if you can pass round COM
methods as first class functions!

Many thanks

Simon
 
P

Peter Duniho

Simon said:
Hi

I am working on a web service whose business layer has a hybrid of COM
and .Net business objects. I can get the data from the .net business
objects fine. I have tests which prove that the COM business objects
work fine when accessing them from VS's test suite. However, when I try
and access them via thew web service, an error is generated. (Exception
from HResult: 0x800A0062).

Please don't post error codes only. The vast majority of us will have
to look the code up for it to be any use anyway, so you should do that
work yourself and post the exact error name and message for that code.

(And no, I did not bother to look the code up, so I don't really know
what error you're dealing with).
I managed to replicate this problem using
various testing scenarios and got rid of the problem by explicitly
setting the threading to STA.

So I figured that, when invoking any of my COM methods, I first need to
switch to an STA Thread.

That is not an uncommon requirement for COM objects.
I am struggling to know how to do this. I have tried 2 methods.

a) Getting hold of the current thread and setting its apartment state
to STA, but that generates an error on the development server

Generally you cannot change the apartment model for a thread once it's
been started. The real COM rule is that you can't change the apartment
model after you've executed any COM functions, but in .NET it's easiest
to set the apartment model before the thread starts, to ensure that
happens (and in fact, for a managed thread, it's the only way).
b) Explicitly trying to create a new STA thread. Here's my code ... but
be warned I'm a threading newbie! [...]

Yes, that's apparent from the code posted. :)

The biggest threading problems with your code are:

-- Failure to synchronize access to static members. If two
different threads both call the "CreateCOMObject()" method at the same
time, at best only one will succeed and at worst, one will corrupt the
attempt of the other and neither will perform correctly.

-- A special-case of the above is the use of the "_created"
variable. Assuming synchronization elsewhere, you can avoid
interference between client threads. But without that variable being
"volatile", you could still find that the calling thread simply runs
indefinitely in the "while(!_created)" loop.

-- Polling using the "_created" flag is really not a very good idea
anyway. Instead, you should use some kind of object for signaling, and
allow the client thread to simply block in a non-CPU-consuming statement
(e.g. WaitHandle.WaitOne(), Monitor.Wait(), etc.)

-- You also never really use the "_thread" static variable, so it's
not clear why that exists at all. You can just use a local variable in
the public method, so that you can set the apartment state and start the
thread.

There may be other issues. Those are the ones that are immediately
apparent.

That said, the above is all probably moot, because I don't think you're
solving the COM issue in the correct way.
[...]
I am finding that the CreateCOMObjectInstance executes, but it is
failing with the same error.

Right. I'm not a COM expert, but my recollection is that it's not just
that the object needs to be created using an STA thread, but that you
need to use that STA thread any time you call some method of your COM
object.

In the code you posted, the thread used to create the object disappears
as soon as you've created the object. This does the object no good for
its long-term viability.
a) Could it be failing for some other reason?
b) Where am I going wrong in relation to the implementation of creating
an new STA thread
(finally but non-essentially since it doesn't work)

You set the apartment state correctly, but because it's a whole new
thread, it's not helping matters.

You haven't posted a concise-but-complete code example, so it's not
entirely clear how you're using the COM classes/objects here. But you
seem to be passing a factory instance to your creation method. It seems
possible (likely, given your description, even) that you need to execute
methods of that factory on the same STA thread where the factory itself
was created.

If you have a GUI application using Forms or WPF, the usual solution is
to use Control.Invoke() (Forms) or Dispatcher.Invoke() (WPF) to execute
a delegate on the main STA thread any time you need to interact with an
STA-only COM object. This keeps all of the COM operations on the same
thread.

If you're not using Forms or WPF, it gets slightly more complicated.
There are lots of previous discussions in this newsgroup, if you go back
far enough in the archives. But someone just recently asked about a
similar issue, in which they needed to execute code on a specific
thread, but didn't have a GUI application. It wasn't COM-specific, but
the same basic issues apply here. You can read that discussion here:
http://groups.google.com/group/micr....csharp/browse_thread/thread/7c6e69080e2c3232
c) Is there a more elegant ways of passing around the COM function (e.g.
as a first class function). I don't know if you can pass round COM
methods as first class functions!

I don't know what you mean by "first class function", nor do I
understand what this has to do with your STA thread question. Certainly
using COM interop, methods in a COM class work the same as methods in a
..NET class, and can be encapsulated by a delegate instance.

But there's nothing about your question that suggests that's relevant,
nor is it clear that's what you mean by "first class function".

Pete
 
S

Simon Woods

Pete

That was a tremendously helpful post. Many thanks.

I think the main thing was that, as you said, I needed to create the
factory on the same thread as the COM object it was creating. Obvious -
except I missed it!

So I have a solution which at least now at least creates the COM object,
but I'm still a bit unsure as to whether I've got it right in threading
terms. I'm a bit concerned


a) when I step it (and break it) in VS2008 it gen's a "Object
synchronization method was called from an unsynchronized block of code"
error and other times having executed if I break on

" return _MyCOMObject;"

and try and look at it's properties, intellisense gives me a
'System.AccessViolationException' even though the correct data appears
to be being returned to the browser, it I just let it run. I'm wondering
if that is a feature of trying to debug a parallel process serially.

b) I would have thought I needed an instruction after starting the
thread so that it just doesn't return a null object

c) that I'm not tidying up properly, but no doubt there are other things
I just don't know enough about.


Anyway, here's my current code

public class STATaskExecutor
{
static MyCOMDllTypes.IMyCOMInterface _MyCOMObject;
static readonly object _COMObjectLock = new object();
static string _var1;
static string _var2;
static string _var3;

public static MyCOMDllTypes.IMyCOMInterface
CreateMyCOMObject(string p_param1, string p_param2, string p_param3)
{

_var1 = p_param1;
_var2 = p_param2;
_var3 = p_param3;

var thread = new Thread(CreateMyCOMObjectInstance);
thread.SetApartmentState(ApartmentState.STA);

try
{
thread.Start();
return _MyCOMObject;
}
catch (Exception ex)
{
thread.Abort();
throw (ex);
}
}

private static void CreateMyCOMObjectInstance()
{
lock (_COMObjectLock)
{
var factory = new MyCOMDll.CFactory();
_MyCOMObject = factory.CreateMyCOMObject(_var1, _var2,
_var3);
}
}
}


With reference to first class functions, I should have said first class
data types. I was hoping to generalise this into a form whereby I could
execute different COM business methods with different method signatures,
no of params, return types etc, so that I had just one STA executor, if
you will.

Many thx once again, though

Simon


Simon said:
Hi

I am working on a web service whose business layer has a hybrid of COM
and .Net business objects. I can get the data from the .net business
objects fine. I have tests which prove that the COM business objects
work fine when accessing them from VS's test suite. However, when I
try and access them via thew web service, an error is generated.
(Exception from HResult: 0x800A0062).

Please don't post error codes only. The vast majority of us will have to
look the code up for it to be any use anyway, so you should do that work
yourself and post the exact error name and message for that code.

(And no, I did not bother to look the code up, so I don't really know
what error you're dealing with).
I managed to replicate this problem using various testing scenarios
and got rid of the problem by explicitly setting the threading to STA.

So I figured that, when invoking any of my COM methods, I first need
to switch to an STA Thread.

That is not an uncommon requirement for COM objects.
I am struggling to know how to do this. I have tried 2 methods.

a) Getting hold of the current thread and setting its apartment state
to STA, but that generates an error on the development server

Generally you cannot change the apartment model for a thread once it's
been started. The real COM rule is that you can't change the apartment
model after you've executed any COM functions, but in .NET it's easiest
to set the apartment model before the thread starts, to ensure that
happens (and in fact, for a managed thread, it's the only way).
b) Explicitly trying to create a new STA thread. Here's my code ...
but be warned I'm a threading newbie! [...]

Yes, that's apparent from the code posted. :)

The biggest threading problems with your code are:

-- Failure to synchronize access to static members. If two different
threads both call the "CreateCOMObject()" method at the same time, at
best only one will succeed and at worst, one will corrupt the attempt of
the other and neither will perform correctly.

-- A special-case of the above is the use of the "_created" variable.
Assuming synchronization elsewhere, you can avoid interference between
client threads. But without that variable being "volatile", you could
still find that the calling thread simply runs indefinitely in the
"while(!_created)" loop.

-- Polling using the "_created" flag is really not a very good idea
anyway. Instead, you should use some kind of object for signaling, and
allow the client thread to simply block in a non-CPU-consuming statement
(e.g. WaitHandle.WaitOne(), Monitor.Wait(), etc.)

-- You also never really use the "_thread" static variable, so it's not
clear why that exists at all. You can just use a local variable in the
public method, so that you can set the apartment state and start the
thread.

There may be other issues. Those are the ones that are immediately
apparent.

That said, the above is all probably moot, because I don't think you're
solving the COM issue in the correct way.
[...]
I am finding that the CreateCOMObjectInstance executes, but it is
failing with the same error.

Right. I'm not a COM expert, but my recollection is that it's not just
that the object needs to be created using an STA thread, but that you
need to use that STA thread any time you call some method of your COM
object.

In the code you posted, the thread used to create the object disappears
as soon as you've created the object. This does the object no good for
its long-term viability.
a) Could it be failing for some other reason?
b) Where am I going wrong in relation to the implementation of
creating an new STA thread
(finally but non-essentially since it doesn't work)

You set the apartment state correctly, but because it's a whole new
thread, it's not helping matters.

You haven't posted a concise-but-complete code example, so it's not
entirely clear how you're using the COM classes/objects here. But you
seem to be passing a factory instance to your creation method. It seems
possible (likely, given your description, even) that you need to execute
methods of that factory on the same STA thread where the factory itself
was created.

If you have a GUI application using Forms or WPF, the usual solution is
to use Control.Invoke() (Forms) or Dispatcher.Invoke() (WPF) to execute
a delegate on the main STA thread any time you need to interact with an
STA-only COM object. This keeps all of the COM operations on the same
thread.

If you're not using Forms or WPF, it gets slightly more complicated.
There are lots of previous discussions in this newsgroup, if you go back
far enough in the archives. But someone just recently asked about a
similar issue, in which they needed to execute code on a specific
thread, but didn't have a GUI application. It wasn't COM-specific, but
the same basic issues apply here. You can read that discussion here:
http://groups.google.com/group/micr....csharp/browse_thread/thread/7c6e69080e2c3232

c) Is there a more elegant ways of passing around the COM function
(e.g. as a first class function). I don't know if you can pass round
COM methods as first class functions!

I don't know what you mean by "first class function", nor do I
understand what this has to do with your STA thread question. Certainly
using COM interop, methods in a COM class work the same as methods in a
.NET class, and can be encapsulated by a delegate instance.

But there's nothing about your question that suggests that's relevant,
nor is it clear that's what you mean by "first class function".

Pete
 
P

Peter Duniho

Simon said:
[...]
a) when I step it (and break it) in VS2008 it gen's a "Object
synchronization method was called from an unsynchronized block of code"
error and other times having executed if I break on

" return _MyCOMObject;"

and try and look at it's properties, intellisense gives me a
'System.AccessViolationException' even though the correct data appears
to be being returned to the browser, it I just let it run. I'm wondering
if that is a feature of trying to debug a parallel process serially.

Without a concise-but-complete code example so that one can reproduce
the behavior you're seeing, it would be hard to say what might cause
errors like that.

However, looking at your code, it appears to me that it will generally
work _better_ if you step through it. It's got a serious
synchronization bug that will result in incorrect results most of the time.

It may well be that with unmanaged code involved (i.e. the COM object),
there are errors that show up when debugging. I would expect them
normally to be benign, but an access violation is hardly benign, so
there may be something else going on.
b) I would have thought I needed an instruction after starting the
thread so that it just doesn't return a null object

Yes, you do. Very much.
c) that I'm not tidying up properly, but no doubt there are other things
I just don't know enough about.

Other than failing to synchronize the worker thread with the caller, to
ensure you return a valid object, the other big problem is that it's not
the thread you create that needs the synchronization, but rather the
CreateMyCOMObject() method. The way the code is now, multiple callers
can still overwrite your static variables and cause problems for an
already-executing creator thread.

That said, given the design as you've presented it, I don't feel there's
really any need for the shared static variables at all. Rather than
locking, you can instead achieve thread safety simply by not sharing
data between threads at all.

Try this version:

public class STATaskExecutor
{
public static MyCOMDllTypes.IMyCOMInterface
CreateMyCOMObject(string p_param1, string p_param2, string p_param3)
{
MyCOMDllTypes.IMyCOMInterface _MyCOMObject;
Exception excThread = null;
EventHandle eh = new AutoResetEvent(false);

var thread = new Thread(delegate()
{
try
{
var factory new MyCOMDll.CFactory();

_MyCOMObject = factory
.CreateMyCOMObject(p_param1, p_param2, p_param3);
}
catch (Exception exc)
{
excThread = exc;
}

eh.Set();
});

thread.SetApartmentState(ApartmentState.STA);
thread.Start();

eh.WaitOne();
if (excThread != null)
{
throw (excThread);
}

return _MyCOMObject;
}
}

Two main things to observe in the code above:

-- Each object creation thread gets its own private copy of the
variables used for the operation

-- This particular example achieves that by using an anonymous
method. Doing it that way causes the compiler to automatically generate
a hidden class that stores the "captured" variables (i.e. those that you
want to share between the calling code and the thread executing) and is
used during the execution of the anonymous method.

Now, all that said...it seems to me that you _still_ have this issue
that after this method returns, you've got an instance of a STA COM
object, the owning thread for which has now terminated. Usually, STA
implies more than just having to be created on a STA thread; that thread
also has to remain around, and be used, for any use of that COM object
the thread owns.

As I said, I'm not a COM expert and maybe there's some exception
relevant to your own code. But you should look very carefully at that
possibility. You may need a single, long-running thread dedicated to
both the creation and use of these COM objects. Note that if you do
implement it that way, you can also avoid creating a new factory each
time you want a new COM object; you can just create one factory for the
thread, and reuse it each time you need a new COM object.

Pete
 
S

Simon Woods

Thx very much again Pete - see in-line

That said, given the design as you've presented it, I don't feel there's
really any need for the shared static variables at all. Rather than
locking, you can instead achieve thread safety simply by not sharing
data between threads at all.

OK - I'm sure you are correct.
Try this version:

<snip>

Many thx. It works well.
Now, all that said...it seems to me that you _still_ have this issue
that after this method returns, you've got an instance of a STA COM
object, the owning thread for which has now terminated. Usually, STA
implies more than just having to be created on a STA thread; that thread
also has to remain around, and be used, for any use of that COM object
the thread owns.

Do you mean that when the COM object isn't marshalled onto the main
thread rather only a pointer? If so, when the thread terminates is the
object's reference count not decremented? (I know you say you are not a
COM expert ... but you do seem to know a fair bit!)

As I said, I'm not a COM expert and maybe there's some exception
relevant to your own code. But you should look very carefully at that
possibility. You may need a single, long-running thread dedicated to
both the creation and use of these COM objects. Note that if you do
implement it that way, you can also avoid creating a new factory each
time you want a new COM object; you can just create one factory for the
thread, and reuse it each time you need a new COM object.

Again, I am sorry for my ignorance (see PS1 below), do you mean have a
static thread object (pool) which instantiates the factory at startup
and requires a stop-type method?


Many thx again

Simon


PS1 - can you recommend any books references for me to get up to speed
on this?

PS2 - as a matter of interest, do you know if this is easily
implementable (or better implemented) using the Px/Rx frameworks? I'm
wondering whether the disposing of the COM objects may be better
achieved with this.
 
P

Peter Duniho

Simon said:
[...]
Now, all that said...it seems to me that you _still_ have this issue
that after this method returns, you've got an instance of a STA COM
object, the owning thread for which has now terminated. Usually, STA
implies more than just having to be created on a STA thread; that thread
also has to remain around, and be used, for any use of that COM object
the thread owns.

Do you mean that when the COM object isn't marshalled onto the main
thread rather only a pointer? If so, when the thread terminates is the
object's reference count not decremented?

I'm not all that concerned about the referencing counting aspect. I
think that the .NET COM interop takes care of that (or maybe you have to
dispose the COM object explicitly...I don't recall).

Rather, it's the fact that once you have this COM instance, presumably
you intend to actually call the COM instance at some point. But for an
STA COM object, you need to make sure you always use that object on the
same thread, and I'm pretty sure that thread needs to be the one you
used to create the object.

But with the code as it stands now, the thread that creates the object
is gone before you ever get a chance to use it. It's impossible to also
use the thread for calling members of the COM object.

At least, that's how I understand MTA and STA COM objects. MTA objects
are written with synchronization in mind and are thread-safe, so they
can be used in a multi-threaded environment without problem.

STA objects are owned by a thread, and COM makes sure that when you use
the object, it marshals the calls and data to the thread that owns the
object. If that thread isn't there any more, I don't know what happens
(but I'll bet a COM expert would :) ). It doesn't seem like it'd be a
good thing though.

I think that for STA COM objects, you need to have a thread dedicated to
the use of those objects. That thread would be used to create the
objects, and would stay around for the duration, so that the objects can
use that thread for executing their methods. And of course, it's
wasteful to have a new thread for each object, so if you can share a
single thread among all the objects, even better.

(By the way, I just did a bit of quick Googling to try to verify what
I've written above, and I think I have. But I also found a note that
pointed out that an STA thread has to have a message pump of some sort
in order for the marshaling to occur, which makes sense. That means not
only do you probably need a dedicated thread, but you also need to
ensure that thread can pump COM messages. Again, not being a COM
expert, I'm not 100% sure what condition satisfies that requirement, but
I think COM can pump messages for any "waitable state", not just if you
have a window message pump loop, a la
System.Windows.Forms.Application.Run() or WPF's equivalent).
(I know you say you are not a
COM expert ... but you do seem to know a fair bit!)

Just enough to be dangerous. You should take anything I write about COM
with a grain of salt; confirm any assumptions I seem to me making by
looking at other references, such as the MSDN documentation, or articles
written by people who _are_ COM experts.
Again, I am sorry for my ignorance (see PS1 below), do you mean have a
static thread object (pool) which instantiates the factory at startup
and requires a stop-type method?

I'm not sure what you mean by "stop-type method". If you mean, "a way
to signal to the thread to stop executing", then that's probably
optional. You can create the thread as a background thread, and it will
run until there are no more foreground threads in the process, at which
point it will be aborted.

If you can ensure in some other way in your application that the thread
won't be doing something important when it gets aborted (for example,
make sure that wherever you're creating and using those COM objects,
_that_ code is definitely done), then letting it get aborted
automatically as a background thread should be fine.

Otherwise, yes...you'll probably want to synchronize termination of your
program, or the usage of the COM stuff anyway, and that would include
having a well-ordered shutdown of the dedicated COM thread.

I think I mentioned before, but I'll reiterate: if this is a Forms or
WPF application, then you already have a suitable STA thread where you
can create and use COM objects. Just make sure you create the object
using that thread (e.g. with System.Windows.Forms.Control.Invoke()), and
then the .NET COM interop will deal with the rest.
[...]
PS1 - can you recommend any books references for me to get up to speed
on this?

PS2 - as a matter of interest, do you know if this is easily
implementable (or better implemented) using the Px/Rx frameworks? I'm
wondering whether the disposing of the COM objects may be better
achieved with this.

Unfortunately, I don't have a good answer to either of those questions.
I don't even know what "the Px/Rx frameworks" are.

Pete
 
P

Peter Duniho

Peter said:
[...]
Rather, it's the fact that once you have this COM instance, presumably
you intend to actually call the COM instance at some point. But for an
STA COM object, you need to make sure you always use that object on the
same thread, and I'm pretty sure that thread needs to be the one you
used to create the object.

Sorry, I should clarify: by "use that object on the same thread", I mean
that I believe the COM rule is that the methods of the COM object are
always executed on the thread that owns the object. But you can
actually call the methods from a different thread; COM will marshal the
call to the owning thread on your behalf, if you fail to do so (at a
performance cost, of course).

The point is that the owning thread needs to be there, so that COM or
your own code can marshal the calls to that thread. If you let the
thread exit, it's not around to handle that work.

Pete
 
S

Simon Woods

On 29/11/2009 20:11, Peter Duniho wrote:

OK. Thx again. There is some light emerging within - although my
questions may reveal otherwise :)
Otherwise, yes...you'll probably want to synchronize termination of your
program, or the usage of the COM stuff anyway, and that would include
having a well-ordered shutdown of the dedicated COM thread.

Since I have several COM components from which I need to retrieve data,
would it be better then, rather than using static methods, to have an
instance class (classes or perhaps threadpool) which is instantiated as
part of the instantiation of the web service and as part of the
construction create threads instantiate factories as required and which
remain instantiate while the web service is up an running, and closes
down 'in an orderly fashion' when the web service terminates. Would that
be good enough or does that make the situation more complex?

I think I mentioned before, but I'll reiterate: if this is a Forms or
WPF application, then you already have a suitable STA thread where you
can create and use COM objects. Just make sure you create the object
using that thread (e.g. with System.Windows.Forms.Control.Invoke()), and
then the .NET COM interop will deal with the rest.

FYI the COM objects are business objects.
Unfortunately, I don't have a good answer to either of those questions.
I don't even know what "the Px/Rx frameworks" are.

FYI, Parallel Extensions (Px) and Reactive Framework (Rx), I think
effectively permitting declarative parallel/asynch programming via
linq/plinq.
 
P

Peter Duniho

Simon said:
On 29/11/2009 20:11, Peter Duniho wrote:

OK. Thx again. There is some light emerging within - although my
questions may reveal otherwise :)


Since I have several COM components from which I need to retrieve data,
would it be better then, rather than using static methods, to have an
instance class (classes or perhaps threadpool) which is instantiated as
part of the instantiation of the web service and as part of the
construction create threads instantiate factories as required and which
remain instantiate while the web service is up an running, and closes
down 'in an orderly fashion' when the web service terminates. Would that
be good enough or does that make the situation more complex?

The decision of static class versus instantiable should relate to the
need to maintain persist, per-instance data. Static classes can have
static data members, and if there's no need to separate the data on a
per-instance basis, there's no need to use anything but a static class.

Without any additional information, everything you've posted so far
suggests that a single COM-dedicated STA thread should be sufficient.
That thread would be used for all the factory invocations needed to
create your STA COM objects, and with message pumping from COM would
also implicitly be used to invoke any code in any of those COM objects.
A single static class can easily maintain that single thread.

As I mentioned before, you'll have to look into the question of the
message pumping yourself, as I'm not completely sure about how that may
work. However, if my guess is correct that any waitable state the
thread enters is sufficient to allow COM to do its message pumping, then
all you need to do is have some kind of consumer queue to deal with the
factory invocations, and the mere fact that queue will necessarily
involve waiting will allow the COM stuff to work too.

(You should be able to test this in a straightforward way, as if the
message pumping isn't working for COM, your STA object will simply not
work).

As far as that consumer queue goes, you can make it a special purpose
queue that only deals with specific COM interfaces, or you could use a
general-purpose cross-thread invocation queue such as that discussed in
the message thread I referenced in my first reply to you on this topic.
FYI the COM objects are business objects.

My text you quoted doesn't seem to be addressed by your reply. You can
have business objects with or without a Forms or WPF application.
FYI, Parallel Extensions (Px) and Reactive Framework (Rx), I think
effectively permitting declarative parallel/asynch programming via
linq/plinq.

Okay, well Parallel Extensions I know about, though I've never seen
anyone refer to them as "Px". I don't think that the Parallel
Extensions are necessarily going to be helpful here. I'm also not an
expert in that area of .NET (okay, frankly...I'm not much of an expert
in any area of .NET...I just happen to have a lot of exposure to a lot
of .NET :) ), but my impression of it is that it is intended as a
task-abstraction API that automatically distributes work across threads
in a transparent way.

In other words, while here you need specific control over a persistent
thread, Parallel Extensions is all about hiding the thread from you. I
would expect the two to be mutually exclusive in nature (though no doubt
you could shoehorn this problem into a Parallel Extensions solution :) ).

If the "Rx" is the same kind of thing, I'd expect the same logic to apply.

Pete
 
S

Simon Woods

As I mentioned before, you'll have to look into the question of the
message pumping yourself, as I'm not completely sure about how that may
work. However, if my guess is correct that any waitable state the thread
enters is sufficient to allow COM to do its message pumping, then all
you need to do is have some kind of consumer queue to deal with the
factory invocations, and the mere fact that queue will necessarily
involve waiting will allow the COM stuff to work too.

(You should be able to test this in a straightforward way, as if the
message pumping isn't working for COM, your STA object will simply not
work).

That is interesting. Having stepped your code in a bit more detail, I
find that if I set a breakpoint on the COMObject within the delegate,
after creation, the properties seem okay. However, once I have moved out
of the delegate and set a break point prior to returning the COMObject
to the calling app, intellisense reports


'((MyCOMDllTypes.IMyCOMInterface) (_MyCOMObject)).Token' threw an
exception of type 'System.AccessViolationException'

+ base {"Attempted to read or write protected memory. This is often an
indication that other memory is corrupt."} System.SystemException
{System.AccessViolationException}


As far as that consumer queue goes, you can make it a special purpose
queue that only deals with specific COM interfaces, or you could use a
general-purpose cross-thread invocation queue such as that discussed in
the message thread I referenced in my first reply to you on this topic.

Noted. Sorry - I'm a bit slow on all this - steep learning curve (for me).
My text you quoted doesn't seem to be addressed by your reply. You can
have business objects with or without a Forms or WPF application.

OK. Sorry. It's the back end of a web service with hybrid dotnet/COM
objects - so no WPF/Winforms.

As I mentioned before, I've got sets of tests for each layer which all
work fine.

(I would have thought that this kind of scenario was pretty common so I
wondering whether I'm approaching it in the right way!)

Many thx again though.

Simon
 
P

Peter Duniho

Simon said:
[...]
That is interesting. Having stepped your code in a bit more detail, I
find that if I set a breakpoint on the COMObject within the delegate,
after creation, the properties seem okay. However, once I have moved out
of the delegate and set a break point prior to returning the COMObject
to the calling app, intellisense reports

No concise-but-complete code example means there's no way for me or
anyone else to reliably reproduce the problem you're seeing.

Absent that, my first thought is: does the exception occur after the STA
thread that created the object has exited? If so, it doesn't surprise
me at all that an exception would occur.

After all, behind the scenes, COM is trying to marshal an invocation of
your COM object's member to the thread that created the COM object. But
if that thread no longer exists, it will fail. Somehow. How
specifically I don't know, but an AccessViolationException seems like as
reasonable an outcome as any other. :)

There's nothing in the examples or discussion you've provided so far
that even mentions a "Token" property, so it's difficult to understand
the context in which the exception has happened. If you can provide a
concise-but-complete code example, then at least it would be more clear
what's going on.

(Note that a complete code example will have to include the managed
code, an unmanaged COM implementation, and if necessary, instructions
for how to properly register the COM object, if not just using the
regsvr32 utility. The COM object need not be exactly the COM objects
you're using...just some placeholder that can be used to demonstrate the
interaction between it and your managed code.)
[...]
(I would have thought that this kind of scenario was pretty common so I
wondering whether I'm approaching it in the right way!)

Define "common". Note that this newsgroup is not necessarily the best
place to find advice for the specific question. I am (and others are)
happy to help as best we can, but there might actually be a newsgroup
specific to COM interop questions. The fact that few people here have
specific experience with that issue, that doesn't mean that the world at
large doesn't. We're not exactly a representative sample of the
programming community as a whole. :)

Also note that COM is, at this point, a bit of a legacy technology.
Lots of people still use it, of course. But .NET provides interop
support for transitional purposes, not because it's expected that COM is
the preferred technology for modularizing code. So, there are probably
lots of people out there using COM with C++ or VB programs, and lots of
people out there using pure .NET (no COM), but only some much smaller
group of people who are finding themselves in that intersection between
having to use previously-coded COM objects with new managed .NET code.

On top of all that, I would guess that COM outside the context of an
application with a GUI is actually a much smaller subset of that
already-small subset. Perhaps just a mere fraction of the total number
of people doing COM interop. And in the context of a GUI application,
simply creating the COM objects on the main GUI thread is generally
sufficient.

So, no...I wouldn't say that what you're doing is necessarily "pretty
common" (to me, "pretty common" is along the lines of a Forms or WPF
application, or a program needing database access, or having to do file
i/o, that sort of thing). Whether you're approaching it in exactly the
right way, I can't say for sure. I think that once you understand and
get past the COM rules for dealing with STA objects on STA threads, and
put together a basic framework for managing that, you'll be fine. But I
can't guarantee that. :)

Pete
 
S

Simon Woods

OK

Here's a breakdown - and it's 'pseudo' VB6 simply because lines such as
Public Interface/Public Class are implicit within VB6

===> On the COM side - all VB6. <===

1) A type library (registered using regtlib.exe)

MyCOMTypes.tlb

Public Interface IMyCOMType
Public Property Get Token() as String
End Interface

2) A standard COM dll (registered using regsvr32.exe) referencing the
typelib in 1) containing a factory used to create objects implementing
the above interface

MyCOMObjects.dll

Friend Class MyCOMObject
Implements IMyCOMType

Private _token as String

Friend Function Init(Byval p_token as String) as IMyCOMType
_token = p_token
Set Init = Me
End Function

Private Property Get IMyCOMType_Token() as String
IMyCOMType_Token = _token
End Property
End Class

Public Class Factory
Public Function CreateMyCOMObject(Byval p_token as String) as
IMyComType
Dim l_MyCOMObject as MyCOMObject

Set l_MyCOMObject = New MyCOMObject
Set MyCOMType = l_MyCOMObject.Init(p_token)
End
End Class


===> On the DotNet side <===

3) DotNetBusLayer (VBNet proj)

References both of the above COM components

3a) Public Class DataContext

Private _myCOMType As MyCOMTypes.IMyCOMType

Public Sub New(p_myCOMType As MyCOMTypes.IMyCOMType)
m_myCOMType = p_myCOMType
End Sub

Public Function GetData(Byval p_key as String) as XElement
Dim retriever = New DataRetriever(m_myCOMType, p_key)
Return retriever.GetXML
End Function

End Class

3b) Public Class DataRetriever

Private _myCOMType As MyCOMTypes.IMyCOMType
Private _key as String

Public Sub New(Byval p_myCOMType As MyCOMTypes.IMyCOMType, Byval p_key
as String)
_myCOMType = p_myCOMType
_key = p_key
End Sub

Public Function GetXML() as XElement
//create XElement stub here
End Function

End Class


4) Web Service (using WCF REST starter kit -
http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24644)

References both of the above COM components and the DotNetBusLayer also

4a) Public Interface decorated as a RESTful service

[ServiceContract]
public interface IGateway
{

[WebGet(UriTemplate = "data/{p_key}")]
[OperationContract]
XElement GetData(string p_key);

}

4b) Implementation of above interface

public class Gateway : IGateway
{

private MYCOMTypes.IMyCOMType _myCOMType;
private DotNetBusLayer.DataContext _dataContext;

public Gateway()
{
_MyCOMType = COMExecutor.CreateMyCOMType("Token goes here")
_dataContext = new DotNetBusLayer.DataContext(_MyCOMType);
}


private XElement GetDataXML(string p_key)
{

try
{
return _dataContext.GetData(p_key);
}
catch
{
throw new
WebProtocolException(HttpStatusCode.BadRequest, "Error text goes here",
null);
}
}
}

4c) COMExecutor is the class you've put together. As it stands it looks like

Public class COMExecutor
{


public static MyCOMType.IMyCOMType CreateMyCOMObject(string p_token)
{
MyCOMType.IMyCOMType myCOMObject = null;
Exception threadException = null;
EventWaitHandle eventWaitHandle = new AutoResetEvent(false);

var thread = new Thread(delegate
{
try
{
var factory = new MyCOMObjects.Factory();
myCOMObject =
factory.CreateMyCOMObject(p_token);
}
catch (Exception exc)
{
threadException = exc;
}
eventWaitHandle.Set();
}
);

thread.SetApartmentState(ApartmentState.STA);
thread.Start();

eventWaitHandle.WaitOne();
if (threadException != null)
{
throw (threadException);
}

return MyCOMObject;
}
}

At the moment, I'm just trying to get the Gateway instantiated correctly
and the call in the constructor is where I'm up to. I haven't looked at
the data retrieval aspect, but, as I mentioned before, I've tests which
prove this functionality so I'm quite hopeful, at each tier and also at
the interactions between each tier. It was only when I included the
GateWay and tested across there that I encountered this threading issue.

Obviously, atm I'm invoking the gateway via my browser.

I hope this is good enough for you. If there's anything still missing,
pls give me a shout.

Many thx

Simon
 
S

Simon Woods

Simon said:
[...]
That is interesting. Having stepped your code in a bit more detail, I
find that if I set a breakpoint on the COMObject within the delegate,
after creation, the properties seem okay. However, once I have moved
out of the delegate and set a break point prior to returning the
COMObject to the calling app, intellisense reports
my first thought is: does the exception occur after the STA
thread that created the object has exited? If so, it doesn't surprise me
at all that an exception would occur.

After all, behind the scenes, COM is trying to marshal an invocation of
your COM object's member to the thread that created the COM object. But
if that thread no longer exists, it will fail. Somehow. How specifically
I don't know, but an AccessViolationException seems like as reasonable
an outcome as any other. :)

FYI ... when I step the code, and check the thread state when the
AccessViolationException occurs, intellisense tells me that the thread
isAlive and the ThreadState is running.



I think that once you understand and
get past the COM rules for dealing with STA objects on STA threads, and
put together a basic framework for managing that, you'll be fine. But I
can't guarantee that. :)

That sounds hopeful!
 
P

Peter Duniho

Simon said:
[...]
At the moment, I'm just trying to get the Gateway instantiated correctly
and the call in the constructor is where I'm up to. I haven't looked at
the data retrieval aspect, but, as I mentioned before, I've tests which
prove this functionality so I'm quite hopeful, at each tier and also at
the interactions between each tier. It was only when I included the
GateWay and tested across there that I encountered this threading issue.

Obviously, atm I'm invoking the gateway via my browser.

I hope this is good enough for you. If there's anything still missing,
pls give me a shout.

Unfortunately, there's nothing in the code example that suggests to me
why you are getting the exception.

I do have the impression from your other reply that the exception occurs
before your code ever tries to even access the newly created COM object
(which would indicate it's not related to the thread issue I've
personally been focusing on). But you haven't even explained what
thread the exception is occurring in, nor the stack trace, nor exactly
what statement in the code you posted is executing when the exception
occurs (assuming it's always the same statement, that is).

Also, your code example seems to have a lot of stuff in it that isn't
necessarily needed in order to reproduce the problem. Those of us
answering questions here are usually willing to put in _some_ time to
try to figure things out, but when you link to a whole web service
project and expect us to set up a server/client scenario just to test
what is probably just an in-process issue, that generally will exceed
the amount of time we have available to investigate your question, never
mind the time we're actually willing to give up.

When I write "concise-but-complete", I mean two things:

-- "concise" means that there's nothing in the example that isn't
absolutely required in order to reproduce the problem. The code example
should distill down to the absolute minimum the code involved in
reproducing the problem.

-- "complete" means that nothing else needs to be added to the
example in order to get it to work, at least with respect to code.
Obviously, you can't run regsvr32 on my computer, nor add references to
DLLs, that sort of thing (though, actually...DLL registration could be
automated as part of the code example). But no one should have to add
any more code just to see the code you posted in operation.

Your own example appears to include a lot of layered abstractions that
are probably not required in order to reproduce the problem, not to
mention tht whole web service part. At the same time, there's no
obvious entry point, nor instructions for how to invoke the code of
interest.

I and others are happy to try to work to reproduce and help solve
whatever issues you're having with your code. But there are limits to
the amount of time we're going to put into it (and of course, each
person has their own personal threshold). You'll be much more likely to
get answers you want if you simplify your question, thus making the most
efficient use of the time available by all parties involved.

If you are not yet convinced of the need for a simplified,
"concise-but-complete" code example, for a more elaborate and in-depth
discussion of the issues above, you should read these references:
http://www.yoda.arachsys.com/csharp/complete.html
http://www.yoda.arachsys.com/csharp/incomplete.html
http://sscce.org/

If nothing else, you'll see that I'm not alone in my desire to work with
simplified code examples. :)

Pete
 
D

Doug Forster

Hello Simon,

I've been following this thread for a bit so I'll butt in.
I haven't tried to do what you are doing but I can tell you one or two
things from out in the unmanaged world.
Firstly a COM object created in an STA must either be always used
entirely from within the STA or its interface calls must be marshalled.
I don't know if you can marshall COM interfaces from managed code so I
would suggest you assume you can't. You cannot simply create a COM
object inside an STA and chuck it out into the MTA world as you seem to
be attempting.
Secondly an STA *must* have a message loop. COM serializes STA COM
object calls via a hidden window. The message loop needs to have a
lifetime >= the COM object lifetime.
Thirdly you need to devise a mechanism to call your COM object from
within your STA message loop. If this was a windows app I would suggest
a hidden form that you could do Synchronize calls into, but this is a
web app so I don't know if you can do that.

Cheers
Doug Forster
 
P

Peter Duniho

Doug said:
Hello Simon,

I've been following this thread for a bit so I'll butt in.
I haven't tried to do what you are doing but I can tell you one or two
things from out in the unmanaged world.
Firstly a COM object created in an STA must either be always used
entirely from within the STA or its interface calls must be marshalled.
I don't know if you can marshall COM interfaces from managed code so I
would suggest you assume you can't. You cannot simply create a COM
object inside an STA and chuck it out into the MTA world as you seem to
be attempting.

My understanding (which may be incorrect) is that in the unmanaged
environment, calls to an STA object from outside that object's STA
thread (i.e. the one where it was created) are _automatically_ marshaled
by COM. That is, assuming that STA thread is set up correctly, and has
the message pump required, there's nothing specific the client code
needs to do in order to marshal the call.

Is that understanding incorrect? If not, then I would expect the same
mechanism to work even from managed code, since the managed code doesn't
AFAIK replace the COM mechanism, but rather just encapsulates it.
Secondly an STA *must* have a message loop. COM serializes STA COM
object calls via a hidden window. The message loop needs to have a
lifetime >= the COM object lifetime.

I see from this article:
http://msdn.microsoft.com/en-us/library/ms809971.aspx

....that "the marshaling process for STAs translates the call into a
WM_USER message and posts the result to a hidden top-level window
associated with the apartment when it was created".

I take that to mean that the thread is required to have an actual window
message pump (e.g. GetMessage(), WaitMessage(), etc. in a loop that
eventually calls DispatchMessage()) in order for this to work.
Specifically, other mechanisms such as APC don't suffice (e.g. a thread
in an alertable wait state, but not specifically dispatching window
messages). Is that correct?
Thirdly you need to devise a mechanism to call your COM object from
within your STA message loop. If this was a windows app I would suggest
a hidden form that you could do Synchronize calls into, but this is a
web app so I don't know if you can do that.

My understanding (and this is supported by the quoted documentation
above) is that COM itself creates the necessary hidden window. There
should be no need to create one specifically for _that_ purpose. The
remaining requirement then would be the message pump itself. So, I
suppose one solution might be to have the STA thread that owns the
object call the System.Windows.Forms.Application.Run() method.

Of course, the other part of the problem is getting that STA thread to
service requests to create new STA COM objects, so that they are owned
by the right thread. For that, the easiest thing _would_ be yet another
hidden window, so that the _client_ code can then do the same kind of
cross-thread invocation COM is already doing on its behalf for calls to
the COM object.

Unfortunately, I don't think Forms has support for message-only windows.
Of course one can always just create a normal Form instance and keep
it hidden (using CreateHandle() or CreateControl() to ensure the
instance has a proper window handle needed for cross-thread invocation).
That should work fine for the purpose.

And of course if the thread is handling only STA COM objects that are
created before it enters its message pump loop, then no additional
window needs to be created, because COM's already made one for itself.

Pete
 
D

Doug Forster

My understanding (which may be incorrect) is that in the unmanaged
environment, calls to an STA object from outside that object's STA
thread (i.e. the one where it was created) are _automatically_ marshaled
by COM. That is, assuming that STA thread is set up correctly, and has
the message pump required, there's nothing specific the client code
needs to do in order to marshal the call.
Is that understanding incorrect? If not, then I would expect the same
mechanism to work even from managed code, since the managed code doesn't
AFAIK replace the COM mechanism, but rather just encapsulates it.
It's not calls that are marshalled - it's interfaces. Only interfaces
that are transported by COM itself can be automatically marshalled. And
then there must be a marshaller available for the interface concerned.
This would require the interface to either be a known interface
(IDispatch for example) or for someone to have written a marshaller
specially. I can't find the error message that Simon mentions except
that questions about it all seem to relate to marshalling. Presumably if
his object could be marshalled across apartments it wouldn't have had
that error to start with.
If you look up the CoMarshalInterface type methods you can see how one
might shift an interface across an apartment boundary 'manually' out in
the unmanaged world, but I don't even know if this is possible in .NET
and I haven't done it myself out in unmanaged world either.
I take that to mean that the thread is required to have an actual window
message pump (e.g. GetMessage(), WaitMessage(), etc. in a loop that
eventually calls DispatchMessage()) in order for this to work.
Specifically, other mechanisms such as APC don't suffice (e.g. a thread
in an alertable wait state, but not specifically dispatching window
messages). Is that correct?

I believe so.
My understanding (and this is supported by the quoted documentation
above) is that COM itself creates the necessary hidden window. There
should be no need to create one specifically for _that_ purpose.

Yes I was only suggesting one possible way of getting inside the thread
directly assuming that was the only way of invoking the objects methods.
The
remaining requirement then would be the message pump itself. So, I
suppose one solution might be to have the STA thread that owns the
object call the System.Windows.Forms.Application.Run() method.

Of course, the other part of the problem is getting that STA thread to
service requests to create new STA COM objects, so that they are owned
by the right thread. For that, the easiest thing _would_ be yet another
hidden window, so that the _client_ code can then do the same kind of
cross-thread invocation COM is already doing on its behalf for calls to
the COM object.

Unfortunately, I don't think Forms has support for message-only windows.
Of course one can always just create a normal Form instance and keep it
hidden (using CreateHandle() or CreateControl() to ensure the instance
has a proper window handle needed for cross-thread invocation). That
should work fine for the purpose.

And of course if the thread is handling only STA COM objects that are
created before it enters its message pump loop, then no additional
window needs to be created, because COM's already made one for itself.

I suppose he could PInvoke the api calls and directly pick messages out
of the message queue. That way he wouldn't need a window (apart from the
one made by COM). Unfortunately there is only a PostThreadMessage (not a
Send) so he would also have to somehow synchronize the calls. All very
messy.

Cheers
Doug Forster
 
P

Peter Duniho

Doug said:
It's not calls that are marshalled - it's interfaces. Only interfaces
that are transported by COM itself can be automatically marshalled. And
then there must be a marshaller available for the interface concerned.

Hmmm. Well, I've been trying to read up on COM to refresh my memory and
fill in the gaps I never really understood that well in the first place
(it's been over a decade since I had to do any COM programming). This
article:
http://msdn.microsoft.com/en-us/library/ms680112(VS.85).aspx

....seems to be saying that you have to marshal the interfaces, but
implies that you can marshal _any_ interface, and COM will provide the
necessary proxy. In particular:

...all pointers to objects must be marshaled when passed
between apartments. COM provides the following two functions
for this purpose:

* CoMarshalInterThreadInterfaceInStream marshals an interface
into a stream object that is returned to the caller.

* CoGetInterfaceAndReleaseStream unmarshals an interface
pointer from a stream object and releases it.

These functions wrap calls to CoMarshalInterface and
CoUnmarshalInterface functions, which require the use of
the MSHCTX_INPROC flag.

In other words, not only does the original apartment thread where the
object was created need to continue to run (as I had suggested to Simon
earlier), the interface pointer used in threads other than the original
apartment thread has to be one obtained by the above marshaling
operation in the thread where the pointer will be used.
This would require the interface to either be a known interface
(IDispatch for example) or for someone to have written a marshaller
specially.

The document I referenced above doesn't mention having to write any
specific code to support marshaling. The doc page for
CoMarshalInterface says that the default COM marshaling implementation
will be used if the object doesn't implement IMarshal.

So it seems to me that there shouldn't be a need for the object itself
to support the marshaling. Or is that document just glossing over that
detail?
I can't find the error message that Simon mentions except
that questions about it all seem to relate to marshalling. Presumably if
his object could be marshalled across apartments it wouldn't have had
that error to start with.

That's the assumption I've made, and it's what I suggested earlier.
But, without a code example from Simon that clearly shows everything
that's going on and provides a reproducible case to debug, it's hard to
know for sure.

I will say that I've read enough about COM at this point to be confident
that the thread where the object is created MUST continue to run until
that object is no longer around. You stated this yourself, and the MSDN
documentation is clear on the point. So I feel confident that my
previous conjecture on that point was in fact accurate. :)
If you look up the CoMarshalInterface type methods you can see how one
might shift an interface across an apartment boundary 'manually' out in
the unmanaged world, but I don't even know if this is possible in .NET
and I haven't done it myself out in unmanaged world either.

The object reference in managed code is actually a _wrapper_ for the COM
object itself. So that wrapper could in fact transparently handle any
marshaling required (i.e. check the current thread, if it's not the COM
object's owning thread do the necessary marshaling). It could even
cache the "unmarshaled" pointer per-thread to improve performance (but
the proxying of the interface calls would of course still have its own
overhead).

Whether that's actually what .NET does, I don't know. But it could. If
so, that would leave only the need to preserve that thread and make sure
a message pump is executing on it (calling Application.Run() being the
simplest approach, but that would then require a separate hidden window
for any other work the thread is being asked to do, such as create
additional COM objects so that one doesn't need a whole thread dedicated
to each COM object).
[...]
Unfortunately, I don't think Forms has support for message-only
windows. Of course one can always just create a normal Form instance
and keep it hidden (using CreateHandle() or CreateControl() to ensure
the instance has a proper window handle needed for cross-thread
invocation). That should work fine for the purpose.

And of course if the thread is handling only STA COM objects that are
created before it enters its message pump loop, then no additional
window needs to be created, because COM's already made one for itself.

I suppose he could PInvoke the api calls and directly pick messages out
of the message queue. That way he wouldn't need a window (apart from the
one made by COM). Unfortunately there is only a PostThreadMessage (not a
Send) so he would also have to somehow synchronize the calls. All very
messy.

If it were me, I think I'd go ahead and implement a delegate invocation
queue to handle the non-COM stuff, and then use something like
MsgWaitForMultipleObjects() (via p/invoke of course) to implement my own
message pump loop rather than calling Application.Run(). That way I
could use an EventHandle for my own delegate invocation queue, but still
dispatch the window messages for COM.

But yes, I think there a variety of ways it could be done without using
a Form instance as the hidden window. :)

Frankly, part of the problem for me here is that it's not really clear
to me how the OP is winding up using COM or why. The VB6 code he posted
appears to be some kind of VB6-enabled way to make a COM object without
having to explicitly write the usual DLL entry points and factory
methods (I've only ever done COM in C/C++ and I never found a VS project
or other dev tool to simplify the declaration of interfaces and
implementation of DLL functions to support COM...it was always quite
tedious).

But, if that's the case, it begs the question as to why use COM at all?
Seems like the OP could just compile the VB6 code as a normal DLL and
import it using DllImport (p/invoke), or even better just update the
code so that it's a VB.NET managed DLL that can be referenced directly
by the C# code. Maybe there's a good answer to that...I just can't see
it at the moment.

Pete
 
P

Peter Duniho

Been doing some playing around. I feel like I'm on firmer ground now.
Some comments related to my previous post:

Peter said:
[...]
I will say that I've read enough about COM at this point to be confident
that the thread where the object is created MUST continue to run until
that object is no longer around. You stated this yourself, and the MSDN
documentation is clear on the point. So I feel confident that my
previous conjecture on that point was in fact accurate. :)

I have definitely confirmed this requirement. I wrote up a simple test
solution, with a C++/ATL COM server and a C# client. If I let the
thread that owns the COM object expire before I try to use the COM
object, I get an InvalidComObjectException.
[...]
If you look up the CoMarshalInterface type methods you can see how one
might shift an interface across an apartment boundary 'manually' out
in the unmanaged world, but I don't even know if this is possible in
.NET and I haven't done it myself out in unmanaged world either.

The object reference in managed code is actually a _wrapper_ for the COM
object itself. So that wrapper could in fact transparently handle any
marshaling required (i.e. check the current thread, if it's not the COM
object's owning thread do the necessary marshaling). It could even
cache the "unmarshaled" pointer per-thread to improve performance (but
the proxying of the interface calls would of course still have its own
overhead).

Whether that's actually what .NET does, I don't know. But it could.

Okay...I have confirmed that .NET does in fact handle all of this for
the client. In my sample, I created the managed object representing the
COM object on the STA thread as needed, but then used that reference
from the main, non-STA thread. No problems at all, _provided_ my STA
thread was still running (see above :) ).
[...]
If it were me, I think I'd go ahead and implement a delegate invocation
queue to handle the non-COM stuff, and then use something like
MsgWaitForMultipleObjects() (via p/invoke of course) to implement my own
message pump loop rather than calling Application.Run(). That way I
could use an EventHandle for my own delegate invocation queue, but still
dispatch the window messages for COM.

Apparently, I don't actually know myself. Turns out, if it were me, I'd
create a message-only window for my own use (not a Form, but rather a
NativeWindow set up to be a message-only window), just like COM is
doing, and then just call Application.Run() to let .NET handle the
dispatching for me.

See code sample attached below. (I've omitted the COM server itself,
because it's trivial to reproduce using an ATL project template -- just
add a new COM interface and implementation, with the "Number" property
-- and because it's quite a lot longer than the part that's actually
interesting. I assume that Simon and anyone else can easily adapt the
code example to whatever COM object they are actually using, since it's
just a simple variable type change).

I still have my uncertainties about Simon's particular issues. For one,
the error he's describing isn't the same that I see when I break the
code in the way I think his code is broken. For another, there are
still these questions I mentioned before about why he's even using COM,
whether that's the best way to accomplish what he wants.

But, assuming COM is really the best way to solve his particular issue,
this code example may help. In particular, the MessageWindowInvoker
class is a self-contained, general-purpose class that creates a STA
thread, a message-only window owned by that thread, and then starts a
message pump for the window. In addition, it provides Post() and Send()
methods to allow delegates to be invoked on the STA thread.

With this class, it's trivial to force operations onto the STA thread,
such as creating COM objects. The STA thread continues to run until the
Stop() method is called, allowing the same thread to be reused for any
number of COM objects.

One could even use the thread for access to a given COM object that's
already been created. I haven't tested the performance of doing that,
but it could be a significant improvement, assuming .NET and/or COM
(whichever is actually doing the marshaling work) is smart enough to not
bother with the marshaling when the COM object function call is being
executed on the correct thread already.

In my code, I use variable capturing exclusively to pass data to the
invoked delegates (which in this implementation must be of type Action).
I leave as an exercise for the reader the task of elaborating on the
Post() and Send() methods to support parameter passing a la
Action<object> or the "params object[] args" approach of
Control.Invoke() or Delegate.Invoke(). Personally, I wouldn't bother
(and didn't), but if someone else wants to I won't think less of them. :)

Note: the "TestSTACOMInteropLib" namespace is from my simple COM server.
Obviously, you'd omit that and include instead whatever COM server
you're actually using. Likewise the STACOMObject and ISTACOMObject types.

Pete


using System;
using System.Collections.Generic;
using TestSTACOMInteropLib;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace TestSTACOMInteropCsharpClient
{
class Program
{
static void Main(string[] args)
{
MessageWindowInvoker mw = MessageWindowInvoker.Create();
ISTACOMObject isco = null;
int i = 0;
object objWait = new object();

mw.Send(delegate
{
isco = new STACOMObject();
});

// Stopping the invoker thread before trying to access
// the COM object's property results in an exception:
// System.Runtime.InteropServices.InvalidComObjectException
// "COM object that has been separated from its underlying
// RCW cannot be used."

//mw.Stop();

// However, as long as the invoker thread is still running,
// no additional marshaling is required on the part of the
// client code. .NET interop and/or COM handle the marshaling
// automatically.
i = isco.Number;

mw.Stop();

Console.WriteLine(i);
Console.ReadLine();
}
}

class MessageWindowInvoker
{
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
private static extern bool PostMessage(IntPtr hWnd, uint Msg,
IntPtr wParam, IntPtr lParam);
private static IntPtr HWND_MESSAGE = new IntPtr(-3);
private const UInt32 WM_APP = 0x8000;

private class MessageWindow : NativeWindow
{
MessageWindowInvoker _invoker;

public MessageWindow(MessageWindowInvoker invoker)
{
_invoker = invoker;

CreateParams cp = new CreateParams();

cp.Parent = HWND_MESSAGE;
CreateHandle(cp);
}

protected override void WndProc(ref Message m)
{
if (m.Msg == WM_APP)
{
_invoker.Consume();

return;
}

base.WndProc(ref m);
}
}

private Thread _thread;
private MessageWindow _mw;

// Yes, this could have been all in the constructor
// rather than this factory method. I just prefer
// to avoid code of this complexity and non-deterministic
// execution time (i.e. even less deterministic than
// is usual) in a constructor.
public static MessageWindowInvoker Create()
{
MessageWindowInvoker mwRet = null;
object objWait = new object();

Thread thread = null;

thread = new Thread(delegate()
{
mwRet = new MessageWindowInvoker(thread);

lock (objWait)
{
Monitor.Pulse(objWait);
}

Application.Run();
});

thread.SetApartmentState(ApartmentState.STA);

lock (objWait)
{
thread.Start();

Monitor.Wait(objWait);
}

return mwRet;
}

private MessageWindowInvoker(Thread thread)
{
_thread = thread;
_mw = new MessageWindow(this);
}

private Queue<Action> _queue = new Queue<Action>();
private object _objLock = new object();
private bool _fSignaled;
private bool _fStopped;

public void Post(Action action)
{
lock (_objLock)
{
if (_fStopped)
{
throw new InvalidOperationException("message pump
has been stopped");
}

_queue.Enqueue(action);
if (!_fSignaled)
{
PostMessage(_mw.Handle, WM_APP, IntPtr.Zero,
IntPtr.Zero);
}
}
}

public void Send(Action action)
{
object objWait = new object();

lock (objWait)
{
Post(delegate
{
action();

lock (objWait)
{
Monitor.Pulse(objWait);
}
});

Monitor.Wait(objWait);
}
}

public void Stop()
{
lock (_objLock)
{
if (_fStopped)
{
throw new InvalidOperationException("message pump
has already been stopped!");
}

Post(delegate
{
Application.ExitThread();
});

_fStopped = true;
}

_thread.Join();
}

private void Consume()
{
Action[] rgaction;

lock (_objLock)
{
rgaction = _queue.ToArray();
_queue.Clear();
_fSignaled = false;
}

foreach (Action action in rgaction)
{
action();
}
}
}
}
 
S

Simon Woods

Thx Pete vvm ...

Wrt COM, my COM libs wrap many 000s of lines of COM legacy code so I
think I have to jump into the COM world one way or another.

I have many other comments which I'll make in another response but I'm
just trying your code in a console app but replacing with my own COM libs.

I'm finding that if I replace

ISTACOMObject isco = null;
....
mw.Send(delegate
{
isco = new STACOMObject();
});

with

MyCOMDll.Factory factory = null;
MyCOMDll.IMyCOMType constructedObject = null;

.....

mw.Send(delegate
{
factory = new STACOMObject();
constructedObject = factory.CreateCOMObject()
});

although the factory gets instantiated fine, I get an unhandle COM
exception error when the factory tries to create the constructedObject.
(FYI, I do have unit tests proving that this is okay in a pure COM
environment)

Simon
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top