5

DispatcherObject (私の場合は BitmapSource) を別のスレッドにコピーする方法を見つけようとしています。

ユース ケース:
新しいスレッドでウィンドウを表示する必要がある WPF アプリがあります (アプリは実際には Outlook アドインであり、Outlook のメイン UI スレッドにいくつかのフックがあり、使用する必要がある特定のホットキーを盗んでいるため、これを行う必要があります。 - Outlook、WPF (UI に使用)、および Winforms (Microsoft が提供する特定の winforms コントロールを使用する必要があります) の相互運用における「翻訳で失われた」。

これで、いくつかの静的プロパティを設定することで構成された WPFMessageBox の実装ができました。そのうちの 1 つはアイコンの BitmapSource です。これは、起動時に WPFMessageBox.Icon を一度設定できるようにするために使用され、それ以降、すべての WPFMessageBox が同じアイコンを持つようになります。

問題は、icon に割り当てられている BitmapSource が DispatcherObject であり、読み取り時に InvalidOperationException をスローすることです: "別のスレッドが所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません。".

その BitmapSource を実際のスレッドに複製するにはどうすればよいですか? Clone() および CloneCurrentValue() メソッドがありますが、機能しません (同じ例外がスローされます)。originalIcon.Dispatcher.Invoke( ここでクローンを作成する ) を使用することも思いつきましたが、BitmapSource の Dispatcher は null であり、それでも - 間違ったスレッドでコピーを作成し、それを自分では使用できませんでした。BitmapSource.IsFrozen == true。

BitmapSource を別のスレッドにコピーする方法についてのアイデアはありますか (新しいスレッドのイメージ ファイルから完全に再構築せずに)。

編集: したがって、フリーズは役に立ちません:最終的には BitmapFrame (Window.Icon は他の種類の ImageSource を取りません) を持っています。 、私は InvalidOperationException を取得します:「別のスレッドが所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません。」次のスタック トレースを使用します。

    WindowsBase.dll!System.Windows.Threading.Dispatcher.VerifyAccess() + 0x4a bytes 
    WindowsBase.dll!System.Windows.Threading.DispatcherObject.VerifyAccess() + 0xc bytes    
    PresentationCore.dll!System.Windows.Media.Imaging.BitmapDecoder.Frames.get() + 0xe bytes    
    PresentationFramework.dll!MS.Internal.AppModel.IconHelper.GetIconHandlesFromBitmapFrame(object callingObj = {WPFControls.WPFMBox.WpfMessageBoxWindow: header}, System.Windows.Media.Imaging.BitmapFrame bf = {System.Windows.Media.Imaging.BitmapFrameDecode}, ref MS.Win32.NativeMethods.IconHandle largeIconHandle = {MS.Win32.NativeMethods.IconHandle}, ref MS.Win32.NativeMethods.IconHandle smallIconHandle = {MS.Win32.NativeMethods.IconHandle}) + 0x3b bytes   
>   PresentationFramework.dll!System.Windows.Window.UpdateIcon() + 0x118 bytes  
    PresentationFramework.dll!System.Windows.Window.SetupInitialState(double requestedTop = NaN, double requestedLeft = NaN, double requestedWidth = 560.0, double requestedHeight = NaN) + 0x8a bytes  
    PresentationFramework.dll!System.Windows.Window.CreateSourceWindowImpl() + 0x19b bytes  
    PresentationFramework.dll!System.Windows.Window.SafeCreateWindow() + 0x29 bytes 
    PresentationFramework.dll!System.Windows.Window.ShowHelper(object booleanBox) + 0x81 bytes  
    PresentationFramework.dll!System.Windows.Window.Show() + 0x48 bytes 
    PresentationFramework.dll!System.Windows.Window.ShowDialog() + 0x29f bytes  
    WPFControls.dll!WPFControls.WPFMBox.WpfMessageBox.ShowDialog(System.Windows.Window owner = {WPFControlsTest.MainWindow}) Line 185 + 0x10 bytes  C#
4

4 に答える 4

7

を呼び出すとFreeze、複数のスレッドで動作するはずです。

于 2010-05-03T15:20:56.323 に答える
4

bitmapSourceForOtherThread = new WriteableBitmap(previousBitmapSource);

これには代償が伴いますが、シリアル化に比べるとかなり安価です。

長い答え

于 2011-12-14T18:32:44.547 に答える
3

重要なのは、使用するスレッドでビットマップを作成することです。したがって、新しいスレッドで新しいウィンドウを開くたびに、アイコンを静的フィールド/プロパティにキャッシュしたり、(ファイル、リソース、ストリームなどから) バッドロードしたりすることはできません。

BitmapFrame は、それが作成されたスレッドでのみ使用できます。

あなたが正しく述べたように、ここではクローン作成さえ機能しません(これはひどいです)。

私はまったく同じ問題を抱えていて、毎回アイコンをロードするだけで解決しました。

// get your stream somewhere - 
window.Icon = BitmapFrame.Create(stream)

これは、WPF のリソースからアイコンを取得する方法です。

var streamResourceInfo = Application.GetResourceStream(new Uri(@"pack://application:,,,/YourAssembly;relative path to the icon", UriKind.RelativeOrAbsolute));
// use streamResourceInfo.Stream 
于 2010-12-30T02:40:51.217 に答える
2

パフォーマンスは高くありませんが、機能する回避策の 1 つは、イメージ データからメモリ ストリームを作成し、それを使用するスレッドでイメージを再構築することです。

BitmapSource:

Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate()
{
    //serialize image on UI thread
    imageStream = GetImageBytes(cameraImage);
}

...
//reconstruct image on a different thread:
Bitmap bitmap = new Bitmap(imageStream); 

private MemoryStream GetImageBytes(BitmapSource image)
{
    MemoryStream ms = new MemoryStream();
    BitmapEncoder encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(image));
    encoder.Save(ms);
    ms.Seek(0, SeekOrigin.Begin);
    return ms;
}
于 2010-12-30T02:49:50.487 に答える