2

JPG画像にメタデータがないものを設定しようとしています。この場合、インプレースライター(InPlaceBitmapMetadataWriter)を使用することはできません。これは、画像にメタデータを配置する場所がないためです。

FileStreamを出力として使用すると、すべてが正常に機能します。しかし、MemoryStreamを出力として使用しようとすると、JpegBitmapEncoder.Save()が例外をスローします(HRESULTからの例外:0xC0000005)。調査の結果、メタデータの代わりにnullを指定した場合に、どのエンコーダーが画像をメモリストリームに保存できるかもわかりました。

問題を再現する非常に単純化された短い例を作成しました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Media.Imaging;

namespace JpegSaveTest
{
    class Program
    {
        public static JpegBitmapEncoder SetUpMetadataOnStream(Stream src, string title)
        {
            uint padding = 2048;
            BitmapDecoder original;
            BitmapFrame framecopy, newframe;
            BitmapMetadata metadata;
            JpegBitmapEncoder output = new JpegBitmapEncoder();
            src.Seek(0, SeekOrigin.Begin);
            original = JpegBitmapDecoder.Create(src, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
            if (original.Frames[0] != null) {
                framecopy = (BitmapFrame)original.Frames[0].Clone();
                if (original.Frames[0].Metadata != null) metadata = original.Frames[0].Metadata.Clone() as BitmapMetadata;
                else metadata = new BitmapMetadata("jpeg");
                metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", padding);
                metadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", padding);
                metadata.SetQuery("/xmp/PaddingSchema:Padding", padding);
                metadata.SetQuery("System.Title", title);
                newframe = BitmapFrame.Create(framecopy, framecopy.Thumbnail, metadata, original.Frames[0].ColorContexts);
                output.Frames.Add(newframe);
            }
            else {
                Exception ex = new Exception("Image contains no frames.");
                throw ex;
            }
            return output;
        }

        public static MemoryStream SetTagsInMemory(string sfname, string title)
        {
            Stream src, dst;
            JpegBitmapEncoder output;
            src = File.Open(sfname, FileMode.Open, FileAccess.Read, FileShare.Read);
            output = SetUpMetadataOnStream(src, title);
            dst = new MemoryStream();
            output.Save(dst);
            src.Close();
            return (MemoryStream)dst;
        }

        static void Main(string[] args)
        {
            string filename = "Z:\\dotnet\\gnom4.jpg";
            MemoryStream s;
            s = SetTagsInMemory(filename, "test title");
        }
    }
}

シンプルなコンソールアプリケーションです。これを実行するには、filename変数contentをメタデータのない任意の.jpgファイルへのパスに置き換えます(またはmineを使用します)。

多くの場合、最初に一時ファイルに画像を保存し、それを閉じてから、開いてMemoryStreamにコピーすることができますが、汚れがひどく、回避策が遅くなります。これを機能させるためのアイデアは大歓迎です:)

4

2 に答える 2

2

誰かが同じ問題に遭遇した場合の解決策は次のとおりです。

メインアプリケーションスレッドから.Save()jpegを実行しようとする場合は、Main()の前に[STAThread]を追加してください。

そうでない場合は、JpegBitmapEncoder.Save()を呼び出すスレッドに対して.SetApartmentState(ApartmentState.STA)を呼び出します。

Windowscodecs.dllのWinXPおよびWinVistaバージョンは再入力できないため、JpegBitmapEncoder.Save()関数を呼び出すスレッドにデフォルトのMTAモデル(.NET Framework 2.0以降のデフォルト)を使用すると、奇妙な動作をして、記述された例外をスローする可能性があります。Windowscodecs.dllのWin7バージョンにはこの問題はありません。

于 2010-04-24T22:58:58.393 に答える
1

コードを変更せずに実行しましたが、エラーは発生しませんでした。変更したデータをディスクに保存しようとしても、イメージ自体は破損していませんでした。

string filename = "e:\\a.jpg";
        MemoryStream s;
        s = SetTagsInMemory(filename, "test title");
        FileStream fs = new FileStream("e:\\b.jpg", FileMode.CreateNew, FileAccess.ReadWrite);
        BinaryWriter sw = new BinaryWriter(fs);
        s.Seek(0, SeekOrigin.Begin);
        while (s.Position < s.Length)
       {
            byte[] data = new byte[4096];
            s.Read(data, 0, data.Length);
            sw.Write(data);
       }

        sw.Flush();
        sw.Close();
        fs.Close();

ディスクに書き込むためにs=SetTagsInMemory(...)の下に追加したものを除いて、残りのコードは変更されていません。

編集:ああ、メタデータは間違いなく新しいファイルになりました。前のファイルには、私が見ることができるものからのメタデータがありませんでした。

于 2010-04-24T17:47:21.533 に答える