IPropertyStorage::ReadMultiple 讀取在屬性集中找到的 rgpspec 陣列中指定的許多屬性。 只要讀取所要求的任何屬性,擷取不存在之屬性的要求就不是錯誤。 相反地,這必須將該屬性的VT_EMPTY寫入傳回 rgvar[] 陣列。 當要求的屬性不存在時,方法應該傳回S_FALSE,並在每個 PROPVARIANT中設定VT_EMPTY。 如果傳回任何其他錯誤,則不會擷取屬性值,而呼叫端不需要擔心釋放它們。
rgpspec 參數是一個 PROPSPEC 結構的陣列,它會針對每個屬性指定其屬性標識符,或者,如果已指派,則為字元串標識符。 您可以呼叫 IPropertyStorage::WritePropertyNames,將字串對應至屬性標識符。 不過,屬性標識碼的使用可能比使用字串更有效率。
字串名稱 (PRSPEC_LPWSTR) 要求的屬性會不區分大小寫地對應至屬性識別元 (標識符),因為它們是在目前屬性集中指定的 (以及根據目前的系統地區設定)。
當屬性類型VT_LPSTR且屬性是從 ANSI 屬性集讀取時,屬性集的代碼頁會設定為 Unicode 以外的值,屬性的值會使用與屬性集相同的代碼頁。 從 Unicode 屬性集讀取VT_LPSTR屬性時,屬性的值會使用系統目前的預設 ANSI 代碼頁,也就是從 getACP 函式傳回 代碼頁。
除了數據流和記憶體指標以外的 PROPVARIANT,稱為簡單 PROPVARIANT。 這些簡單的 PROPVARIANT會依值接收數據,因此呼叫 IPropertyStorage::ReadMultiple 提供呼叫端接著擁有的數據複本。 若要建立或更新這些屬性,請呼叫 IPropertyStorage::WriteMultiple。
相反地,變數類型VT_STREAM、VT_STREAMED_OBJECT、VT_STORAGE和VT_STORED_OBJECT都是非簡單的屬性,因為方法不會提供值,而是會擷取指示介面的指標,然後從中讀取數據。 這些類型允許透過單一屬性儲存大量的資訊。 使用非簡單屬性時發生數個問題。
若要建立這些屬性,請呼叫 IPropertyStorage::WriteMultiple。 不過,與其呼叫相同的方法來更新,最好先呼叫 IPropertyStorage::ReadMultiple 取得數據流或記憶體的介面指標,然後使用 IStream 或 IStorage 方法寫入數據。 透過屬性開啟的數據流或記憶體一律會以直接模式開啟,因此不會導入額外的巢狀交易層級。 不過,根據透過 IPropertySetStorage開啟或建立該屬性的方式,可能仍會有整個屬性上的交易。 此外,在開啟或建立屬性集時所指定的存取和共用模式卷標會傳遞至以屬性為基礎的數據流或記憶體。
屬性型數據流或儲存指標的存留期,雖然理論上與其相關聯的 IPropertyStorage 和 IPropertySetStorage 指標無關,但實際上實際上取決於它們。 透過數據流或記憶體可見的數據,與從中擷取其所擷取之屬性儲存物件上的交易有關,就如同包含數據流和儲存子對象的記憶體物件(支援 IStorage)。 如果父物件上的交易已中止,則無法再存取該對象的現有 IStream 和 IStorage 指標。 由於 IPropertyStorage 是屬性儲存物件上唯一的介面,因此包含 IStream 的實用存留期,且 IStorage 指標會系結於 IPropertyStorage 介面的存留期。
實作也必須處理透過相同 IPropertyStorage 介面實例多次要求相同數據流或記憶體值屬性的情況。 例如,在 COM 複合檔案實作中,開啟將會成功或失敗,視屬性是否已開啟而定。
另一個問題是在交易模式中多次開啟。 結果取決於透過呼叫 IPropertySetStorage 所指定的隔離等級 方法(Open 或 Create 方法,透過 STGM 旗標開啟時)。
如果開啟屬性集的呼叫指定讀寫許可權,IStorage 和 IStream值屬性一律會以讀寫許可權開啟。 然後,數據可以透過這些介面寫入,變更 屬性的值,這是更新這些屬性的最有效率的方式。 屬性值本身沒有額外的交易巢狀層級,因此變更的範圍在屬性儲存物件上的交易下(如果有的話)。
記憶體和串流屬性
若要將數據流或儲存物件寫入屬性集,屬性集必須建立為非簡單。 如需簡單和非簡單屬性集的詳細資訊,請參閱屬性集 標題為Storage and Stream Objects 一節。 下列屬性類型,如 rgvar 陣列元素的 vt 字段中所指定,是數據流或儲存類型:VT_STREAM、VT_STORAGE、VT_STREAMED_OBJECT、VT_STORED_OBJECT。
若要將資料流或記憶體物件寫入為非簡單屬性集中的屬性,請呼叫 IPropertyStorage::WriteMultiple。 雖然您也會呼叫此方法來更新簡單屬性,但它並不是更新屬性集中數據流和儲存物件的有效率方式。 這是因為透過呼叫來更新其中一個屬性,WriteMultiple 會在屬性記憶體物件中建立傳入數據的複本,而且 IStorage 或 IStream 指標不會在此呼叫期間之後保留。 最好先呼叫 IPropertyStorage::ReadMultiple,以取得數據流或記憶體的介面指標,然後 透過 IStream 或 IStorage 方法,直接更新數據流或儲存物件,以更有效率的方式更新數據流或儲存物件。
例如,您可以呼叫 IPropertyStorage::WriteMultiple 來寫入 NULL 數據流或記憶體物件。 然後,實作會在屬性集中建立空白物件。 接著,您可以呼叫 IPropertyStorage::ReadMultiple來存取此物件。 當您完成更新此物件時,您不需要將它寫入屬性集,因為您的更新會直接進入屬性集。
透過屬性開啟的數據流或記憶體一律會以直接模式開啟,因此不會導入額外的巢狀交易層級。 整個屬性上可能仍有交易。 (例如,如果 IPropertyStorage 是透過呼叫 IPropertySetStorage::Open,並在 grfmode 參數中設定STGM_TRANSACTED旗標來取得。此外,如果可能的話,如果屬性集上的模式,則會在讀寫模式中開啟屬性型數據流或記憶體;否則會使用讀取模式。
如先前所述,將數據流或儲存物件寫入使用 WriteMultiple 方法設定的屬性時,就會建立對象的複本。 在數據流物件上建立這類複本時,複製作業會從來源的目前搜尋位置開始。 搜尋位置在失敗時未定義,但在成功時則位於數據流結尾;搜尋指標不會還原至其原始位置。
如果已從具有 ReadMultiple的屬性集讀取數據流或儲存屬性,仍會保持開啟狀態,且後續對相同屬性 WriteMultiple 的呼叫將會成功,WriteMultiple 作業將會成功。 先前開啟的數據流或儲存屬性會處於還原狀態(所有對它的呼叫都會傳回STG_E_REVERTED錯誤)。
如果 WriteMultiple 方法在寫入屬性陣列,甚至個別的非簡單屬性時傳回錯誤,則實際寫入的數據量是未定義的。
參考屬性
如果指定的 PROPVARIANT 結構在其 vt 成員中包含VT_BYREF旗標,相關聯的屬性就是參考屬性。 參考屬性會在將值寫入屬性集之前自動取值。 例如,如果 vt 成員 PROPVARIANT 結構指定類型為 VT_BYREF |VT_I4,寫入的實際值是VT_I4類型。 IPropertyStorage::ReadMultiple 方法的後續呼叫會傳回值做為VT_I4。 使用參考屬性類似於呼叫 VariantCopyInd 函式。 VariantCopyInd 釋放目的地變體並製作來源 VARIANTARG 的複本,並在 VT_BYREF指定來源時執行必要的間接存取。 此函式在需要變體複本時很有用,並保證它不會VT_BYREF,例如,在 IDispatch::Invoke 實 作中處理自變數時::Invoke。
呼叫端的附註
建議將屬性集建立為 Unicode,方法是不要在 IPropertySetStorage::Create的 grfFlags 參數中設定 PROPSETFLAG_ANSI 旗標。 也建議您避免使用VT_LPSTR值,並改用VT_LPWSTR值。 當屬性集代碼頁為 Unicode 時,VT_LPSTR字串值會在儲存時轉換成 Unicode,並在擷取時轉換為多位元組字串值。 當屬性集的代碼頁不是 Unicode 時,屬性名稱、VT_BSTR字串和非簡單屬性值會在儲存時轉換成多位元組字串,並在擷取時轉換成 Unicode,全部都使用目前的系統 ANSI 代碼頁。
實作者的注意事項
配置屬性標識碼時,實作可以選擇屬性標識碼的屬性集目前未使用的任何值,只要不是 0 或 1 或大於 0x80000000,這些值全都是保留值。 propidNameFirst 參數會為集合內的屬性標識元建立最小值,而且必須大於 1 且小於 0x80000000。 請參閱上面的一節。
相關主題