Lazarus/FreePascal LibreOffice 自动化事件

问题描述 投票:0回答:1

我正在尝试在 Lazarus (Windows) 中编写一个应用程序,它使用 LibreOffice (Calc) 自动化来编辑电子表格并响应修改和保存事件。 我在 Delphi 中有一个工作版本,但我无法直接移植代码,因为 FreePascal 似乎没有 IConnectionPoint 和 IConnectionPoints 接口的任何实现。

我可以使用标准 CreateOleObject 函数成功打开和操作文档以获取顶级对象:

var libreMgr, libreDsk: OleVariant;

libreMgr := CreateOleObject('com.sun.star.ServiceManager');
libreDsk := libreMgr.createInstance('com.sun.star.frame.Desktop'); 

etc...

但我不知道如何创建和连接事件接口。

我的 Delphi 实现(来自很多年前)涉及重写 Delphi TComServer 来加载 LibreOffice 类型库而不将其添加到注册表中,我已经丢失了对此的原始归属,但我当然没有自己从头开始弄清楚它!

这是我原来的 Delphi 单元 - 由于 TConnectionPoints 类型,它无法在 Lazarus 中编译 - 我尝试重新实现此类,但它在运行时什么也不做 - 应用程序运行,但没有收到任何事件。

interface

uses
  ComObj, Windows, ActiveX, AxCtrls, Classes, LibreOfficeEventListener_TLB;

type
  TLibreEvent = procedure(const Source: OleVariant) of object;

  ILibreOfficeEvents = interface(IDispatch)
    ['{06E57844-1345-406B-A18B-C7D88C44F11E}']
    // additional events can be added here if required, this interface
    // does not need to be immutable
    function Get_OnUnload: TLibreEvent;
    procedure Set_OnUnload(Value: TLibreEvent);
     function Get_OnModifyChanged: TLibreEvent;
    procedure Set_OnModifyChanged(Value: TLibreEvent);
    function Get_OnPrepareUnload: TLibreEvent;
    procedure Set_OnPrepareUnload(Value: TLibreEvent);
    property OnUnload: TLibreEvent read Get_OnUnload write Set_OnUnload;
    property OnModifyChanged: TLibreEvent read Get_OnModifyChanged write Set_OnModifyChanged;
     property OnPrepareUnload: TLibreEvent read Get_OnPrepareUnload write Set_OnPrepareUnload;
  end;

  // To connect to LibreOffice document events, we have to implement an
  // IDispatch listener with a type library, but it does not need to be registered.
  // This is an acceptable compromise - it is not a full-blown COM object,
  // but the type-library must be included as a resource in the EXE.
  TLibreOfficeEventListener = class(TAutoObject,
    IConnectionPointContainer,
    ILibreOfficeEventListener,
    ILibreOfficeEvents)
  private
    { Private declarations }
    FConnectionPoints: TConnectionPoints;
    FConnectionPoint: TConnectionPoint;
    FEvents: ILibreOfficeEventListenerEvents;
    { note: FEvents maintains a *single* event sink. For access to more
      than one event sink, use FConnectionPoint.SinkList, and iterate
      through the list of sinks. }
    FOnUnload: TLibreEvent;
    FOnModifyChanged: TLibreEvent;
    FOnPrepareUnload: TLibreEvent;
  protected
    { Protected declarations }
    property ConnectionPoints: TConnectionPoints read FConnectionPoints implements IConnectionPointContainer;
    procedure EventSinkChanged(const EventSink: IUnknown); override;
    { ILibreOfficeEventListener methods }
    procedure disposing; safecall;
    procedure notifyEvent(const event: IDispatch); safecall;
    { ILibreOfficeEvents methods }
    function Get_OnUnload: TLibreEvent;
    procedure Set_OnUnload(Value: TLibreEvent);
     function Get_OnModifyChanged: TLibreEvent;
    procedure Set_OnModifyChanged(Value: TLibreEvent);
    function Get_OnPrepareUnload: TLibreEvent;
    procedure Set_OnPrepareUnload(Value: TLibreEvent);
  public
    procedure Initialize; override;
  end;

