GDI+的Image及派生类中涉及到IStream流,在Delphi和C++Builder中广泛使用的TStream不能直接作为参数进行传递,VCL提供了一个TStreamAdapter类,用于把VCL流TStream转换为IStream。TStreamAdapter的构造过程原型如下:
constructor Create(Stream: TStream; Ownership: TStreamOwnership = soReference);
其中的TStreamOwnership枚举类型有2个值:soReference和,如果是soOwned,TStream对象由TStreamAdapter释放,否则由用户自己处理。请看下面的用流建立Image的Delphi代码:
代码中,首先建立一个内存流,并从磁盘中装入一个图像文件,然后用TStreamAdapter将这个流封装起来,转换为IStream,供建立TGpImage使用。注意,我在建立TStreamAdapter对象时使用了soOwned,这样,由Adapter负责释放Stream,否则,必须在Image.Free后面Stream.Free。但是TStreamAdapter对象Adapter为什么没有释放代码呢?因为TStreamAdapter是个接口实现类,作为IStream传递给TGpImage.Create后,由系统自动跟踪释放,如果我们将它Free,会引起错误,如果非要显式释放它,必须用var I: IStream; I := Adapter; I := nil;的方法释放,而且必须在Image.Free之前,因为在Image.Free的同时,Adapter也随着IStream一起被释放掉了。
C++Builder与Delphi享有同一个VCL,也可按照Delphi的方法使用TStream,但是,由于C++Builder使用的GDI+代码包含在Gdiplus名字空间中,所有函数均采用inline方式,不是真正的函数调用方式,按照Delphi的方式,用TStreamAdapter将TStream转换为IStream传递给Image的构造函数,将会出现运行时刻错误,因为如果Image的构造函数是真正的函数,在传递接口参数时,会对接口对象作隐式的引用调用,而inline函数一般只是直接将函数体内容插入调用函数的地方,没有真正地传递参数的过程,所以,我们必须显式的做一个引用调用。请看下面保存Image到流的C++代码片断:
代码中,显式的调用了TStreamAdapter的_AddRef()和_Release()函数,如果将这个调用省略,运行时会出现错误。
由于本人是业余爱好者,水平和功力有限,错误在所难免,欢迎来信指导:[email protected]
补充(2008.9.7):从本文举例中可以看到,在C++Builder的GDI+中 使用TStreamAdapter,必须显式的调用其_AddRef()和_Release()函数,否则会出错,而Delphi中使用TStreamAdapter,似乎不必显式调用_AddRef和_Release,程序就工作的很好,但是如果多次使用TStreamAdapter的同一个对象,同样也会出错,也需要显式调用_AddRef和_Release。而Delphi的TStreamAdapter的_AddRef和_Release不是public方法,只能转换为IStream后调用。请看下面的代码片断:
var
tmp: TGpBitmap;
Adapter: TStreamAdapter;
Stream: TStream;
Clsid: TGUID;
begin
Stream := TMemoryStream.Create;
Adapter := TStreamAdapter.Create(Stream, soOwned);
GetEncoderClsid('image/bmp', Clsid);
IStream(Adapter)._AddRef;
Image.Save(Adapter, Clsid);
tmp := TGpBitmap.Create(Adapter);
try
......
......
finally
tmp.Free;
IStream(Adapter)._Release;
end;
代码中的Image是函数中的一个TGpImage类型参数,代码先把Image保存到内存流,然后用这个流建立一个临时TGpBitmap对象,便于进行操作,操作完毕,释放临时对象。为了适应GDI+的IStream接口,代码中用TStreamAdapter进行了转换,如果不显式地调用_AddRef和_Release,这段代码根本就运行不下去。