Managing PropertiesProperty Storage Considerations

プロパティストレージの考察

IPropertyStorage.ReadMultiple() は、プロパティセットから、rgpspec 配列で指定された量のプロパティを発見し読み込みます。 要求されたプロパティが読み込まれている間に、存在していないプロパティの検索を要求することはエラーではありません。 その代りに、この関数が戻る際、このプロパティに対する rgvar[] 配列に VT_EMPTY が書き込まれます。 要求されたプロパティが存在しない場合、このメソッドは S_FALSE を返さなければならず、 PROPVARIANTVT_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_STREAMVT_STREAMED_OBJECTVT_STORAGEVT_STORED_OBJECT のバリアント型は非シンプルプロパティなので、値を提供するのではなく、メソッドが読み取ることができるデータから、指定されたインタフェースへのポインタを検索します。 これらの型は、シングルプロパティを通じて大容量ストレージを許可します。 非シンプルプロパティを使用する場合には、いくつかの問題点が発生します。

ほかのプロパティのように、これらのプロパティを作成するには、IPropertyStorage.WriteMultiple() を呼び出します。 更新するためには同じメソッドを呼び出すのではなく、効率的に行うためには、まず、ストリームまたはストレージへのインタフェースポインタを取得するために IPropertyStorage.ReadMultiple() を呼び出し、その後 IStream または IStorage のメソッドを使用してデータを書き込みます。 ストリームまたはストレージは、常にダイレクトモードで開かれたプロパティを通して開かれるため、入れ子のトランザクションになった追加の層は導入されません。 しかしながら、まだ全体としてのプロパティセットでのトランザクション処理があるかどうかは、IPropertySetStorage を通してどのように開かれたかまたは作成されたかに依存します。 さらに、このアクセスと共有モードタグは、プロパティセットが開かれるまたは作成される際に指定され、プロパティ基底のストリームまたはストレージに渡されます。

プロパティ基底のストリームまたはストレージポインタのライフタイムは、理論上、IPropertyStorageIPropertySetStorage ポインタとは無関係ですが、実際には、これらに依存します。 ストリームまたはストレージを通して有効となるデータは、ストリームやストレージのサブオブジェクトと共に ( IStorage でサポートされる) ストレージオブジェクトとして検索されることで、プロパティストレージオブジェクトのトランザクションへ伝達されます。 親オブジェクトでのトランザクションが中断された場合、オブジェクトに従属する現存の IStreamIStorage ポインタは、即座にアクセスできなくなります。 なぜなら、IPropertyStorage は、プロパティストレージオブジェクトのインタフェースのみなので、IStorageIStream ポインタの使用可能なライフタイムは、IPropertyStorage インタフェースのライフタイムと関連付けられているからです。

この実装では、同一のストリームまたはストレージ値のプロパティが、同一の IPropertyStorage インタフェースインスタンスに複数回要求されたシチュエーションでも分配されます。 たとえば、COM 複合ファイル実装では、オープンが成功するか失敗するかは、このプロパティがすでに開かれているか否かに依存します。

トランザクションモードのマルチオープンには別の問題があります。 この結果は、プロパティストレージが開かれたときに、IPropertySetStorage のメソッドでの呼び出しの際に指定されたの分離レベル ( Open() または Create() メソッドのどちらかで、渡される STGM フラグ) に依存します。

read-write アクセスを指定してプロパティセットが Open() で呼び出された場合、IStorageIStream 値のプロパティは、常に read-write アクセスで開かれます。 データはこれらのインタフェースを通して書き込みすることができ、プロパティの値を変更します。これは、プロパティを更新する最も効率的な方法です。 プロパティ値は、それ自身にはトランザクションの入れ子の追加層を持たないので、変更点(が存在する場合)は、プロパティストレージオブジェクト上のトランザクションとしてとらえられます。

ストレージとストリームのプロパティ

ストリームやストレージオブジェクトのプロパティセットに書き込むには、プロパティセットは非シンプルで生成されなければなりません。 シンプルと非シンプルプロパティセットの詳細については、Storage and Stream Objects for a Property Set というタイトルのセクションをを参照してください。 rgvar 配列要素の vt フィールドで指定されるストリームまたはストレージ型のプロパティの型は、以下の通りです。: vt_stream, vt_storage, vt_streamed_object, vt_stored_object

