Multithreading concurrency with Interop

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Mark Sisson

    Multithreading concurrency with Interop

    Hey all.
    I've got a legacy COM dll that I'd like to use in my multithreaded C#
    app. Problem though is when I create several instances of the interop
    object and start them on their own threads they always seem to run
    synchronously.

    I've boiled down the code to a simple "lab" experiment below. In this
    console app you'll see that instance one comes back after roughly 4
    seconds and then instance two comes back in 8 seconds, twice the time.
    This indicates that it is wait on the first thread to finish. Why
    won't they run concurrently?

    I've tried my best to make sure that the thread types are MTAThread
    before calling the COM objects. Also, in VB I made sure the project
    threading model was "Apartment Threaded" and the Instancing set to
    GlobalUseMulti.

    Any ideas???
    Thanks



    *************** **** .NET C# CONSOLE TEST APP *************** *****
    using System;
    using System.Threadin g;

    namespace ComThreadTest
    {
    class MyConsoleApp
    {
    [MTAThread]
    static void Main(string[] args)
    {
    TestClass test1 = new TestClass("1");
    TestClass test2 = new TestClass("2");

    Thread th = new Thread(new ThreadStart(tes t1.Go));
    th.ApartmentSta te = ApartmentState. MTA;
    th.Start();

    th = new Thread(new ThreadStart(tes t2.Go));
    th.ApartmentSta te = ApartmentState. MTA;
    th.Start();
    }
    }

    public class TestClass
    {
    public DotNetSleeperCl ass dotnet;
    public MyCom.SleeperCl ass com;
    public string mName;

    public TestClass(strin g name)
    {
    mName = name;
    dotnet = new DotNetSleeperCl ass();
    com = new MyCom.SleeperCl ass();
    }

    [MTAThread]
    public void Go()
    {
    DateTime start = DateTime.Now;
    Console.WriteLi ne(mName + " OUT at " +
    start.ToString( "hh:mm:ss:fff") );

    //THIS WORK FINE BECAUSE IT'S SIMPLE MANAGED CODE.
    //dotnet.Go();

    //THIS BLOCKS OTHER THREADS WHILE IT RUNS com.Go();

    DateTime finish = DateTime.Now;
    TimeSpan ts = finish - start;
    Console.WriteLi ne(mName + " IN at " +
    finish.ToString ("hh:mm:ss:fff" ) + " ELAPSED=" +
    ts.TotalMillise conds.ToString( ) + " milliseconds.") ;
    Console.WriteLi ne("Press any key to continue...");
    Console.Read();
    }
    }

    public class DotNetSleeperCl ass
    {
    public DotNetSleeperCl ass() {}

    public void Go()
    {
    Thread.Sleep(30 00);
    }
    }

    }


    *************** **** VB6 ACTIVEX DLL *************** *****
    Option Explicit
    Public Sub Go()
    'On my box this runs in about 4 seconds to run.
    Dim a, b, c
    For a = 1 To 1000
    For b = 1 To 1000
    c = a Mod b
    DoEvents
    Next b
    Next a
    End Sub
  • Justin Rogers

    #2
    Re: Multithreading concurrency with Interop

    If you are running this on a single-proc or sometimes even a dual-proc
    machine you wouldn't see concurrency on these threads. The processor is
    allowing the first thread to finish before it does a context switch to a new
    thread. The only real way to see multi-threading in action on a single-proc
    machine is to have the thread jump into the wait state while it isn't doing
    anything. Then you'll see your other threads running.


    --
    Justin Rogers
    DigiTec Web Consultants, LLC.

    "Mark Sisson" <mark@corporate digital.com> wrote in message
    news:88cc4eb9.0 309051440.71098 734@posting.goo gle.com...[color=blue]
    > Hey all.
    > I've got a legacy COM dll that I'd like to use in my multithreaded C#
    > app. Problem though is when I create several instances of the interop
    > object and start them on their own threads they always seem to run
    > synchronously.
    >
    > I've boiled down the code to a simple "lab" experiment below. In this
    > console app you'll see that instance one comes back after roughly 4
    > seconds and then instance two comes back in 8 seconds, twice the time.
    > This indicates that it is wait on the first thread to finish. Why
    > won't they run concurrently?
    >
    > I've tried my best to make sure that the thread types are MTAThread
    > before calling the COM objects. Also, in VB I made sure the project
    > threading model was "Apartment Threaded" and the Instancing set to
    > GlobalUseMulti.
    >
    > Any ideas???
    > Thanks
    >
    >
    >
    > *************** **** .NET C# CONSOLE TEST APP *************** *****
    > using System;
    > using System.Threadin g;
    >
    > namespace ComThreadTest
    > {
    > class MyConsoleApp
    > {
    > [MTAThread]
    > static void Main(string[] args)
    > {
    > TestClass test1 = new TestClass("1");
    > TestClass test2 = new TestClass("2");
    >
    > Thread th = new Thread(new ThreadStart(tes t1.Go));
    > th.ApartmentSta te = ApartmentState. MTA;
    > th.Start();
    >
    > th = new Thread(new ThreadStart(tes t2.Go));
    > th.ApartmentSta te = ApartmentState. MTA;
    > th.Start();
    > }
    > }
    >
    > public class TestClass
    > {
    > public DotNetSleeperCl ass dotnet;
    > public MyCom.SleeperCl ass com;
    > public string mName;
    >
    > public TestClass(strin g name)
    > {
    > mName = name;
    > dotnet = new DotNetSleeperCl ass();
    > com = new MyCom.SleeperCl ass();
    > }
    >
    > [MTAThread]
    > public void Go()
    > {
    > DateTime start = DateTime.Now;
    > Console.WriteLi ne(mName + " OUT at " +
    > start.ToString( "hh:mm:ss:fff") );
    >
    > //THIS WORK FINE BECAUSE IT'S SIMPLE MANAGED CODE.
    > //dotnet.Go();
    >
    > //THIS BLOCKS OTHER THREADS WHILE IT RUNS com.Go();
    >
    > DateTime finish = DateTime.Now;
    > TimeSpan ts = finish - start;
    > Console.WriteLi ne(mName + " IN at " +
    > finish.ToString ("hh:mm:ss:fff" ) + " ELAPSED=" +
    > ts.TotalMillise conds.ToString( ) + " milliseconds.") ;
    > Console.WriteLi ne("Press any key to continue...");
    > Console.Read();
    > }
    > }
    >
    > public class DotNetSleeperCl ass
    > {
    > public DotNetSleeperCl ass() {}
    >
    > public void Go()
    > {
    > Thread.Sleep(30 00);
    > }
    > }
    >
    > }
    >
    >
    > *************** **** VB6 ACTIVEX DLL *************** *****
    > Option Explicit
    > Public Sub Go()
    > 'On my box this runs in about 4 seconds to run.
    > Dim a, b, c
    > For a = 1 To 1000
    > For b = 1 To 1000
    > c = a Mod b
    > DoEvents
    > Next b
    > Next a
    > End Sub[/color]


    Comment

    • Willy Denoyette [MVP]

      #3
      Re: Multithreading concurrency with Interop

      VB6 COM objects are STA objects, that means they must run on an STA thread.
      You did create two instances of the object from two MTA threads, but the object itself will run on a single (COM (OLE) created) STA
      thread, and access from the two MTA threads will be marshaled and synchronized.
      So what you should do is, initialize the threads as STA so that each objects runs on his own STA thread without marshaling and you
      will be fine.

      Willy.


      "Mark Sisson" <mark@corporate digital.com> wrote in message news:88cc4eb9.0 309051440.71098 734@posting.goo gle.com...[color=blue]
      > Hey all.
      > I've got a legacy COM dll that I'd like to use in my multithreaded C#
      > app. Problem though is when I create several instances of the interop
      > object and start them on their own threads they always seem to run
      > synchronously.
      >
      > I've boiled down the code to a simple "lab" experiment below. In this
      > console app you'll see that instance one comes back after roughly 4
      > seconds and then instance two comes back in 8 seconds, twice the time.
      > This indicates that it is wait on the first thread to finish. Why
      > won't they run concurrently?
      >
      > I've tried my best to make sure that the thread types are MTAThread
      > before calling the COM objects. Also, in VB I made sure the project
      > threading model was "Apartment Threaded" and the Instancing set to
      > GlobalUseMulti.
      >
      > Any ideas???
      > Thanks
      >
      >
      >
      > *************** **** .NET C# CONSOLE TEST APP *************** *****
      > using System;
      > using System.Threadin g;
      >
      > namespace ComThreadTest
      > {
      > class MyConsoleApp
      > {
      > [MTAThread]
      > static void Main(string[] args)
      > {
      > TestClass test1 = new TestClass("1");
      > TestClass test2 = new TestClass("2");
      >
      > Thread th = new Thread(new ThreadStart(tes t1.Go));
      > th.ApartmentSta te = ApartmentState. MTA;
      > th.Start();
      >
      > th = new Thread(new ThreadStart(tes t2.Go));
      > th.ApartmentSta te = ApartmentState. MTA;
      > th.Start();
      > }
      > }
      >
      > public class TestClass
      > {
      > public DotNetSleeperCl ass dotnet;
      > public MyCom.SleeperCl ass com;
      > public string mName;
      >
      > public TestClass(strin g name)
      > {
      > mName = name;
      > dotnet = new DotNetSleeperCl ass();
      > com = new MyCom.SleeperCl ass();
      > }
      >
      > [MTAThread]
      > public void Go()
      > {
      > DateTime start = DateTime.Now;
      > Console.WriteLi ne(mName + " OUT at " +
      > start.ToString( "hh:mm:ss:fff") );
      >
      > //THIS WORK FINE BECAUSE IT'S SIMPLE MANAGED CODE.
      > //dotnet.Go();
      >
      > //THIS BLOCKS OTHER THREADS WHILE IT RUNS com.Go();
      >
      > DateTime finish = DateTime.Now;
      > TimeSpan ts = finish - start;
      > Console.WriteLi ne(mName + " IN at " +
      > finish.ToString ("hh:mm:ss:fff" ) + " ELAPSED=" +
      > ts.TotalMillise conds.ToString( ) + " milliseconds.") ;
      > Console.WriteLi ne("Press any key to continue...");
      > Console.Read();
      > }
      > }
      >
      > public class DotNetSleeperCl ass
      > {
      > public DotNetSleeperCl ass() {}
      >
      > public void Go()
      > {
      > Thread.Sleep(30 00);
      > }
      > }
      >
      > }
      >
      >
      > *************** **** VB6 ACTIVEX DLL *************** *****
      > Option Explicit
      > Public Sub Go()
      > 'On my box this runs in about 4 seconds to run.
      > Dim a, b, c
      > For a = 1 To 1000
      > For b = 1 To 1000
      > c = a Mod b
      > DoEvents
      > Next b
      > Next a
      > End Sub[/color]


      Comment

      • Mark Sisson

        #4
        Re: Multithreading concurrency with Interop

        Thank Willy and Justin. You comments are both helpful but I'm still
        not much closer to a solution. I found a few more posts on
        context-switching I'm starting to get the feeling that when you're
        running on a single processor system that you can't be guaranteed of
        any context switching. The latest COM code and timing results are
        below. Note that my only weak attempt at forcing a context-switch is
        with Sleep. Are there any more potent WinAPI's that I could enlist to
        force the context-thread?

        Now let's step out of the lab for a second. In the real world my
        production COM component doesn't sit in a loop doing math
        calculations. Instead it executes an HTTP POST and stores the results
        into SQL. Since the POST is what will take 95% of processing time
        will the CPU-time-slice-manager see this as a context-switching
        opportunity (I hope)? Or am I doomed to think that I will ever be
        able to get my multi-threaded COM objects to be sending 4 concurrent
        HTTP POSTs simultaneously? It's seems ludicrous that while my CPU is
        doing nothing but waiting for the HTTP results (which take between 5
        and 25 seconds) that all other threads could not at least send their
        HTTP POSTs in the meantime. That's the whole point of my architecture
        being multi-threaded.

        Thanks again.
        mark



        =============== COM CODE THAT NEVER SWITCHES CONTEXT ===============
        Option Explicit
        Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As
        Long)

        Public Function Go() As String
        Dim rslt As String
        Dim a As Double
        Dim b As Double
        Dim c As Double
        Dim CR As String

        CR = Chr(13) + Chr(10)
        rslt = CR + " 00%=" + FormatDateTime( Now, vbLongTime) + CR
        For a = 1 To 1000
        If a Mod 100 = 0 Then
        rslt = rslt + " " + CStr(a * 100 / 2000) + "%=" +
        FormatDateTime( Now, vbLongTime) + CR
        Sleep (100)
        End If
        For b = 1 To 1000
        c = a Mod b
        DoEvents
        Next b
        Next a
        Go = rslt
        End Function

        =============== ===== RESULTS SEEN IN .NET CALL APP ===============
        Stats for #1
        00%=7:07:39 AM
        10%=7:07:40 AM
        20%=7:07:41 AM
        30%=7:07:41 AM
        40%=7:07:42 AM
        50%=7:07:43 AM
        60%=7:07:43 AM
        70%=7:07:44 AM
        80%=7:07:45 AM
        90%=7:07:45 AM
        100%=7:07:46 AM

        Stats for #2
        00%=7:07:39 AM
        10%=7:07:47 AM
        20%=7:07:48 AM
        30%=7:07:49 AM
        40%=7:07:49 AM
        50%=7:07:50 AM
        60%=7:07:51 AM
        70%=7:07:52 AM
        80%=7:07:53 AM
        90%=7:07:53 AM
        100%=7:07:54 AM

        Comment

        • Dick Grier

          #5
          Re: Multithreading concurrency with Interop

          Hi,

          The standard way to handle this is to create an ActiveX EXE (convert your
          DLL to an EXE). Then, in the class module where you call the
          "long-winded-process", disconnect the call by invoking a timer that starts
          the actual process, and allow the call from your outside process (in this
          case, your .NET code) to return immediately. When the "long-winded-process"
          is finished, it can execute an event to notify the calling routine. There
          still will be some bookkeeping required to keep identify (associated the
          event with the call).

          Since this still is COM based, creating a free thread in .NET hasn't added
          anything to the equation. The only way to get the COM (STAThread) object to
          act as you want is to create it as a separate process.

          Dick

          --
          Richard Grier (Microsoft Visual Basic MVP)

          See www.hardandsoftware.net for contact information.

          Author of Visual Basic Programmer's Guide to Serial Communications, 3rd
          Edition ISBN 1-890422-27-4 (391 pages) published February 2002.


          Comment

          • Steffen Ramlow

            #6
            Re: Multithreading concurrency with Interop

            Mark Sisson wrote:
            [color=blue]
            > Now let's step out of the lab for a second. In the real world my
            > production COM component doesn't sit in a loop doing math
            > calculations. Instead it executes an HTTP POST and stores the results
            > into SQL. Since the POST is what will take 95% of processing time
            > will the CPU-time-slice-manager see this as a context-switching
            > opportunity (I hope)? Or am I doomed to think that I will ever be
            > able to get my multi-threaded COM objects to be sending 4 concurrent
            > HTTP POSTs simultaneously? It's seems ludicrous that while my CPU is
            > doing nothing but waiting for the HTTP results (which take between 5
            > and 25 seconds) that all other threads could not at least send their
            > HTTP POSTs in the meantime. That's the whole point of my architecture
            > being multi-threaded.[/color]

            Your VB-Dll must be compiled with setting "Apartment threaded" and not
            "single threaded". If you compile it "single threaded", all objects "in" the
            dll will run in the main STA of the process, thus all calls get serialized.


            Comment

            • Steffen Ramlow

              #7
              Re: Multithreading concurrency with Interop

              Dick Grier wrote:
              [color=blue]
              > Since this still is COM based, creating a free thread in .NET hasn't
              > added anything to the equation. The only way to get the COM
              > (STAThread) object to act as you want is to create it as a separate
              > process.[/color]

              What kind of weird tip. Multiprocessing instead of Multithreading.


              Comment

              • Mark Sisson

                #8
                Re: Multithreading concurrency with Interop

                Ok everyone, there's been a lot of input but we still have no
                solution.

                Dick - you proposed that I go Active.exe. That seems a little extreme
                but maybe it really is the only way to go. My problem is that I'm
                dealing with a legacy dll that is very involved and is used by other
                apps. I'm not exactly sure how I'm going to pull that off. What's
                the best-practices on that? Create an ActiveX.exe wrapper?

                Steffen - you barked at the multi-processing tip but have you actually
                tried to make this work? Like me, I think you believe that this SHOULD
                work in theory but haven't actually tried it. Multi-threading just
                doesn't seem to work. The context-switching doesn't happen and they
                won't run in parallel like you'd expect. I've already setup everything
                with MTA (see my first post) and IT DOESN'T WORK! TRY IT!

                Justin - you seem like you've been around the block before on this one
                and point out that it's just not going to switch contexts on a
                single-processor box. BUT HOW DO IT GET THE THREAD TO JUMP INTO A WAIT
                STATE LIKE YOU RECOMMEND? The WinAPI Sleep didn't work. Tons of
                DoEvents didn't work. Any other API?

                All - you all have good advice but if we're going to get to the bottom
                of this may I recommend that back your recommendation with a working
                code sample? There's a lot of "theory" floating around but it just
                theory until you can prove it works. I'm willing to work my ass on to
                try and make this work but I've tried all of your recommendations and
                it's just not happening. The code I posted originally should save you
                most of the typing and I'D REALLY REALLY REALLY APPRECIATE IT!!

                Thanks again for all your help guys.
                mark

                Comment

                • Steffen Ramlow

                  #9
                  Re: Multithreading concurrency with Interop

                  Mark Sisson wrote:[color=blue]
                  > Steffen - you barked at the multi-processing tip but have you actually
                  > tried to make this work? Like me, I think you believe that this SHOULD
                  > work in theory but haven't actually tried it. Multi-threading just
                  > doesn't seem to work. The context-switching doesn't happen and they
                  > won't run in parallel like you'd expect. I've already setup everything
                  > with MTA (see my first post) and IT DOESN'T WORK! TRY IT![/color]

                  You did not anwser my question! Is the VB-dll build "apartment threaded" or
                  "single threaded"?
                  Apartment threaded objects work well in a multithread environment, e.g. each
                  ASP is a STA object, tons of com+ and iis-apps are build with apartment
                  threaded vb-dlls.
                  And you have ignored the hint (forgotten the poster) that you should not mix
                  apartment types (MTA vs. STA).


                  Comment

                  • Mark Sisson

                    #10
                    Re: Multithreading concurrency with Interop

                    > You did not anwser my question! Is the VB-dll build "apartment threaded" or[color=blue]
                    > "single threaded"?
                    > Apartment threaded objects work well in a multithread environment, e.g. each
                    > ASP is a STA object, tons of com+ and iis-apps are build with apartment
                    > threaded vb-dlls.
                    > And you have ignored the hint (forgotten the poster) that you should not mix
                    > apartment types (MTA vs. STA).[/color]

                    Steffen,
                    Ummmmm... in my very first post in explained that my COM object was
                    compiled with threading model set to "Apartment Threaded" and the
                    Instancing set to GlobalUseMulti. I also said that in .NET I'm using
                    the attribute [MTAThread] for all of my class methods and I'm setting
                    the Thread object's ApartmentState property to ApartmentState. MTA
                    before calling it's Start method (that invokes the COM object). Isn't
                    this what you're asking for? If it is, which is where I started from
                    in the first place, it doesn't work. I've already proven that. If it
                    isn't, my apologies I'll make any modifications you think are
                    necessary and try it. If it is, would you mind compiling it and
                    seeing the results for yourself? MTA threading doesn't guarantee
                    context-switching! Justin's explanation seemed to agree but I just
                    don't know where to go from here.

                    Thanks for your help. I'm amazed that I can't get this to work!!!!!!!

                    Comment

                    • Steffen Ramlow

                      #11
                      Re: Multithreading concurrency with Interop

                      Mark Sisson wrote:
                      [color=blue]
                      > Ummmmm... in my very first post in explained that my COM object was
                      > compiled with threading model set to "Apartment Threaded" and the
                      > Instancing set to GlobalUseMulti.[/color]

                      Ok, i missed that. GlobalMultiUse might be a prob, try MultiUse.

                      [color=blue]
                      > I also said that in .NET I'm using
                      > the attribute [MTAThread] for all of my class methods[/color]

                      And someone here said, that this is not a good idea when calling VB-objects.
                      Use STA instead.


                      Comment

                      • Willy Denoyette [MVP]

                        #12
                        Re: Multithreading concurrency with Interop

                        I hope Dick meant create a separate thread ... :-)
                        Anyway, VB style COM objects are always STA. Now in order to prevent apartment marshaling and thread switching you need to create
                        instances in STA initialized apartments.
                        Note also that when you set the [MTAThread] attribute on Main, you effectively initialize the main thread as MTA, when you create
                        instances of STA objects from MTA threads COM will create a separate (unmanaged) thread and initialize it as STA (this is called the
                        default STA), all calls to STA objects from MTA threads will be marshaled (and incur thread switches), in some cases Idispatch calls
                        will fail due to IP marshaling failures.
                        So the advise is use STA (and therefore VB6) objects from compatible apartments only.

                        Willy.


                        "Steffen Ramlow" <s.ramlow@gmx.n et> wrote in message news:OsUqaDVdDH A.3332@TK2MSFTN GP09.phx.gbl...[color=blue]
                        > Dick Grier wrote:
                        >[color=green]
                        > > Since this still is COM based, creating a free thread in .NET hasn't
                        > > added anything to the equation. The only way to get the COM
                        > > (STAThread) object to act as you want is to create it as a separate
                        > > process.[/color]
                        >
                        > What kind of weird tip. Multiprocessing instead of Multithreading.
                        >
                        >[/color]


                        Comment

                        • Dick Grier

                          #13
                          Re: Multithreading concurrency with Interop

                          Hi,

                          I meant a separate process (the COM ActiveX EXE is a simulation of a
                          separate thread by creating a separate process). ActiveX objects that are
                          created in VB do not inherently support free threading. As you note, there
                          can (and will, in my experience) be marshalling errors if you call an STA
                          object from an MTA thread.

                          Now, there are tricky workarounds for this, but they require the use of a
                          helper DLL that was created for the purpose in C, to do the threading work.
                          I haven't tried free threading in VB6 (other than the use of an ActiveX EXE
                          substitute), because it is begging to create a debugging nightmare.

                          Dick

                          --
                          Richard Grier (Microsoft Visual Basic MVP)

                          See www.hardandsoftware.net for contact information.

                          Author of Visual Basic Programmer's Guide to Serial Communications, 3rd
                          Edition ISBN 1-890422-27-4 (391 pages) published February 2002.


                          Comment

                          • Dick Grier

                            #14
                            Re: Multithreading concurrency with Interop

                            Hi,

                            Yes, I suggest creating an ActiveX EXE wrapper. This will be only a few
                            (perhaps as many as 20?) lines of extra code. The complexity depends, at
                            the end of the day, on details that I don't know.

                            --
                            Richard Grier (Microsoft Visual Basic MVP)

                            See www.hardandsoftware.net for contact information.

                            Author of Visual Basic Programmer's Guide to Serial Communications, 3rd
                            Edition ISBN 1-890422-27-4 (391 pages) published February 2002.


                            Comment

                            • Steffen Ramlow

                              #15
                              Re: Multithreading concurrency with Interop

                              Willy Denoyette [MVP] wrote:
                              [color=blue]
                              > "Steffen Ramlow" <s.ramlow@gmx.n et> wrote in message
                              > news:OsUqaDVdDH A.3332@TK2MSFTN GP09.phx.gbl...[color=green]
                              >> Dick Grier wrote:
                              >>[color=darkred]
                              >>> Since this still is COM based, creating a free thread in .NET hasn't
                              >>> added anything to the equation. The only way to get the COM
                              >>> (STAThread) object to act as you want is to create it as a separate
                              >>> process.[/color]
                              >>
                              >> What kind of weird tip. Multiprocessing instead of Multithreading.[/color][/color]
                              [color=blue]
                              > I hope Dick meant create a separate thread ... :-)[/color]

                              No, unfortunately not...
                              [color=blue]
                              > Anyway, VB style COM objects are always STA. Now in order to prevent
                              > apartment marshaling and thread switching you need to create
                              > instances in STA initialized apartments.
                              > Note also that when you set the [MTAThread] attribute on Main, you
                              > effectively initialize the main thread as MTA, when you create
                              > instances of STA objects from MTA threads COM will create a separate
                              > (unmanaged) thread and initialize it as STA (this is called the
                              > default STA), all calls to STA objects from MTA threads will be
                              > marshaled (and incur thread switches), in some cases Idispatch calls
                              > will fail due to IP marshaling failures.
                              > So the advise is use STA (and therefore VB6) objects from compatible
                              > apartments only.[/color]


                              Argh! Willy gave the proper answer already in his first posting! Since i
                              read this from the COM+ / Component Services group, i thought we talk about
                              COM+ (Nice x-post, Mark...). Here you have a STA-Thread Pool and your
                              VB-objects will run on any thread in this pool. But with a "normal" client
                              all your vb objects will served by the main STA thread.

                              So Mark, make your client threads STA and all will be fine.



                              Comment

                              Working...