【问题标题】:Automation server is launched twice with CreateOleObject but not all the time自动化服务器使用 CreateOleObject 启动两次,但不是一直
【发布时间】:2014-07-01 06:38:11
【问题描述】:

我有一个用 Delphi 7 编写的程序,它也是一个自动化服务器。

自动化服务器注册方式如下:

  TAutoObjectFactory.Create(ComServer, TMyServer, Class_App,
    ciMultiInstance, tmSingle);

我有两个 COM 加载项,一个用于 Word,一个用于 Outlook。他们都使用自动化服务器从主程序中获取一些信息。加载项调用以下代码,即:当用户点击加载项中的按钮时:

 MyServerApp: Variant;
begin
 MyServerApp := CreateOleObject('MyServer.App');
 try
   MyServerApp.DoSomething;
 finally
   MyServerApp := UnAssigned;
 end–

问题出在: 大多数情况下,代码工作正常。如果主应用程序已经在运行,插件将连接到自动化服务器并执行它们的操作,如果它没有运行,那么插件将启动主应用程序。

但由于某些未知情况,尤其是 Outlook,有时会出现即使主程序正在运行,加载项也不会连接到它,而是会再次重新启动主应用程序并连接到这个新实例的自动化服务器。灾难来了:因为我的应用程序不允许自己在两个实例中运行,所以第二个应用程序实例只会显示一条错误消息,而我的加载项将冻结整个 Outlook。

为什么会这样?为什么 CreateOleObject 在大多数情况下都会像它应该的那样连接,并且不时地再次启动我的应用程序?

【问题讨论】:

  • 什么版本的 Windows?我在 XP 中看到过类似的问题,但在 Windows 7、Vista 或 Win8 中从未见过。
  • 这是带有 Outlook2010 的 Windows 7,但我记得在不同的 Win 和 Outlook 版本中也看到了错误。

标签: delphi com automation delphi-7 createoleobject


【解决方案1】:

你真的不应该在一篇文章中问多个问题。

问题 1

这件事发生在我身上很多。 问题是 Office 会为触发加载项中的代码的每个事件生成两个调用。 我找到的解决方案是只响应第一个电话。

我将Add-In Express 用于 COM 插件,它为我提供了一些可以链接到的事件。
我不确定你是否正在使用它,但这是我使用的代码:

interface

....

var
  MyApp: TAddInModule = nil;

implementation

procedure TAddInModule.adxCOMAddInModuleAddInFinalize(Sender: TObject);
begin
  MyApp:= nil;
end;

procedure TAddInModule.adxCOMAddInModuleAddInInitialize(Sender: TObject);
begin
  if not(Assigned(MyApp)) then try
    MyApp:= Self;
  except
    {ignore}
  end; {if try}
end;

在事件处理程序中,您必须测试是否引用了第一个实例或幽灵实例。 (有时两者都会被调用)。

procedure TAddInModule.adxCommandBar1Controls3Click(Sender: TObject);
begin
  if (Self <> MyApp) then exit;
  //ToggleDisplay
  if not(ExcelBezig(xbQuestion)) then try
    ToggleDisplay;
  except {ignore}
  end;
end;

这是一个混搭(我承认),但它一劳永逸地解决了问题,并且该插件从那时起就非常稳定。

不要一遍又一遍地重新创建链接
您不应该在每次需要查询应用程序时都使用CreateOleObject('MyServer.App');。当插件被激活时,您调用一次CreateOleObject,存储该实例,然后重用该链接。比如:

procedure TAddInModule.adxCOMAddInModuleAddInInitialize(Sender: TObject);
begin
  if not(Assigned(MyApp)) then try
    MyApp:= Self;
    MyServerApp:= CreateOleObject('MyServer.App');
  except
    {ignore}
  end; {if try}
end;


procedure TAddInModule.adxCommandBar1Controls3Click(Sender: TObject);
begin
  if (Self <> MyApp) then exit;
  try
    MyServerApp.DoSomething;
  except
    {ignore}
  end;
end;

使用变体访问自动化服务器很慢!
因为您使用变体来存储对自动化服务的引用,Delphi 无法在编译时解析您的调用。
它也不能帮助您避免拼写错误和其他错误。
对通过变体访问的服务器的任何调用都是有效的。

所以

 MyServer.StupidTyyyypo('hallo').doesnotexist('should be integer');

编译不会出错。
如果您导入类型库并将访问变量设置为特定类型,例如:

type
  TMyServer = IMyServer;

您可以通过从 Delphi 自动化服务器导入类型库来获得 IMyServer,请参阅:http://www.blong.com/Articles/Automation%20In%20Delphi/Automation.htm
部分:Controlling Automation Servers Using Interfaces 及以下。

问题 2

为什么 CreateOleObject 连接到正在运行的应用程序实例,而不是一直创建一个单独的实例?

查看官方文档:http://docwiki.embarcadero.com/Libraries/XE2/en/System.Win.ComObj.CreateOleObject

它说:

CreateOleObject 创建由 ClassName 参数指定的类的单个未初始化对象。 ClassName 指定类 ID (CLSID) 的字符串表示形式。 CreateOleObject 用于在 CLSID 已知且对象位于本地或进程内服务器上时创建指定类型的对象。仅使用 CreateOleObject 创建不属于聚合的对象。

注意:在 Delphi 代码中,CreateOleObject 被调用一次以创建一个类的每个新的单个实例。 要创建同一个类的多个实例,建议使用类工厂。

问题 3

tmSingle 线程模型是否意味着对自动化服务器的所有调用都在应用程序的主线程中执行?

你应该在一个单独的问题中问这个问题。

【讨论】:

  • 我不明白答案 1。我正在使用 add-in express,但是我对主应用程序自动化服务器的所有调用都发生在用户引发的事件中 - 即。在您的示例中, adxCommandBar1Controls3Click 事件。我知道 AddInFinalize 或 AddInInitialize 可能会被多次调用,但根据我的经验,用户引发的事件只会被调用一次。 答案 2: 我不想创建同一个类的多个实例,我想创建多个单个实例。这就是大多数时候发生的情况,在主应用程序中创建了新实例,但为什么不总是这样?
  • 我澄清了原来的问题,请再读一遍——也许现在更有意义了。谢谢!
  • 我认为创建一次自动化服务器并重用变量不是一个好习惯,尤其是。在加载项中。例如,Word 加载项将具有多个实例,因为 Word 可以在多个副本中启动。这些实例取决于加载项 express 的工作方式,可能共享相同的地址空间,这意味着使用全局变量可能会导致完全混乱......甚至不谈论 Ole 接口在变量超出范围时释放变量!关于 CreateOleObject 请查看我之前评论中的答案 2,帮助文件可以用不同的方式理解...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-17
  • 2013-12-25
相关资源
最近更新 更多