非シンプルプロパティセットのプロパティのストリームまたはストレージを書き込むには、IProprtyStorage.WriteMultiple() を呼び出します。 シンプルプロパティセットでこのメソッドを呼び出した場合には、プロパティセット上のストリームとストレージオブジェクトの更新には何も影響しません。 これは、これらのプロパティのうちの 1 つが、 WriteMultiple() の呼び出しによって、プロパティストレージオブジェクト内に渡されたデータを複製を作成し更新されるからです。IStorage または IStream のポインタは、この呼び出しの期間を超えては保持されません。 ストリームまたはストレージオブジェクトのインタフェースポインタを取得する目的で、まず IPropertyStorage.ReadMultiple() 呼び出し、その後 IStorage または IStream メソッドを通してデータを書き込むことによって直接更新することは、通常ではより効率的な方法です。

たとえば、ストリームまたはストレージオブジェクトを IPropertyStorage.WriteMultiple() の呼び出しで、NULL として書き込むことが可能です。 この実装ではプロパティセットに空のオブジェクトを作成します。 IPropertyStorage.ReadMultiple() を呼び出すことで、このオブジェクトのアクセス権を取得できます。 このオブジェクトの更新を完了させた際に、プロパティセットが直接更新されるため、プロパティセットへの書き込みは不要です。

プロパティを通して開かれたストリームまたはストレージは、常にダイレクトモードで開かれ、入れ子になった追加層のトランザクションは導入されません。 全体としてのプロパティセットには、トランザクションが存在する場合もあります。 (たとえば、IPropertySetStorage.Open()grfmode パラメータに STGM_TRANSACTED フラグを設定して呼び出した際に取得された IPropertyStorage の場合。) さらに、プロパティ基底のストリームまたはストレージは、read-write で開かれ、可能であればプロパティセットにそのモードが与えられ、そうでなければ read モードが使用されます。

前述のとおり、ストリームまたはストレージオブジェクトのプロパティセットへ WriteMultiple() メソッドで書き込む場合、オブジェクトの複製が作成されます。 そのようにストリームオブジェクト上に複製が作成された場合、この複製操作は複製元の現在のシーク位置から始まります。 このシーク位置は失敗すると不確定となりますが、成功した場合はストリームの末尾となります。このシークポインタは、オリジナルの位置に修復されません。

ストリームまたはストレージプロパティが、プロパティセットから ReadMultiple() で読みだされた場合、ストリームまたはストレージは開いたままにされ、サブシーケンスが 同じプロパティを作成するために WriteMultiple() から呼び出され、この操作は成功します。 以前に開かれたストリームまたはストレージのプロパティは、リバート状態として配置されます。 (すべての呼び出しは STG_E_REVERTED エラーが返されます)

WriteMultiple() メソッドが プロパティの配列または各々の非シンプルプロパティを書き込む際にエラーを返す場合、実際に書き込まれたデータサイズは不確定です。

プロパティの参照

PROPVARIANT 構造体の vt メンバに VT_BYREF フラグが含まれている場合、関連付けられたプロパティは参照プロパティです。 参照プロパティは、プロパティセットへ値が書き込まれる直前に自動的に非参照になります。 たとえば、PROPVARIANT 構造体の vt メンバに VT_BYREF | VT_I4 の値が指定された場合、実際の値としては VT_I4 型が書き込まれます。 IPropertyStorage.ReadMultiple() メソッドを呼び出した後は VT_I4 型の値が返されます。 参照プロパティを使用することは、VariantCopyInd() 関数を呼び出すことと同じです。 VariantCopyInd() は、転送先のバリアントを開放し、転送元が VT_BYREF を指定していて必要な場合に、転送元の VARIANTARG の複製を間接的に行います。 この関数は、バリアントの複製が必要とされており、VT_BYREF が指定されていないことが保障されている際に有用です。たとえば、IDispatch::Invoke の実装で引数をハンドリングするときなどです。

呼び出しに関する注意

プロパティセットは、IPropertySetStorage.Create()grfFlags パラメータに PROPSETFLAG_ANSI フラグを設定せずに、Unicode で作成されることが推奨されます。 また、VT_LPSTR 値の使用を避け、代わりに VT_LPWSTR 値を使用することが推奨されます。 プロパティセットのコードページが Unicode の場合、VT_LPSTR 文字列値は、格納時に Unicode に変換され、検索される際にはマルチバイト文字列値を返します。 プロパティセットのコードページが Unicode ではなかった場合、プロパティ名、VT_BSTR文字列、非シンプルプロパティ値は、格納時に現在のシステム ANSI コードページを使用してマルチバイト文字列に変換されて、検索される際に Unicode に変換されます。

実装に関する注意

プロパティ識別子を割り当てる際、この実装は、予約されている値である、010x80000000 以上の値を除く、現在使用していない値をプロパティセット識別子として選択することができます。 propidNameFirst パラメータは、このセット内でのプロパティ識別子値の最小値となり、1 より大きく 0x80000000より小さい値になります。Remarks セクションも参照してください。

関連するトピック