如何将dispatcherObject(bitmapsource)复制到不同的线程中?
-
02-10-2019 - |
题
我试图弄清楚如何将dispatcherObject(在我的情况下bitmapsource)复制到另一个线程中。
用例:
我有一个WPF应用需要在新线程中显示窗口(该应用实际上是Outlook Addin,我们需要这样做,因为Outlook在主UI线程中有一些挂钩,并且正在窃取我们需要使用的某些热键 - '在transpation'在Outlook Interop中,WPF(我们用于UI)和Winforms(我们需要使用某些Microsoft提供的Winforms控件)。
因此,我实现了WPFMessageBox的实现,它是通过设置一些静态属性来配置的 - 并且其中一个是ICON的bitmapsource。它可以使用以便在启动中我可以设置wpfmessagebox.icon一次,从那以后,每个wpfmessagebox都将具有相同的图标。
问题在于,分配给图标的BitMapsource是一个dispatcherObject,当读取时,它将抛出InvalidoperationException:“该调用线程无法访问此对象,因为不同的线程拥有它。”。
我如何克隆该位图像库中的实际线程?它具有clone()和clonecurrentValue()方法,它们不起作用(它们也引发了相同的例外)。我也想使用primateicon.dispatcher.invoke(在此处进行克隆) - 但是Bitmapsource的调度程序是无效的,仍然 - 我仍然会在错误的线程上创建一个副本,并且仍然无法在我的线上使用它。 bitmapsource.isfrozen == true。
关于如何将bitmapsource复制到不同线程中的任何想法(不完全从新线程中的图像文件重构)吗?
编辑:因此,冻结无济获取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#
解决方案
关键是 创造 您要使用的线程上的位图。因此,每次打开新线程上的新窗口时,您都不能在某些静态字段/属性中缓存图标(从文件,资源,流或其他)加载。
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
其他提示
一旦您打电话 Freeze
, ,它应该在多个线程上工作。
一个确实有效的解决方法,尽管不是很有性能,但正在从图像数据中创建一个内存流,然后重建要使用的线程上的图像。
示例 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;
}