My friends, my life, my style - James S.F. Hsieh

1/08/2010

免費的 "IStream, IPropertyStorage" system-provided, stand-alone implementation COM



相信寫過 .NET C# 的人對於 System.IO.MemoryStream 這樣的物件都感到十分方便, 因為MemoryStream 會在需要的時候自己 ReAlloc 來改變大小, 並且由於 MemoryStream 繼承自 Stream 這個 class 所以用途十分廣泛. 其實 COM 也有相似的設計 (不如說 .NET Framework 學 COM 的) COM 有定義一個基本的 interface IStream, 概念上 IStream 就如同 .NET 中的 Stream 一樣, 不過它是 interface 而不是 abstract class, 在 COM 的世界裡 interface 是種 protocol, 所以任何人都可以實做 IStream 的 COM, 並且跟認得 IStream 的其它 COM 合作. 我之前就有寫過一個實做 IStream 的 COM, 它支援 non-buffering 的方式來 OpenFile. MSDN 就有一篇文章來教大家怎麼時做一個 FileStream - Default IStream Implementation.


COM 的定位並不是個 Framework 所以沒有那麼多現成的 class 可以使用, 而且系統提供的 COM implement 也少的可憐. 但是 OLE32.DLL 還是提供了類似 MemoryStream 這類好用的東西, 我們可以使用 CreateStreamOnHGlobal 來建立一個實做 IStream 的 COM instance, 而這個 instance 的功能就如同 MemoryStream 一樣會自動 ReAlloc.  當然 OLE32 也允許你從 IStream 拿到 HGlobal 的 HANDLE, 我們只要呼叫 GetHGlobalFromStream

HGLOBAL handle = NULL;
CComPtr spStream;
HRESULT hr = ::CreateStreamOnHGlobal(handle , FALSE, &spStream);
hr = ::GetHGlobalFromStream(spStream, handle);


一但有了這個類似 MemoryStream 的 IStream 實做體, 我們還可以利用更多 OLE32 提供的功能, 例如 IPropertyStorage, IPropertyStorage 這個 interface 的功能是個用來存放 property 的 container, property 可以是 PROPVARIANT 所支援的任何 type, WIC 就是用 PROPVARIANT 來記錄存放在 JPEG EXIF 中的 metadata 詳細可以參考 IWICEnumMetadataItem (說遠了), 為什麼要介紹 IPropertyStorage 呢, 因為 OLE32 又提供了一個 system-provided, stand-alone implementation 的實做體, 我們只要提供 IStream 或是 IStorage 給傳給 StgCreatePropStg 這個 API, 它就會送一個 IPropertyStorage 的 instance 給我們, 我想 property bag 這種東西的方變程度就不用說了. 有注意到嗎? 我們只要結合 CreateStreamOnHGlobal 和 StgCreatePropStg, 它就可以幫我們建立一個在記憶體中的 property bag, 這是何等的方便與容易呢?


延伸思考一下 IStream 能不能在 .NET 中使用呢? 當然可以啊因為 IStream 是 COM 呢, 連 .NET Framework 都有對應的 Interop interface System.Runtime.InteropServices.ComTypes.IStream 而 IPropertyStorage 則對應於 MS.Internal.IO.Packaging.CompoundFile.UnsafeNativeIPropertyStorage, 不過似乎沒有公開 ...
(後來查了一下 MSDN 發現 Microsoft.VisualStudio.OLE.Interop 有公開對應大部分 OLE32 的介面, 不過放在一個很怪的 DLL 中 microsoft.visualstudio.ole.interop.dll 而 IStream 與 Stream 的轉換則有 DataStreamFromComStream 與 ComStreamFromDataStream 兩個 wrapper class 可以使用, 不過都包在 System.Windows.Forms.UnsafeNativeMethods 這個 internal class 中, 微軟真的很小氣 = =. 不過 UnsafeNativeMethods 真的是大個誇張啊, 大家可以用 .NET Reflector 看一下就可以知道了. 


順帶一提 System.IO.UnmanagedMemoryStream 似乎是個不錯用的 class, 它可以 wrapper 一個 unmanaged 的 pointer 程為一個 Stream class, 雖然它是 unsafe 的 class 但是對於 performance 也許有不少幫助呢.