我正在使用WinForm应用程序,它可以正常使用COM对象,因为它使用单线程。但我的系统也使用了从COM对象加载数据的progressBar,这里问题开始
在加载数据期间(从COM对象获取数据)冻结GUI。
为了解决这个问题,我尝试使用BackgroundWork解决了冻结GUI的问题。但后来我发现BackgroundWork默认使用Threadpool因为Multithread而在COM Object类中创建错误。我也试过这个代码创建单身,但仍无法正常工作
Thread thread = new Thread(() =>
{
Firmware_update_access();//it consumes Com Object
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
COM组件是dll,当我尝试在包含的新线程下运行时会创建异常(thread.SetApartmentState(ApartmentState.STA).Exception是System.InvalidCastException: HRESULT:0x80004002(E_NOINTERFACE) )。 我的问题是我们如何创建一个新的STA线程(除了UI线程),它应该可以用于COM对象。
答案 0 :(得分:1)
因此,在一个专用后台线程中创建,访问和配置托管COM包装器(RCW),即所有COM包装器访问都在线程回调方法中完成。
必须从/向UI线程编组数据或方法调用。
<强>更新强>
根据您的表现和其他要求,您有两种选择:
Task.Factory.StartNew()
)。您只需要确保只有一个线程同时访问COM服务器,否则会出现线程问题。选择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
}
}
});
选择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
}
}
}
这只是一个显示程序流程的简单示例。您可以通过调用Enqueue()
方法从任何线程触发COM服务器调用,但只能从专用线程访问COM服务器。