none
ファイルの属性の変更について RRS feed

  • 質問

  • よく分からない事があり、質問させていただきます。

    エクスプローラ-で、jpgファイルを右クリックした場合に、プロパティが表示され、その「詳細」タブの中に☆で「評価」を表示・変更できる部分があります。

    C#のプログラムで、☆の数を取得する事はできたのですが、プログラムから変更する方法が分かりません。

    取得は、Shell32を利用して、

    FileInfo fi = new FileInfo(fname);
    string fpath = fi.DirectoryName;
    string myname = fi.Name;
    Shell32.Shell shell = new Shell32.Shell();
    Shell32.Folder objFolder;

    objFolder = shell.NameSpace(fpath);
             if (objFolder != null)
                    {
                        Shell32.FolderItem objFolderItem = objFolder.ParseName(myname);
                        if (objFolderItem != null)
                        {
                            //項目の値を表示 19が「評価」
                            textBox1.Text = (objFolder.GetDetailsOf(objFolderItem, 19)).ToString()
                            objFolderItem = null;
                        }
                        objFolder = null;
                    }

    ですが、Shell32では、読み取りしかできません。

    「評価」をC#で書き込むにはどうすれば良いのでしょう。

    お知恵をお借りいただければ幸いです。

    2015年1月19日 8:45