{$R LibreOfficeEventListener.TLB} // type library is REQUIRED for hooking LibreOffice events

implementation

uses ComServNoRegister;

type
  TAutoObjectFactoryNoRegister = class(TAutoObjectFactory)
  public
    procedure UpdateRegistry(Register: Boolean); override;
  end;

procedure TLibreOfficeEventListener.EventSinkChanged(const EventSink: IUnknown);
begin
  FEvents := EventSink as ILibreOfficeEventListenerEvents;
end;

procedure TLibreOfficeEventListener.Initialize;
begin
  inherited Initialize;
  FConnectionPoints := TConnectionPoints.Create(Self);
  if AutoFactory.EventTypeInfo <> nil then
    FConnectionPoint := FConnectionPoints.CreateConnectionPoint(
      AutoFactory.EventIID, ckSingle, EventConnect)
  else FConnectionPoint := nil;
end;


procedure TLibreOfficeEventListener.disposing;
begin
   // do nothing here
end;

procedure TLibreOfficeEventListener.notifyEvent(const event: IDispatch);
var
    e: OleVariant;
begin
    e := event;
   { document events which can be intercepted:
     OnTitleChanged
     OnModifyChanged
     OnStorageChanged
     OnModeChanged
     OnSave
     OnSaveDone
     OnSaveAs
     OnSaveAsDone
     OnPrepareUnload
     OnUnload
   }
   if e.EventName='OnModifyChanged' then begin
    if Assigned(FOnModifyChanged) then FOnModifyChanged(e.Source);
   end
   else if e.EventName='OnPrepareUnload' then begin // CM 12/09/2024 added
    if Assigned(FOnPrepareUnload) then FOnPrepareUnload(e.Source);
   end
   else if e.EventName='OnUnload' then begin
    if Assigned(FOnUnload) then FOnUnload(e.Source);
   end;
end;

function TLibreOfficeEventListener.Get_OnUnload: TLibreEvent;
begin
   Result := FOnUnload;
end;

procedure TLibreOfficeEventListener.Set_OnUnload(Value: TLibreEvent);
begin
   FOnUnload := Value;
end;

{ TAutoObjectFactoryNoRegister }

procedure TAutoObjectFactoryNoRegister.UpdateRegistry(Register: Boolean);
begin
   // do nothing - do not save COM class or typelib info in the registry.
   // See also ComServNoRegister.pas
end;

function TLibreOfficeEventListener.Get_OnModifyChanged: TLibreEvent;
begin
   Result := FOnModifyChanged;
end;

procedure TLibreOfficeEventListener.Set_OnModifyChanged(Value: TLibreEvent);
begin
   FOnModifyChanged := Value;
end;

function TLibreOfficeEventListener.Get_OnPrepareUnload: TLibreEvent;
begin
    Result := FOnPrepareUnload;
end;

procedure TLibreOfficeEventListener.Set_OnPrepareUnload(Value: TLibreEvent);
begin
    FOnPrepareUnload := Value;
end;

initialization
  TAutoObjectFactoryNoRegister.Create(ComServer, TLibreOfficeEventListener,
    Class_LibreOfficeEventListener, ciMultiInstance, tmApartment);
end.

这应该通过调用

与打开的 LO 文档挂钩
libreDoc.addEventListener(libreEvents);

但是,正如我所说,这没有任何作用。

任何帮助,或指向其他实现的指针都会有很大的帮助,谢谢!

以前有人这样做过吗?

libreoffice freepascal lazarus
1个回答
0
投票

您对 TLibreEvent 的定义是一个双指针(一个用于方法地址,一个用于 SELF/THIS),并且它在 COM 接口中使用。 libreoffice 不太可能支持这种 Delphi 内部样式事件。

我还错过了接口定义中的任何调用约定修饰符。

您可以尝试在 lazarus 论坛上发布示例项目,一般来说,C/C++ 示例比 Python 更适合(因为您永远不知道解释器和/或包在幕后屏蔽了什么)

© www.soinside.com 2019 - 2024. All rights reserved.