Question

J'essaie de comprendre comment puis-je copier DispatcherObject (dans mon cas BitmapSource) dans un autre thread.

Exemple d'utilisation:
J'ai une application WPF qui doit Étalage dans un nouveau thread (l'application est en fait d'Outlook Addin et nous devons le faire, car Outlook a quelques crochets dans le thread principal de l'interface utilisateur et est en train de voler certains raccourcis clavier que nous devons utiliser - « perdu en traduction » dans Interop d'Outlook, WPF (que nous utilisons pour l'interface utilisateur), et Winforms (nous avons besoin d'utiliser certains contrôles WinForms fournis par Microsoft)).

Avec cela, j'ai ma mise en œuvre de WPFMessageBox, qui est configuré en définissant des propriétés statiques - et l'un d'eux est BitmapSource pour l'icône. Il est utilisé de telle sorte que dans le démarrage, je peux définir WPFMessageBox.Icon une fois, et depuis lors, chaque WPFMessageBox aura la même icône.

Le problème est que BitmapSource, qui est attribué en icône, est un DispatcherObject, et lors de la lecture, il lancera InvalidOperationException: «Le thread appelant ne peut pas accéder à cet objet parce qu'un autre thread est le propriétaire ».

Comment puis-je clone qui BitmapSource dans le fil réel? Il a des méthodes clone () et CloneCurrentValue (), qui ne fonctionnent pas (ils jettent la même exception aussi bien). Il a également survenu à me utiliser originalIcon.Dispatcher.Invoke (faire le clonage ici) - mais est nulle, Dispatcher BitmapSource et encore - je créer une copie sur un mauvais fil et utilise encore couldnt sur le mien. BitmapSource.IsFrozen de vrai.

Toute idée sur la façon de copier BitmapSource dans différents fils (sans complètement reconstruire à partir d'un fichier image dans un nouveau thread)?

EDIT: Donc, le gel ne pas d'aide: En fin de compte, j'ai un BitmapFrame (Window.Icon ne prend aucun autre type de ImageSource de toute façon), et quand je lui confient comme Window.Icon sur un thread différent, même si elles sont congelées, je obtenir InvalidOperationException: « Le thread appelant ne peut pas accéder à cet objet parce qu'un autre thread est propriétaire. » avec une trace de pile suivant:

    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#
Était-ce utile?

La solution

La clé est de créer le bitmap sur le fil que vous souhaitez utiliser. Donc, vous ne pouvez pas mettre en cache votre icône dans un champ / propriété statique, il charge bourgeon (à partir du fichier, des ressources, un ruisseau ou autre) à chaque fois que vous ouvrez une nouvelle fenêtre sur le nouveau fil.

BitmapFrame peut être utilisé sur le fil, il a été créé uniquement.

Même le clonage ne fonctionne pas ici, comme vous l'avez dit correctement (qui suce juste).

J'avais exactement le même problème et résolu juste en chargeant icône chaque fois que, dans mon cas particulier simplement en appelant

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

Et voici comment vous pouvez obtenir votre icône de ressource dans WPF:

var streamResourceInfo = Application.GetResourceStream(new Uri(@"pack://application:,,,/YourAssembly;relative path to the icon", UriKind.RelativeOrAbsolute));
// use streamResourceInfo.Stream 

Autres conseils

Une fois que vous appelez Freeze, il devrait fonctionner sur plusieurs threads.

bitmapSourceForOtherThread = new WriteableBitmap(previousBitmapSource);

Cela vient à un prix, mais il est assez comparer pas cher à sérialisation.

longue réponse .

Une solution qui fonctionne, mais pas très performant, est la création d'un flux de mémoire à partir des données d'image, reconstruire ensuite l'image sur le fil que vous souhaitez utiliser sur.

Exemple de 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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top