您的位置:首页 > 大数据 > 人工智能

从OleContainer学到的东西

2007-11-21 13:31 417 查看
从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转换也成功,并且比以前快了好多,真是说不出来的兴奋啊。

后记:这次整这个东西,收获还是很多的,尤其是在多次失望与绝望,崩溃中,最后每次都寻找到了解决问题的办法,每次都能收获一个激动,最重要的是迎难而上,坚韧不拔的精神让自己体味深刻!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: