为了从线程中使用COM对象,我将CoInitialize(NULL)插入到线程Execute函数中,并将CoUninitialize()插入到Terminate函数中。
一切正常,除非用户通过调用调用表单中的Terminate函数来中止线程。
似乎表单调用的Terminate函数被视为另一个线程(错误消息:'应用程序称为为不同线程编组的接口')。
另一方面,我无法通过使用Synchronize将代码放入要调用的特定函数中。这种方式使程序保持不变,直到被调用函数的COM进程结束。
我知道存在重新编写COM编组的函数。但是不知道该怎么做。我也没有在C ++中找到例子。
在寻求帮助之前,我尝试了各种方法来解决问题。不幸的是我在这里。
这是我的代码:
class TThreadCamera : public TThread
{
private:
Variant Camera;
protected:
void __fastcall Execute();
public:
void __fastcall Terminate(TObject *Sender);
public:
__fastcall TThreadCamera();
};
-
__fastcall TThreadCamera::TThreadCamera()
: TThread(false)
{
}
//---------------------------------------------------------------------------
void __fastcall TThreadCamera::Execute()
{
FreeOnTerminate = true;
OnTerminate = &Terminate;
CoInitialize(NULL);
Camera = Variant::CreateObject("ASCOM.Simulator.Camera");
Camera.OlePropertySet("Connected", true);
Camera.OleProcedure("StartExposure", 60, true);
while ((! (bool) Camera.OlePropertyGet("ImageReady")))
Sleep 100;
}
//---------------------------------------------------------------------------
void __fastcall TThreadCamera::Terminate(TObject *Sender)
{
if (Camera.OlePropertyGet("CameraState") == 2) // Exposure currently in progress
Camera.OleProcedure("AbortExposure");
CoUninitialize();
}
//---------------------------------------------------------------------------
答案 0 :(得分:1)
您需要在同一个线程上调用CoInitialize
和CoUninitialize
,因为它们作用于调用线程。 OnTerminate
事件始终在主线程上执行。
所以,删除你的OnTerminate
事件处理程序,将该代码移到线程中,然后从线程中调用CoUninitialize
:
void __fastcall TThreadCamera::Execute()
{
FreeOnTerminate = true;
CoInitialize(NULL);
Camera = Variant::CreateObject("ASCOM.Simulator.Camera");
// code to operate on the camera goes here
CoUninitialize();
}
保护finally块内的未初始化可能是谨慎的。
答案 1 :(得分:1)
在Delphi中,如果需要在线程上下文中调用线程终止代码,则应该覆盖受保护的TThread.DoTerminate
方法,而不是编写OnTerminate
事件处理程序。
答案 2 :(得分:1)
在主UI线程的上下文中调用TThread.OnTerminate
事件。工作线程在TThread.DoSynchronize()
退出后调用的虚拟Execute()
方法使用TThread.Synchronize()
来调用OnTerminate
。始终调用DoTerminate()
,即使Execute()
因未捕获的异常而退出,因此覆盖DoTerminate()
是执行特定于线程的清理的好方法。
CoInitialize()
和CoUninitialize()
。因此,您必须在CoUninitialize()
内拨打Execute()
,或覆盖DoTerminate()
。我更喜欢后者,因为它减少了在try/catch
中使用try/__finally
或Execute()
块的需求(RAII解决方案,例如TInitOle
中的utilscls.h
,甚至更好)。
只能在创建它的线程的上下文中访问单元线程COM对象。因此,您必须在CameraState
内调用相机的AbortExposure()
属性和Execute()
程序,或者覆盖DoTerminate()
。
TThread.Terminate()
方法只是将TThread.Terminated
属性设置为true,它什么都不做。 Execute()
有责任定期检查Terminated
财产并尽快退出。等待摄像机的while
属性为真的ImageReady
可以而且应该检查线程的Terminated
属性,以便在请求时停止等待。
尝试更像这样的事情:
class TThreadCamera : public TThread
{
private:
bool init;
protected:
void __fastcall Execute();
void __fastcall DoTerminate();
public:
__fastcall TThreadCamera();
};
__fastcall TThreadCamera::TThreadCamera()
: TThread(false)
{
FreeOnTerminate = true;
}
void __fastcall TThreadCamera::Execute()
{
init = SUCCEEDED(CoInitialize(NULL));
if (!init) return;
Variant Camera = Variant::CreateObject("ASCOM.Simulator.Camera");
Camera.OlePropertySet("Connected", true);
Camera.OleProcedure("StartExposure", 60, true);
while (!Terminated)
{
if ((bool) Camera.OlePropertyGet("ImageReady"))
return;
Sleep(100);
}
if (Camera.OlePropertyGet("CameraState") == 2) // Exposure currently in progress
Camera.OleProcedure("AbortExposure");
}
void __fastcall TThreadCamera::DoTerminate()
{
if (init) CoUninitialize();
TThread::DoTerminated();
}
或者:
class TThreadCamera : public TThread
{
protected:
void __fastcall Execute();
public:
__fastcall TThreadCamera();
};
#include <utilcls.h>
__fastcall TThreadCamera::TThreadCamera()
: TThread(false)
{
FreeOnTerminate = true;
}
void __fastcall TThreadCamera::Execute()
{
TInitOle oleInit;
Variant Camera = Variant::CreateObject("ASCOM.Simulator.Camera");
Camera.OlePropertySet("Connected", true);
Camera.OleProcedure("StartExposure", 60, true);
while (!Terminated)
{
if ((bool) Camera.OlePropertyGet("ImageReady"))
return;
Sleep(100);
}
if (Camera.OlePropertyGet("CameraState") == 2) // Exposure currently in progress
Camera.OleProcedure("AbortExposure");
}