Reputation: 37
I am working at WinForm Application which works fine with COM Object because it use single thread .But my system is also using progressBar which load data from COM Object ,here problem starts
GUI freezed during loading data (data getting from COM Object ) .
To Solve this problem ,i tried to use BackgroundWork which solved problem of freezed GUI .But Later i found BackgroundWork use Threadpool by default which create error in COM Object class because of Multithread .I had also tried this code create single ,but still not working
Thread thread = new Thread(() =>
{
Firmware_update_access();//it consumes Com Object
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
COM component is dll which which creates exception when i try to run under new thread included (thread.SetApartmentState(ApartmentState.STA) .Exception is System.InvalidCastException: HRESULT: 0x80004002 (E_NOINTERFACE)). My Question is how we can create new a STA thread(other than UI thread) which should able use to COM object .
Upvotes: 0
Views: 1891
Reputation: 671
So, create, access and dispose you managed COM wrapper (RCW) in ONE dedicated background thread, i.e. all the COM wrapper access is done in the thread callback method.
Data or method calls have to be marshalled from/to the UI thread.
Update:
Depending on your performance and other requirements, you have two choices:
Task.Factory.StartNew()
) for every COM server call. You only have to ensure, that only one thread accesses the COM server at the same time, otherwise the threading issue occurs.Example for choice 1:
Task.Factory.StartNew(() =>
{
MyRCWType objServer = null;
try
{
objServer = new MyRCWType(); // create COM wrapper object
objServer.MyMethodCall1();
objServer.MyMethodCall2();
}
catch(Exception ex)
{
// Handle exception
}
finally
{
if(objServer != null)
{
while(Marshal.ReleaseComObject(objDA) > 0); // dispose COM wrapper object
}
}
});
Example for choice 2:
private enum MethodCallEnum { None, Method1, Method2 };
private Queue<MethodCallEnum> _queue = new Queue<MethodCallEnum>();
private AutoResetEvent _evtQueue = new AutoResetEvent(false);
private object _syncRoot = new object();
private Thread _myRCWWorker;
private void CancellationTokenSource _cancelSource = new CancellationTokenSource();
// maybe in your main form
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// create one and only background thread for RCW access
_myRCWWorker = new Thread(() => DoRCWWork(_cancelSource.Token, _evtQueue));
_myRCWWorker.IsBackground = true;
_myRCWWorker.Start();
}
private void CallMethod1()
{
Enqueue(MethodCallEnum.Method1);
}
private void Enqueue(MethodCallEnum m)
{
lock(_syncRoot)
{
_queue.Enqueue(m);
}
_evtQueue.Set(); // signal new method call
}
private MethodCallEnum Dequeue()
{
MethodCallEnum m = MethodCallEnum.None;
lock(_syncRoot)
{
if(_queue.Count > 0)
m = _queue.Dequeue();
}
return m;
}
private void DoRCWWork(CancellationToken cancelToken, WaitHandle evtQueue)
{
MyRCWType objServer = null;
int waitResult;
try
{
objServer = new MyRCWType(); // create COM wrapper object
while (!cancelToken.IsCancellationRequested)
{
if(evtQueue.WaitOne(200))
{
MethodCallEnum m = Dequeue();
switch(m)
{
case MethodCallEnum.Method1:
objServer.MyMethodCall1();
break;
case MethodCallEnum.Method2:
objServer.MyMethodCall2();
break;
}
}
}
}
catch(Exception ex)
{
// Handle exception
}
finally
{
if(objServer != null)
{
while(Marshal.ReleaseComObject(objDA) > 0); // dispose COM wrapper object
}
}
}
This is only a very simple example to show the program flow. You can trigger COM server calls from any thread by calling the Enqueue()
method, but the COM server is accessed only from the dedicated thread.
Upvotes: 1