从OleContainer学到的东西

 从OleContainer学到的东西

最近在写一个东西,里面牵涉到了嵌入word的功能,打开word,得到word的文档结构图,实现word中
文档结构图类似的功能(跳转到当前标题),还有把word转换成PDF,最后把PDF文件Base64加密变成字符串存储在xml中。

(1)由于要嵌入到程序中,所以用到了oleContianer,但是这个东西用起来真是有一番曲折,开始的时候打开文件直接用CreateObjectFromFile,但是在某些情况下(文件有一些修改存档)有的文件打开会有问题,具体原因不明,经过多方尝试,终于解决这个问题,方案就是从流中把文件读到olecontianer,完整的方法如下:
type
  //流Header的结构
  TStreamHeader = record
    Signature: Integer;    //$434F4442
    DrawAspect: Integer;   //1
    DataSize: Integer;     //stream.size;
  end;

procedure LoadFromFile(const AFileName: string; Container: TOleContainer);
var
  Header : TStreamHeader;
  oMemoryStream : TMemoryStream;
  oFileStream: TFileStream;
begin
  oFileStream := TFileStream.Create(AFileName, fmShareDenyNone);
  oMemoryStream := TMemoryStream.Create;
  try
    with Header do
    begin
      Signature := $434F4442;
      DrawAspect := 1;
      DataSize := oFileStream.Size;
    end;
    oFileStream.Position := 0;
    oMemoryStream.WriteBuffer(Header,SizeOf(Header));
    oMemoryStream.CopyFrom(oFileStream, 0);
    oMemoryStream.Position := 0;
    Container.LoadFromStream(oMemoryStream);
  finally
    oMemoryStream.Free;
    oFileStream.Free;
  end;
end;
这里要注意的就是olecontainer要求的流结构比普通的流多一个Header,要加上这个Header才可以!
(2)加载以后,由于olecontianer本身并没有对word的相关操作,那么我们需要操作word,应该怎么办?一种办法就是使用Delphi中提供的TWordApplication,代码大概如下:
WordApplication.Connect或者
WordApplication.ConnectTo(IDispatch(OleContainer.OleObject.Application) as _Application)
我开始的时候就是用这个,但是后来在一台机器上出现问题了,这台机器以前装过word2000,现在装的word2003,跑这个程序就有问题了,总是在上面那行代码出错'Interface not Supported',我是想了很多办法,都没有搞定,比如可能是word版本的问题,于是就是word重新卸载,把Office相关的注册表全部删除,重新安装Office,还是没有用;又把Delphi重新安装,Delphi安装的时候要选择Office版本,我是XP和2000的都交叉的试过了,还是不行;然后又上网狂搜索,可惜网上针对我这个问题的信息不多。最后终于想到一个办法,那就是根本不用WordApplication,这样就没有版本问题了,我就直接用OleContainer.OleObject.Application(Variant类型),虽然这个是Variant类型,但是它支持的编程接口还是跟WordApplication一样的,唯一的问题就是写程序没有提示,麻烦一点,但是如果对于word接口比较熟悉的话,对于word简单的操作还是没有问题的,毕竟你还可以参照WordApplication提供的接口来写程序.当然还有一点要注意,在加载文件之后,应该调用OleContainer.Run,这个可以启动Word服务器,然后才能访问OleContainer.OleObject.Application
(3)另外一个问题就是加载之后,一定要把当前的这个WordDocument激活,这样才不会报错(有的机器只要你加载之后,就已经是激活了,但是有的机器又可能不是,所以要手动强制激活),代码如下:
//FApplication := OleContainer.OleObject.Application
procedure SetDocActivate(AIndex: Integer);
var
  nTempIndex: olevariant;
  iDoc: Variant;
begin
  nTempIndex := AIndex;
  iDoc := FApplication.Documents.Item(nTempIndex);

  if not VarIsNull(iDoc) then
    iDoc.Activate;
end;

