プロパティ ストレージに関する考慮事項
IPropertyStorage::ReadMultiple は、rgpspec 配列で指定されたプロパティの数を、プロパティ セットに含まれている数で読み取ります。 要求されたプロパティのいずれかが読み取られた限り、存在しないプロパティを取得する要求はエラーではありません。 代わりに、そのプロパティのVT_EMPTYが戻り値の rgvar[] 配列に書き込まれる必要があります。 要求されたプロパティが存在しない場合、メソッドはS_FALSEを返し、各 PROPVARIANTにVT_EMPTYを設定する必要があります。 他のエラーが返された場合、プロパティ値は取得されず、呼び出し元はそれらを解放することを心配する必要はありません。
rgpspec パラメーターは、PROPSPEC構造体配列であり、プロパティ識別子または割り当てられている場合は文字列識別子を各プロパティに指定します。 IPropertyStorage::WritePropertyNames呼び出すことによって、文字列をプロパティ識別子にマップできます。 ただし、プロパティ識別子の使用は、文字列の使用よりも大幅に効率的である可能性があります。
文字列名 (PRSPEC_LPWSTR) によって要求されるプロパティは、現在のプロパティ セット (および現在のシステム ロケールに従って) で指定されているため、大文字と小文字を区別せずにプロパティ識別子 (ID) にマップされます。
プロパティ型がVT_LPSTRされ、プロパティが ANSI プロパティ セットから読み取られた場合、つまり、プロパティ セットのコード ページが Unicode 以外に設定されている場合、プロパティの値はプロパティ セットと同じコード ページを使用します。 VT_LPSTR プロパティが Unicode プロパティ セットから読み取られる場合、プロパティの値は、システムの現在の既定の ANSI コード ページ、つまり、GetACP 関数から返されるコード ページを使用します。
PROPVARIANTは、ストリームとストレージへのポインターを除き、単純な PROPVARIANTと呼ばれます。 これらの単純な PROPVARIANTは値によってデータを受信するため、IPropertyStorage::ReadMultiple呼び出し元が所有するデータのコピーが提供されます。 これらのプロパティを作成または更新するには、IPropertyStorage::WriteMultiple呼び出します。
これに対し、バリアント型VT_STREAM、VT_STREAMED_OBJECT、VT_STORAGE、およびVT_STORED_OBJECTは、値を指定するのではなく、指定されたインターフェイスへのポインターを取得し、そこからデータを読み取ることができるため、単純ではないプロパティです。 これらの型では、1 つのプロパティを使用して大量の情報を格納できます。 非シンプル プロパティの使用には、いくつかの問題が発生します。
これらのプロパティを作成するには、他のプロパティと同様に、IPropertyStorage::WriteMultiple呼び出します。 ただし、同じメソッドを呼び出して更新するのではなく、最初に IPropertyStorage::ReadMultiple呼び出してストリームまたはストレージへのインターフェイス ポインターを取得してから、IStream または IStorage メソッドを使用してデータを書き込む方が効率的です。 プロパティを介して開かれたストリームまたはストレージは常に直接モードで開かれるため、入れ子になったトランザクションの追加レベルは導入されません。 ただし、IPropertySetStorageを使用して開いたり作成したりする方法によっては、プロパティ セット全体に対するトランザクションが存在する可能性があります。 さらに、プロパティ セットを開くか作成するときに指定されたアクセス および共有モード のタグは、プロパティ ベースのストリームまたはストレージに渡されます。
プロパティ ベースのストリームまたはストレージ ポインターの有効期間は、理論的には、関連付けられた IPropertyStorage および IPropertySetStorageポインターとは無関係ですが、実際には、実質的にそれらに依存します。 ストリームまたはストレージを介して表示されるデータは、包含ストリームおよびストレージ サブオブジェクトを持つストレージ オブジェクト (IStorageをサポートする) の場合と同様に、取得元のプロパティ ストレージ オブジェクトのトランザクションに関連します。 親オブジェクトのトランザクションが中止されると、既存の IStream とそのオブジェクトの下位にある IStorage ポインター アクセスできなくなります。 IPropertyStorage はプロパティ ストレージ オブジェクト上の唯一のインターフェイスであるため、IStream および IStorage ポインター の有効な有効期間は、IPropertyStorage インターフェイスの有効期間によって制限されます。
実装では、同じ IPropertyStorage インターフェイス インスタンスを介して同じストリーム値プロパティまたはストレージ値プロパティが複数回要求される状況にも対処する必要があります。 たとえば、COM 複合ファイルの実装では、プロパティが既に開いているかどうかに応じて、オープンは成功または失敗します。
もう 1 つの問題は、トランザクション モードで複数開くことです。 結果は、プロパティ ストレージが開かれた時点で、IPropertySetStorageメソッド呼び出し (Open または Create メソッドの呼び出しによって指定された分離レベルによって異なります。
プロパティ セットを開く呼び出しで読み取り/書き込みアクセスが指定されている場合、IStorage と IStream-valued プロパティは常に読み取り/書き込みアクセス権で開かれます。 その後、これらのインターフェイスを介してデータを書き込み、プロパティの値を変更できます。これは、これらのプロパティを更新する最も効率的な方法です。 プロパティ値自体には追加レベルのトランザクション入れ子がないため、変更のスコープはプロパティ ストレージ オブジェクトのトランザクション (存在する場合) になります。
Storage プロパティと Stream プロパティ
ストリームまたはストレージ オブジェクトをプロパティ セットに書き込むには、プロパティ セットが非シンプルとして作成されている必要があります。 単純なプロパティ セットと単純でないプロパティ セットの詳細については、「プロパティ セット のストレージ オブジェクトとストリーム オブジェクト」セクションを参照してください。 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 が、grfmode パラメーターに設定された STGM_TRANSACTED フラグ IPropertySetStorage::Open を呼び出すことによって取得された場合)。さらに、プロパティ ベースのストリームまたはストレージは、可能であれば、プロパティ セットのモードを指定すると、読み取り/書き込みモードで開かれます。それ以外の場合は、読み取りモードが使用されます。
前述のように、ストリームまたはストレージ オブジェクトが、WriteMultiple メソッドを使用してプロパティ セットに書き込まれると、オブジェクトのコピーが作成されます。 このようなコピーがストリーム オブジェクトに対して行われると、コピー操作はソースの現在のシーク位置から開始されます。 シーク位置は失敗時には未定義ですが、成功した場合はストリームの最後にあります。シーク ポインターは元の位置に復元されません。
ReadMultipleを持つプロパティ セットからストリームまたはストレージ プロパティ読み取られた後も開いたままで、同じプロパティに対して WriteMultipleをする後続の呼び出しが行われた場合、WriteMultiple 操作は成功します。 以前に開いたストリームまたはストレージ プロパティは、元に戻された状態になります (すべての呼び出しでエラー STG_E_REVERTED返されます)。
WriteMultiple メソッドが、プロパティの配列または個々の単純でないプロパティを書き込むときにエラーを返す場合、実際に書き込まれるデータの量は未定義です。
参照プロパティ
指定した PROPVARIANT 構造体の vt メンバーにVT_BYREF フラグが含まれている場合、関連付けられたプロパティは参照プロパティです。 参照プロパティは、プロパティ セットに値を書き込む前に自動的に逆参照されます。 たとえば、PROPVARIANT 構造体の vt メンバーが型 VT_BYREF |VT_I4、書き込まれた実際の値はVT_I4型です。 IPropertyStorage::ReadMultiple メソッドの後続の呼び出しでは、VT_I4として値が返されます。 参照プロパティの使用は、VariantCopyInd 関数の呼び出しに似ています。 VariantCopyInd は、変換先バリアントを解放し、ソース VARIANTARG のコピーを作成し、ソースがVT_BYREFするように指定されている場合に必要な間接参照を実行します。 この関数は、バリアントのコピーが必要な場合や、IDispatch::Invokeの実装で引数を処理する場合など、VT_BYREFされないことを保証するために役立ちます。
発信者へのメモ
IPropertySetStorage::Createの grfFlags パラメーターにPROPSETFLAG_ANSI フラグ設定しないことで、プロパティ セットを Unicode として作成することをお勧めします。 また、VT_LPSTR値の使用は避け、代わりにVT_LPWSTR値を使用することをお勧めします。 プロパティ セットのコード ページが Unicode の場合、VT_LPSTR文字列値は格納時に Unicode に変換され、取得するとマルチバイト文字列値に戻ります。 プロパティ セットのコード ページが Unicode でない場合、プロパティ名、VT_BSTR文字列、および単純でないプロパティ値は、格納時にマルチバイト文字列に変換され、現在のシステム ANSI コード ページを使用して取得されると Unicode に変換されます。
実装者への注意事項
プロパティ識別子を割り当てるときに、実装は、プロパティ識別子のプロパティ セットで現在使用されていない任意の値を選択できます。0 または 1 以上でない限り、その値はすべて予約値0x80000000。 propidNameFirst パラメーターは、セット内のプロパティ識別子の最小値を確立し、1 より大きく、0x80000000未満である必要があります。 上記の「解説」セクションを参照してください。
関連トピック