C ++ Builder:为不同的线程编组的接口

时间:2015-11-15 14:16:32

标签: c++ delphi com c++builder vcl

为了从线程中使用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();
}
//---------------------------------------------------------------------------

3 个答案:

答案 0 :(得分:1)

您需要在同一个线程上调用CoInitializeCoUninitialize,因为它们作用于调用线程。 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/__finallyExecute()块的需求(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");
}