1

我有一些用于打开 IE 的继承代码,但遇到了一些麻烦,这就是我所拥有的:

IEUnit.pas (no form) 有 LoadIE 和 OpenIE 的例程

从单独的线程调用 OpenIE,它看起来像这样

procedure OpenIE(URL: OleVariant; FieldValues: string = '');
var ie : IWebBrowser2; // <-- This should become "global" variable for the IEUnit
begin
  ie := CreateOleObject('InternetExplorer.Application') as IWebBrowser2;
  ie.Navigate2(URL, Flags, TargetFrameName, PostData, Headers);
  ShowWindow(ie.HWND, SW_SHOWMAXIMIZED);
  ie.Visible := true;
  ...
end;

我想在单元中有“全局”即变量,并像这样编写 LoadIE 例程:

LoadIE 应该从 FormCreate 调用(主线程)

它应该只创建“全局”即对象

  ie := CreateOleObject('InternetExplorer.Application') as IWebBrowser2;

所以OpenIE函数不需要创建它,只是为了使用它(目的是加快速度)

所以问题是如何从 2 个不同的线程访问同一个 OLE 对象,一个创建对象,另一个使用它。


当我编写不处理线程的代码时,我得到一个错误

带有消息的异常类 EOleSysError '应用程序调用了为不同线程编组的接口'

我应该怎么做,照顾线程(我没有线程经验,欢迎一些阅读和视频链接)。

提前致谢

4

4 回答 4

6

MTA 模型COM 服务器只能在其关联的公寓内使用。以下是对错误的解释,并提供以下建议:

将接口指针(直接指针或代理指针)从一个单元传输到另一个单元的正确方法是通过 COM 的编组机制。源单元可以调用 CoMarshalInterThreadInterfaceInStream()将接口指针编组到共享(全局)流。目标单元可以通过调用CoGetInterfaceAndReleaseStream()来解组这个接口指针 。

于 2012-10-11T13:58:07.503 回答
3

如您所知,因为它是您上一个问题的主题,所以您需要对 COM 对象的所有调用都来自同一个线程。显而易见的选择是主 GUI 线程。因此,IWebBrowser2在主窗体的OnCreate事件处理程序中创建。然后在需要显示浏览器时使用TThread.Synchronize或。TThread.Queue您传递给SynchronizeQueue将在主 GUI 线程上执行的代码。

假设您使用的是支持匿名方法的现代版本的 Delphi,您可以这样编写:

procedure TMyThread.ShowBrowser(const URL: string);
var
  Proc: TThreadProcedure;
begin
  Proc := procedure
    begin
      MainForm.Browser.Navigate2(URL, ...);
      ShowWindow(MainForm.Browser.HWND, SW_SHOWMAXIMIZED);
      MainForm.Browser.Visible := true;
    end;
  Queue(Proc);
end;
于 2012-10-11T14:03:24.893 回答
2

非自由线程 COM 对象只能由/on/in 与它为/on/in/with 创建的线程在同一线程中使用。在您的情况下,为了加快速度,我将使用treadvarIWebBrowser2 类型的普通全局值,或者您的类中覆盖 TThread 的属性。

于 2012-10-11T14:42:59.157 回答
1

使用 CriticalSection 包装对 OleObject 的所有调用。还使用 Synchronize 从“其他”线程调用主 UI 线程(这就是你得到异常的原因)。

于 2012-10-11T13:41:14.020 回答