(4)打开word,转换成PDF:这个功能首先要打开word,由于我希望打开一个word到内存,但是又不显示出来,怎么做呢?我开始想到的就是用TwordApplication去Open这个word,然后把它的Visible置为False,这样用户是看不到的,但实际上已经到内存中,然后PrintOut就可以。但是正是因为有上述(2)中出现的版本问题,同样这个地方也会报错,所以只能放弃!但是还有什么方法吗?真是苦思冥想啊,后来终于发现既然(2)中我可能用OleContainer加载,然后用OleObject的Application进行操作,那么我这里也可以的啊,但是一个很严重的问题来了,OleContainer是一个界面的组件啊,我这里只需要把文件载入内存,然后执行虚拟打印的动作,所以这里用OleContainer肯定不行,于是一个大胆的想法产生:看OleContainer的源代码,把界面相关的实现去掉,只需要剥离出其中加载文件的代码就可以了,于是自己整到夜里2点多,终于写出来了,代码如下:
(*$HPPEMIT '#include <DocObj.h>'*)
unit OleClasses;

{$T-,H+,X+}
{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  SysUtils, Windows, ActiveX, Classes, ComObj;

type
  TOleLoader = class
  private
    FOleObject: IOleObject;
    FStorage: IStorage;
    FLockBytes: ILockBytes;

    function GetOleObject: variant;

    procedure DestroyObject;
    procedure CheckObject;
  public
    constructor Create;
    destructor Destroy; override;

    procedure LoadFromStream(Stream: TStream);
    procedure Run;

    property OleObject: variant read GetOleObject;
    property OleObjectInterface: IOleObject read FOleObject;
  end;

implementation

resourcestring
  SEmptyOleObject = 'Operation not allowed on an empty OLE Object';
  SInvalidStreamFormat = 'Invalid stream format';
 
const
  StreamSignature = $434F4442; {'BDOC'}
//Copy From OleCtnrs
type
  TStreamHeader = record
    case Integer of
      0: ( { New }
        Signature: Integer;
        DrawAspect: Integer;
        DataSize: Integer);
      1: ( { Old }
        PartRect: TSmallRect);
  end;


{ TOleLoader }

procedure TOleLoader.CheckObject;
begin
  if FOleObject = nil then
    raise EOleError.CreateRes(@SEmptyOleObject);
end;

constructor TOleLoader.Create;
begin

end;

destructor TOleLoader.Destroy;
begin
  DestroyObject;
  inherited;
end;

procedure TOleLoader.DestroyObject;
begin
  if FOleObject <> nil then
    FOleObject.Close(OLECLOSE_NOSAVE);
  FOleObject := nil;
  FStorage := nil;
  FLockBytes := nil;
end;

function TOleLoader.GetOleObject: variant;
begin
  CheckObject;
  Result := Variant(FOleObject as IDispatch);
end;

procedure TOleLoader.LoadFromStream(Stream: TStream);
var
  DataHandle: HGlobal;
  Buffer: Pointer;
  Header: TStreamHeader;
begin
  DestroyObject;
  Stream.ReadBuffer(Header, SizeOf(Header));
  if (Header.Signature <> StreamSignature) then
    raise EOleError.CreateRes(@SInvalidStreamFormat);
  DataHandle := GlobalAlloc(GMEM_MOVEABLE, Header.DataSize);
  if DataHandle = 0 then OutOfMemoryError;
  try
    Buffer := GlobalLock(DataHandle);
    try
      Stream.Read(Buffer^, Header.DataSize);
    finally
      GlobalUnlock(DataHandle);
    end;
    OleCheck(CreateILockBytesOnHGlobal(DataHandle, True, FLockBytes));
    DataHandle := 0;
    OleCheck(StgOpenStorageOnILockBytes(FLockBytes, nil, STGM_READWRITE or
      STGM_SHARE_EXCLUSIVE, nil, 0, FStorage));
    OleCheck(OleLoad(FStorage, IOleObject, nil, FOleObject));
  except
    if DataHandle <> 0 then GlobalFree(DataHandle);
    DestroyObject;
    raise;
  end;
end;

procedure TOleLoader.Run;
begin
  CheckObject;
  OleCheck(OleRun(FOleObject));
end;

initialization
  OleInitialize(nil);

finalization
  OleUninitialize;
end.
马上调用,一跑,居然通过了,一个错误都没有,并且PDF转换也成功,并且比以前快了好多,真是说不出来的兴奋啊。

后记:这次整这个东西,收获还是很多的,尤其是在多次失望与绝望,崩溃中,最后每次都寻找到了解决问题的办法,每次都能收获一个激动,最重要的是迎难而上,坚韧不拔的精神让自己体味深刻!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值