I am trying to figure out how can I copy DispatcherObject (in my case BitmapSource) into another thread.
Use case:
I have a WPF app that needs to show window in a new thread (the app is actually Outlook addin and we need to do this because Outlook has some hooks in the main UI thread and is stealing certain hotkeys that we need to use - 'lost in translation' in interop of Outlook, WPF (which we use for UI), and Winforms (we need to use certain microsoft-provided winforms controls)).
With that, I have my implementation of WPFMessageBox, that is configured by setting some static properties - and and one of them is BitmapSource for icon. This is used so that in startup I can set WPFMessageBox.Icon once, and since then, every WPFMessageBox will have the same icon.
The problem is that BitmapSource, which is assigned into icon, is a DispatcherObject, and when read, it will throw InvalidOperationException: "The calling thread cannot access this object because a different thread owns it.".
How can I clone that BitmapSource into the actual thread? It has Clone() and CloneCurrentValue() methods, which don't work (they throw the same exception as well). It also occured to me to use originalIcon.Dispatcher.Invoke( do the cloning here ) - but the BitmapSource's Dispatcher is null, and still - I'd create a copy on a wrong thread and still couldnt use it on mine. BitmapSource.IsFrozen == true.
Any idea on how to copy the BitmapSource into different thread (without completely reconstructing it from an image file in a new thread)?
EDIT: So, freezing does not help: In the end I have a BitmapFrame (Window.Icon doesn't take any other kind of ImageSource anyway), and when I assign it as a Window.Icon on a different thread, even if frozen, I get InvalidOperationException: "The calling thread cannot access this object because a different thread owns it." with a following stack trace:
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#
Once you call Freeze
, it should work on multiple threads.
bitmapSourceForOtherThread = new WriteableBitmap(previousBitmapSource);
This comes at a price, but it's pretty cheap comparing to serializing.
Long answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With