プロパティ ストレージに関する考慮事項
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 メソッド、STGM フラグを使用) の呼び出しによって指定された分離レベルによって異なります。
プロパティ セットを開く呼び出しで読み取り/書き込みアクセスが指定されている場合、 IStorage プロパティと IStream 値プロパティは常に読み取り/書き込みアクセスで開かれます。 その後、これらのインターフェイスを介してデータを書き込み、 プロパティの値を変更できます。これは、これらのプロパティを更新する最も効率的な方法です。 プロパティ値自体には追加のレベルのトランザクション入れ子がないため、変更の範囲はプロパティ ストレージ オブジェクトのトランザクション (存在する場合) です。
ストレージとストリームのプロパティ
ストリームまたはストレージ オブジェクトをプロパティ セットに書き込むには、プロパティ セットが非シンプルとして作成されている必要があります。 単純なプロパティ セットと単純でないプロパティ セットの詳細については、 プロパティ セットの 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 を呼び出すことで、このオブジェクトにアクセスできます。 このオブジェクトの更新が完了したら、プロパティ セットに直接更新が行われたため、プロパティ セットに書き込む必要はありません。
プロパティを介して開かれたストリームまたはストレージは常に直接モードで開かれるため、入れ子になったトランザクションの追加レベルは導入されません。 プロパティ セット全体にトランザクションが存在する可能性があります。 (たとえば、iPropertySetStorage::Open を呼び出して、grfmode パラメーターに設定されたSTGM_TRANSACTED フラグを使用して IPropertyStorage を取得した場合など)。さらに、プロパティ ベースのストリームまたはストレージは、可能であれば、プロパティ セットのモードを指定して読み取り/書き込みモードで開かれます。それ以外の場合は、読み取りモードが使用されます。
前述のように、ストリームまたはストレージ オブジェクトが 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未満である必要があります。 上記の「解説」セクションを参照してください。
関連トピック