How to instantiate an ActiveX control on a Windows Form using a STA from a MTA component?

I need to use an ActiveX control, "AxData", in a .NET (C#) adapter component.  The problem is that when the component creates an instance of the form that hosts AxData, an exception (crash) occurs that says that AxData could not be instantiated because the calling thread is not STA.  

The adapter component derives from an abstract class, which is called from a C# app, whose default is ApartmentState.MTA.   The code snippet shows how the component instantiates the form.  How can I run the form in a thread with ApartmentState.STA so that I can use AxData as shown?

(If there were a way to use the Ax control without a [hidden] Windows Form, I'd actually prefer that solution.  Is there?)

/* The snippet isn't complete for simplicity, but the idea is to be running the _client form in a STA */
namespace TheNamespace
{
	public class MyAdapter: AbstractClass
	{
		private static frmDataClient _client;
		
		public override void Init()
		{
			_client = new frmDataClient();
		}
		
		public override DataBunches GetData(string dataSymbol)
		{
			_client.DataRequest(dataSymbol);
			:
			:
			return...
		}		
	}
				
	/* form hosting axData */
	public partial class frmDataClient : Form
	{
		private bool _dataConnected = false;
		
		
		public frmDataClient()
		{
			InitializeComponent();
		}
		
		internal void ConnectHistoryServer()
		{
			axData.MakeConnection("user", "pass");
		}
		
		void axData_Connected(object sender, AxData._IDataEvents_ConnectedEvent e)
		{
			_dataConnected = true;   
		}
		
		internal void DataRequest(string symbol)
		{
			if (!_dataConnected) 
				ConnectHistoryServer();
			
			if (_dataConnected)
				axData.RequestData(symbol);
		}
		
		void axData_UpdateData(object sender, AxData._IDataEvents_UpdateDataEvent e)
		{
			// data returned in response to axData.RequestData
		}
	}
}

Open in new window

Strebor5Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Bob LearnedCommented:
1) I have no idea what ActiveX component you are using, but most Active controls require a site, like a Form, so you usually are not allowed to just create an instance without a form.

2) I believe that you need to execute the form in a single apartment thread, but that may cause you problems with the Windows Form, which usually runs in a multi-thread.  

3) Creating a thread, and setting the apartment state is done like this:

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

Reference:

Part 3: Using Threads
http://www.albahari.com/threading/part3.aspx
0
Strebor5Author Commented:
Thanks for trying to help.

Re. 1. In the snippet I provided, frmDataClient is Windows Form hosting the ActiveX component.  The form is used only as a container for ActiveX, and nothing else.

Re. 2. Yes, this is basically a restatement of my question, so you do understand what I'm asking. :)

Re. 3. That reference is a good one I've worked with already, but the problem is how to start that STA in the context of my component.  Creating a new thread and instantiating frmDataClient in the thread's start method doesn't work either;I don't know why yet.

Update: I think one of the problems I'm up against is that several instances of my component can be created.  I've marked the init method with [STAThread], and the first time through it starts in a STA; everytime thereafter it's a MTA, and this is likely the major problem.
0
Bob LearnedCommented:
May I be so bold as to ask what "AxData" does for you?  Is it a 3rd party ActiveX control?
0
The Ultimate Tool Kit for Technolgy Solution Provi

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy for valuable how-to assets including sample agreements, checklists, flowcharts, and more!

Strebor5Author Commented:
Sure.  Indeed, it's a 3rd-party control (actually there are more than one) that provides internet/intranet access to a data servers. The control translates responses from the server into events (also illustrated in the snippet), and, it has a number of classes to use for data manipulation, etc.
0
Bob LearnedCommented:
I always try to figure out whether the pain that is experienced when working with COM can be supplanted.  That usually requires understanding what you are working with, and whether is can be translated to .NET or not.  This one doesn't look like a good candidate.  

Is this an ActiveX control that requires hosting on a form, or can it be instantiated without a placeable control?  That question requires more information that I have at my disposal...
0
Strebor5Author Commented:
That's a good question and I'm not certain of the answer (see the very last statement in my original post)...

At first, I didn't think I needed to put these on a form at all - and that is preferred.  I just created a reference to the AcitveX libraries, and the COM Interop showed up.  I started programming around that for a while, compiles fine, but the "Connect" method of one of the controls always returned an "invalid parameters" response, even though the same parameters worked in a test project that used a form.  The response was a "valid" response from the object, so it worked at *some level*.  

However, when I started programming the event handlers, the parameter list of the methods generated by the VS was nonsensical.  Instead of having sender and an event argument, string and integer types appeared.  That's when I gave up on the pure COM Interop implementation.
0
Bob LearnedCommented:
Who is the vendor for these 3rd party controls (if you are allowed to tell me)?
0
Strebor5Author Commented:
It's the eSignal Control API; dbcctrl.dll
0
Bob LearnedCommented:
I can see from the eSignal web site that they appear to have an XML web service.  I don't have access to the online support.

Developer Support
http://developer.esignal.com/control/
0
Strebor5Author Commented:
I'm registered for their developer's toolkit, but the problem is mine, not theirs.  (Not sure if you were implying to use their support.)

They have a Standard API for C++, maybe that uses the XML web service, though I'd be surprise if their data were delivered that way.  The Control API is probably a wrapper for the Standard API.

If you want to take a look at dbcctrl.dll, I don't have a problem sending it to you privately.  You could also install the eSignal app and get it there too, I think.  Is my e-mail available in my profile?  (Not sure, I'm new here.)
0
Bob LearnedCommented:
1) It is against the member agreement to do background work on a question.

2) I would be willing to help you, but I don't believe that I would need to install the API library.

3) I would investigate using an open XML feed, if it is possible, since that would be significantly easier to use over a COM library.
0
Strebor5Author Commented:
I appreciate the dialog here, but I think I found the missing piece of the puzzle - actually an oversight in my code.  Basically the idea of creating a static instance of the form in the STAThread works.  What I had forgotten was not to keep recreating the form!  The code in the snippet changes to this -

               private static frmDataClient _client;

                [STAThread]
                public override void Init()
                {
                        if(_client = null) _client = new frmDataClient();
                }

This keeps the form in the STAThread, which always worked on the first call to Init() even though subsequent calls to Init() are MTA.   So far, this is working.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Strebor5Author Commented:
Of course that should have been:

                        if(_client == null) _client = new frmDataClient();

No way to edit posts here it seems. :(
0
Bob LearnedCommented:
Good job, grasshopper!!
0
Strebor5Author Commented:
I actually found the solution myself, but I appreciated the dialog from TheLearnedOne.  I'll try to award split points.

I'll leave as a closing comment for anyone else happening to read the thread:
The main problem, however, remains, which is to make a multi-threaded .NET app interact with a COM server.  I think I've found that it can't. Since the COM process is running in a STA, it's not "legal" to use it from any other thread than the one that created it.  Consequently, another solution that does not use COM is required, and this will involve "Using Unmanaged C++ Libraries (DLLs) in .NET Applications".
0
Bob LearnedCommented:
Oh, yeah, go ahead and slam the experts!!  Maybe I was having a bad hair day that day!!  I always try to remember not to "bite the hand that feeds".
0
Strebor5Author Commented:
This was my first and only question here, so I don't understand where you're coming from.  I would have awarded points to you if I could have marked one of your answers as the solution, but I couldn't.  Tell me where I've gone wrong, what the etiquette is, and I'll try not to "bite" next time.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Web Components

From novice to tech pro — start learning today.