ファイルシステムオブジェクトのプロパティ値をコンソールへ出力していたら、あるオブジェクトの更新日時(System.DateModified)以降が出力されなくなりました。原因はIPropertySystem::FormatForDisplayAlloc
(PSFormatForDisplayAlloc
)関数の仕様「フラグ指定しなければ結果(文字列)にUnicode directional characters(\x200e
、\x200f
)を含む」でした。コンソールの文字コード設定も関係あるかもしれません。ユニコードモードに設定すると制御記号ごと表示できます(関連記事)。
解決策はIPropertySystem::FormatForDisplayAlloc
(PSFormatDisplayAlloc
)のフラグにPDFF_NOAUTOREADINGORDER
(PROPDESC_FORMAT_FLAGS::PDFF_NOAUTOREADINGORDER
)を加えることですか上記のユニコードモードの適用です。ただし、なお、公式ドキュメントによるとフラグを指定してもUnicode directional charactersが追加される可能性は残るそうです。
コンソール出力の途切れは以下のコードで再現できるかもしれません。
#include <io.h> #include <fcntl.h> #include <iostream> #include <string> #pragma comment(lib, "propsys.lib") #define STRICT #include <Windows.h> #include <propsys.h> #include <ShObjIdl.h> // WIL #include <wil/com.h> #include <wil/resource.h> using namespace wil; using namespace std; // PSGetPropertySystem関数のラッパー com_ptr<IPropertySystem> PSGetPropertySystem() { com_ptr<IPropertySystem> propSys; THROW_IF_FAILED(PSGetPropertySystem(IID_PPV_ARGS(&propSys))); return move(propSys); } // SHGetPropertyStoreFromParsingName関数のラッパー com_ptr<IPropertyStore> SHGetPropertyStoreFromParsingName( PCWSTR path, IBindCtx* pbc, GETPROPERTYSTOREFLAGS flags) { com_ptr<IPropertyStore> propStore; THROW_IF_FAILED(SHGetPropertyStoreFromParsingName( path, pbc, flags, IID_PPV_ARGS(&propStore))); return move(propStore); } unique_cotaskmem_string PSFormatPropertyValueFromName( IPropertyStore* pps, PCWSTR name, PROPDESC_FORMAT_FLAGS flags) { PROPERTYKEY key; THROW_IF_FAILED(PSGetPropertyKeyFromName(name, &key)); unique_prop_variant value; THROW_IF_FAILED(pps->GetValue(key, value.addressof())); unique_cotaskmem_string s; THROW_IF_FAILED(PSFormatForDisplayAlloc(key, value, flags, &s)); return std::move(s); } int main() { // ユニコードモードの設定 // アンコメントするとユニコード文字が制御記号ごと表示されます。 //_setmode(_fileno(stdout), _O_U16TEXT); // プロパティを取得するパス auto path = L"C:\\Windows"; // COMの初期化 auto couninit = wil::CoInitializeEx(COINIT_APARTMENTTHREADED); // プロパティストアの取得 auto propSystem = PSGetPropertySystem(); auto propStore = SHGetPropertyStoreFromParsingName( path, nullptr, GPS_DEFAULT); // System.DateCreatedプロパティを取得して // 確認のためにwstringへ変換する。 wstring dateStr = PSFormatPropertyValueFromName( propStore.get(), L"System.DateCreated", PDFF_DEFAULT).get(); std::wcout << dateStr << std::endl; // 出力:(なし)<既定> // 出力:・2019/・12/・07/ ・・18:03<ユニコードモード>(制御記号は中黒へ置換) // デバッグウィンドウ:"2019/12/07 18:03" // 生の値: // 8206 '' <- Unicode directional character // 50 '2' // 48 '0' // 49 '1' // 57 '9' // 47 '/' // 8206 '' <- Unicode directional character // 49 '1' // 50 '2' // 47 '/' // 8206 '' <- Unicode directional character // 48 '0' // 55 '7' // 32 ' ' // 8207 '' <- Unicode directional character // 8206 '' <- Unicode directional character // 49 '1' // 56 '8' // 58 ':' // 48 '0' // 51 '3' return 0; }
dateStr
変数(std::wstring
)をみると、ところどころに8206
(0x200E
)と8207
(0x200F
)が現れています。おそらくコンソールはここで表示が終了しています。0x200E
はLEFT-TO-RIGHT MARK、0x200F
はRIGHT-TO-LEFT MARKでどちらも制御コードです。