Thread and COM Interop

P

Paul Bowman

Hi All

Does anybody have any ideas what I am doing wrong with this code.

What I am trying to do is read data from a Domino Database. For
purposes of the test I have wrapped the DominoCom object in a C# class
that runs in a seperate thread.

If I run this code:
rd = new ReadData(src, 1, 10, this);
list.Add(rd);

t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

rd = new ReadData(src, 30, 20, this);
list.Add(rd);
t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

rd = new ReadData(src, 100, 30, this);
list.Add(rd);

t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

Which starts three threads it all works as I would expect

but if I do it in a loop (which is what I want to do) the code stops
at the
point where it calls into the Domino COM Class.

list = new ArrayList(10);

while (!isComplete)
{
if (list.Count < 10)
{
rd = new ReadData(src, offset * 10 + 1, docCount, this);
list.Add(rd);

t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

offset++;
rd = null;
t = null;
}
}

What I dont understand is why this should be any different to starting
each thread as per the previous example...

If anybody has any ideas I would be grateful...

Regards

Paul Bowman
 
N

Nicholas Paldino [.NET/C# MVP]

Paul,

If the COM component is apartment threaded, then you are making the call
to the thread in the wrong way. Apartment-threaded components in COM
require the thread to be set up correctly for proper marshaling between
apartments. .NET code by default doesn't run in an apartment state. You
have to set it explicitly for a new thread.

Because you are passing the Read method in as the thread entry point,
you aren't setting the thread up correctly. What you really need to do, is
before you make any calls to COM on the new thread, is set the
ApartmentState property of the current thread to ApartmentState.STA. This
will ensure that apartment-state COM components are run correctly on that
thread.

However, you might have an additional problem. If the ReadData class
has an instance of an apartment threaded COM object stored in it that you
are referencing with the Read method, then the interface pointer is not
being marshaled correctly across apartments. In this case, no matter what
the apartment is, you require marshaling of that interface pointer. The
reason for this is that the STA in the new thread is different from the STA
in the original thread it was created in (if it was created in an STA
apartment). If the original apartment was MTA, then you still have to
marshal it correctly (in this case, you are marshaling a proxy actually),
but it still has to be done. The common way to do this was through the
Global Interface Table (GIT).

So, if this is the case, then you will have to take the COM reference,
place it on the GIT, and then get it in the thread, after you have
initialized the apartment state.

Hope this helps.
 
P

Paul Bowman

Nicholas Paldino said:
Paul,

If the COM component is apartment threaded, then you are making the call
to the thread in the wrong way. Apartment-threaded components in COM
require the thread to be set up correctly for proper marshaling between
apartments. .NET code by default doesn't run in an apartment state. You
have to set it explicitly for a new thread.

Because you are passing the Read method in as the thread entry point,
you aren't setting the thread up correctly. What you really need to do, is
before you make any calls to COM on the new thread, is set the
ApartmentState property of the current thread to ApartmentState.STA. This
will ensure that apartment-state COM components are run correctly on that
thread.

However, you might have an additional problem. If the ReadData class
has an instance of an apartment threaded COM object stored in it that you
are referencing with the Read method, then the interface pointer is not
being marshaled correctly across apartments. In this case, no matter what
the apartment is, you require marshaling of that interface pointer. The
reason for this is that the STA in the new thread is different from the STA
in the original thread it was created in (if it was created in an STA
apartment). If the original apartment was MTA, then you still have to
marshal it correctly (in this case, you are marshaling a proxy actually),
but it still has to be done. The common way to do this was through the
Global Interface Table (GIT).

So, if this is the case, then you will have to take the COM reference,
place it on the GIT, and then get it in the thread, after you have
initialized the apartment state.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Paul Bowman said:
Hi All

Does anybody have any ideas what I am doing wrong with this code.

What I am trying to do is read data from a Domino Database. For
purposes of the test I have wrapped the DominoCom object in a C# class
that runs in a seperate thread.

If I run this code:
rd = new ReadData(src, 1, 10, this);
list.Add(rd);

t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

rd = new ReadData(src, 30, 20, this);
list.Add(rd);
t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

rd = new ReadData(src, 100, 30, this);
list.Add(rd);

t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

Which starts three threads it all works as I would expect

but if I do it in a loop (which is what I want to do) the code stops
at the
point where it calls into the Domino COM Class.

list = new ArrayList(10);

while (!isComplete)
{
if (list.Count < 10)
{
rd = new ReadData(src, offset * 10 + 1, docCount, this);
list.Add(rd);

t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

offset++;
rd = null;
t = null;
}
}

What I dont understand is why this should be any different to starting
each thread as per the previous example...

If anybody has any ideas I would be grateful...

Regards

Paul Bowman

Hi Nicholas

Thanks for your reply and the info - that was really helpful and
certainly gives me food for thought.

I seem to have corrected the problem by changing the [STAThread] to
[MTAThread] in code. I am not sure if this is the complete answer and
I will research the points you raised.

Once again thanks for your response.

Regards

Paul Bowman
 
W

Willy Denoyette [MVP]

It's hard to tell without knowing the apartment types supported by the COM
object.

Willy.

Paul Bowman said:
Nicholas Paldino said:
Paul,

If the COM component is apartment threaded, then you are making the
call
to the thread in the wrong way. Apartment-threaded components in COM
require the thread to be set up correctly for proper marshaling between
apartments. .NET code by default doesn't run in an apartment state. You
have to set it explicitly for a new thread.

Because you are passing the Read method in as the thread entry point,
you aren't setting the thread up correctly. What you really need to do,
is
before you make any calls to COM on the new thread, is set the
ApartmentState property of the current thread to ApartmentState.STA.
This
will ensure that apartment-state COM components are run correctly on that
thread.

However, you might have an additional problem. If the ReadData class
has an instance of an apartment threaded COM object stored in it that you
are referencing with the Read method, then the interface pointer is not
being marshaled correctly across apartments. In this case, no matter
what
the apartment is, you require marshaling of that interface pointer. The
reason for this is that the STA in the new thread is different from the
STA
in the original thread it was created in (if it was created in an STA
apartment). If the original apartment was MTA, then you still have to
marshal it correctly (in this case, you are marshaling a proxy actually),
but it still has to be done. The common way to do this was through the
Global Interface Table (GIT).

So, if this is the case, then you will have to take the COM
reference,
place it on the GIT, and then get it in the thread, after you have
initialized the apartment state.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Paul Bowman said:
Hi All

Does anybody have any ideas what I am doing wrong with this code.

What I am trying to do is read data from a Domino Database. For
purposes of the test I have wrapped the DominoCom object in a C# class
that runs in a seperate thread.

If I run this code:
rd = new ReadData(src, 1, 10, this);
list.Add(rd);

t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

rd = new ReadData(src, 30, 20, this);
list.Add(rd);
t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

rd = new ReadData(src, 100, 30, this);
list.Add(rd);

t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

Which starts three threads it all works as I would expect

but if I do it in a loop (which is what I want to do) the code stops
at the
point where it calls into the Domino COM Class.

list = new ArrayList(10);

while (!isComplete)
{
if (list.Count < 10)
{
rd = new ReadData(src, offset * 10 + 1, docCount, this);
list.Add(rd);

t = new Thread(new ThreadStart(rd.Read));
t.Name = "Thread " + ++ThreadId;
t.Start();

offset++;
rd = null;
t = null;
}
}

What I dont understand is why this should be any different to starting
each thread as per the previous example...

If anybody has any ideas I would be grateful...

Regards

Paul Bowman

Hi Nicholas

Thanks for your reply and the info - that was really helpful and
certainly gives me food for thought.

I seem to have corrected the problem by changing the [STAThread] to
[MTAThread] in code. I am not sure if this is the complete answer and
I will research the points you raised.

Once again thanks for your response.

Regards

Paul Bowman
 

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

Top