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代碼:
var
Stream: TMemoryStream;
Adapter: TStreamAdapter;
Image: TGpImage;
begin
Stream := TMemoryStream.Create;
Stream.LoadFromFile('Test.jpg');
Adapter := TStreamAdapter.Create(Stream, soOwned);
Image := TGpImage.Create(Adapter);
try
.....
finally
Image.Free;
end;
end;
代碼中,首先建立一個記憶體流,並從磁碟中裝入一個影像檔,然後用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一起被釋放掉了。
上面的代碼在Delphi7 調試狀態下會出現錯誤,在網上找了個C的代碼,現在改為Pascal的,在個人機子上調試穩定:
function LoadGPImageFormRes(aHMoudle: DWORD; ResName, ResType: string):
TGPImage;
var
HRes: HRSRC;
sResName: string;
sResType: string;
Len: DWORD;
lpRsrc: PByte;
m_hMem: HGLOBAL;
pMem: PByte;
pstm: IStream;
begin
Result := nil;
sResName := ResName;
sResType := ResType;
HRes := FindResource(HInstance, PChar(sResName), PChar(sResType));
if HRes <> 0 then
begin
Len := SizeofResource(HInstance, HRes);
lpRsrc := PByte(LoadResource(HInstance, HRes));
try
if lpRsrc <> nil then
begin
m_hMem := GlobalAlloc(GMEM_FIXED, Len);
pMem := Pbyte(GlobalLock(m_hMem));
CopyMemory(pMem, lpRsrc, Len);
CreateStreamOnHGlobal(m_hMem, False, pstm);
Result := TGPImage.Create(pstm);
GlobalUnlock(m_hMem);
pstm := nil;
FreeResource(Dword(lpRsrc));
end;
except
if Assigned(Result) then
begin
FreeAndNil(Result);
end;
end;
end;
end;