すべての返信

  • こんにちは。

    BitmapMetadataで出来ると思います。

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        const string SAMPLE_FILE = @"C:\hoge.jpg";
        const string OUTPUT_FILE = @"C:\hoge2.jpg";
        const string QUERY_RATING1 = "System.SimpleRating";
        const string QUERY_RATING2 = "/app1/ifd/{ushort=18246}";
        const string QUERY_RATING3 = "/xmp/xmp:Rating";
    
        int updateRating = 4;
    
        bool doOverwrite = false;
        using (var fs = new FileStream(SAMPLE_FILE, FileMode.Open, FileAccess.ReadWrite))
        {
            var decode = JpegBitmapDecoder.Create(fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
            var frame = decode.Frames[0];
            var writer = frame.CreateInPlaceBitmapMetadataWriter();
            writer.SetQuery(QUERY_RATING1, updateRating);
            writer.SetQuery(QUERY_RATING2, updateRating);
            writer.SetQuery(QUERY_RATING3, updateRating.ToString());
            doOverwrite = !writer.TrySave();
        }
    
        //失敗時はメタデータ毎入れ替え
        if(doOverwrite)
        {
            using (var fs = new FileStream(SAMPLE_FILE, FileMode.Open, FileAccess.ReadWrite))
            {
                var decode = JpegBitmapDecoder.Create(fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
                var frame = decode.Frames[0].Clone() as BitmapFrame;
                var meta = frame.Metadata.Clone() as BitmapMetadata;
                meta.SetQuery(QUERY_RATING1, updateRating);
                meta.SetQuery(QUERY_RATING2, updateRating);
                meta.SetQuery(QUERY_RATING3, updateRating.ToString());
                var encoder = new JpegBitmapEncoder();
                var newFrame = BitmapFrame.Create(frame, frame.Thumbnail, meta, frame.ColorContexts);
                newFrame.Freeze();
                encoder.Frames.Add(newFrame);
                using (var outStream = new FileStream(OUTPUT_FILE, FileMode.Create, FileAccess.Write))
                {
                    encoder.Save(outStream);
                }
            }
        }
    }

    2015年1月19日 10:46
    モデレータ
  • 念のために、読み手の方への補足として投稿します。

    JpegBitmapDecoder など、登場するクラスの MSDN を見てもらえればわかると思いますが、PresentationCore.dll に含まれているクラスです。
    Tak1wa さんが書かれたコードを使う際、プロジェクトによっては参照設定(PresentationCore, WindowsBase, System.Xaml)を追加していただく、using namespace System.Windows.Media.Imaging; を先頭に書いていただくなどが必要になるかもしれません。

    2015年1月19日 13:02
    モデレータ
  • 早速のご返答、ありがとうございます。

    あまり良く理解できて居ない部分がありますので、間違っていたらすみません。

    私がShell32を使って見に行っている場所(ファイルの属性)と、Tak1wa様が提示されたコードで書き込まれている場所(Exif情報)は異なるように思うのですが、いかがでしょう?

    例えばEOSなどのデジカメで、レーティングを書き込んでも「18246」のExif情報に保存されていないようです。しかし、エクスプローラーで見ると、「評価」として表示されます。

    エクスプローラー上で、「評価」を変更すると、「18246」のExif情報が作成され、そこにもレーティング情報が書き込まれますが、エクスプローラーの「評価」はExif情報を表示しているわけではないようなのです。

    2015年1月20日 2:34
  • あまり詳しくないですが、ちらっと調べた限りでは、レーティングはAdobeが制定したXMPと呼ばれるメタデータに格納されているようです。Tak1waさんが掲載されたソースコードにも出てきますが、XMPを調べられると良いように思います。

    (例)
    C# XMP Toolkit
    https://forums.adobe.com/thread/290917?start=0&tstart=0

    #上記のToolkitへのリンクは切れていますが、検索すると出てきます。試す場合は自己責任でお願いします。

    #以下にもXMPの例がありますので、以下の方法でも大丈夫かもしれません。

    ExifをC#から編集する
    http://8thway.blogspot.jp/2014/05/exif-edit.html


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2015年1月20日 3:20
    モデレータ
  • プロパティの詳細が編集可能になったのはWindows Vistaからだと思います。もちろんそれを扱うインターフェースは公開されていますので適切に呼び出せば、参照だけでなく編集を行うことはできます。Initializing Property Handlers辺りが参考になるでしょうか。

    ただし、質問者さんの期待する「C#で書き込む」のは大変な困難を伴います。もちろん手順を示すことはできますが、回答にあたってそこまでの気力はないため、C++での記述を載せます。

    #include <comdef.h>
    #include <Windows.h>
    #include <propkey.h>
    #include <propsys.h>
    #include <Propvarutil.h>
    #include <ShlObj.h>
    #pragma comment(lib, "propsys.lib")
    
    _COM_SMARTPTR_TYPEDEF(IPropertyStore, __uuidof(IPropertyStore));
    
    struct ComInitializer {
    	ComInitializer() {
    		CoInitialize(nullptr);
    	}
    	~ComInitializer() {
    		CoUninitialize();
    	}
    };
    
    int _tmain() {
    	ComInitializer com;
    	PIDLIST_ABSOLUTE pidl;
    	auto hr = SHParseDisplayName(L"C:\\Users\\sayuri\\Desktop\\Capture.JPG", nullptr, &pidl, 0, nullptr);
    	IPropertyStorePtr propertyStore;
    	hr = SHGetPropertyStoreFromIDList(pidl, GPS_READWRITE, IID_PPV_ARGS(&propertyStore));
    	PROPVARIANT propRating;
    	hr = propertyStore->GetValue(PKEY_Rating, &propRating);
    	if (propRating.vt == VT_EMPTY) {
    		printf("rating: empty\n");
    	} else {
    		ULONG rating;
    		hr = PropVariantToUInt32(propRating, &rating);
    		printf("rating: %d\n", rating);
    	}
    	hr = InitPropVariantFromUInt32(50, &propRating);
    	hr = propertyStore->SetValue(PKEY_Rating, propRating);
    	hr = propertyStore->Commit();
    	return 0;
    }
    ちなみにC#に移植する際、PROPVARIANTの扱いが面倒くさいです。


    • 回答の候補に設定 星 睦美 2015年1月26日 8:02
    2015年1月20日 5:08
  • いろいろとお教えいただき、ありがとうございます。

    今、AdobeのXMPまわりをごちゃごちゃやっていますが、なかなかうまくいっていない状況です。

    エクスプローラーで簡単に触れる「評価」も、変更はけっこう大変なのですね。

    2015年1月22日 4:11