Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception when running in multi-threaded environment #35

Closed
dusansmail opened this issue Sep 21, 2017 · 11 comments
Closed

Exception when running in multi-threaded environment #35

dusansmail opened this issue Sep 21, 2017 · 11 comments

Comments

@dusansmail
Copy link

Try to open window with gif image in new thread, first time it works.
Try to open other window with gif image in new thread, fails miserably:

The calling thread cannot access this object because a different thread owns it.

at System.Windows.Threading.Dispatcher.VerifyAccess()
at System.Windows.Media.Imaging.BitmapDecoder.ToString()
at System.Windows.Media.Imaging.BitmapFrameDecode.ConvertToString(String format, IFormatProvider provider)
at System.Windows.Media.ImageSource.ToString()
at WpfAnimatedGif.AnimationCache.CacheKey.GetUri(ImageSource image)
at WpfAnimatedGif.AnimationCache.CacheKey.ImageEquals(ImageSource x, ImageSource y)
at WpfAnimatedGif.AnimationCache.CacheKey.Equals(CacheKey other)
at WpfAnimatedGif.AnimationCache.CacheKey.Equals(Object obj)
at System.Collections.Generic.ObjectEqualityComparer1.Equals(T x, T y) at System.Collections.Generic.Dictionary2.FindEntry(TKey key)
at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
at WpfAnimatedGif.ImageBehavior.GetAnimation(Image imageControl, BitmapSource source)
at WpfAnimatedGif.ImageBehavior.InitAnimationOrImage(Image imageControl)
at WpfAnimatedGif.ImageBehavior.ImageControlLoaded(Object sender, RoutedEventArgs e)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
at System.Windows.BroadcastEventHelper.BroadcastEvent(DependencyObject root, RoutedEvent routedEvent)
at System.Windows.BroadcastEventHelper.BroadcastLoadedEvent(Object root)
at MS.Internal.LoadedOrUnloadedOperation.DoWork()
at System.Windows.Media.MediaContext.FireLoadedPendingCallbacks()
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.Resize(ICompositionTarget resizedCompositionTarget)
at System.Windows.Interop.HwndTarget.OnResize()
at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.HwndSubclass.DefWndProcWrapper(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)

@thomaslevesque
Copy link
Member

Well, I wouldn't expect anything different. Why are you opening a window in a new thread? That's a pretty unusual thing to do... In any case, it's not a scenario I intend to support.

If you really need to do that, you can try to Freeze() the image before you assign it to AnimatedSource, but I can't guarantee it will work.

@igharrac
Copy link

igharrac commented Sep 29, 2017

Using WpfAnimatedGif within devexpress control also fails with the message:

<DataTemplate x:Key="waitIndicator">
            <dx:WaitIndicator DeferedVisibility="True">
<Image Height="61" Stretch="None" Source="pack://application:,,,/Loader-Blue.gif" />
</dx:WaitIndicator>
</DataTemplate>
        <dxwui:NavigationFrame>
            </dxmvvm:Interaction.Triggers>
                <dxmvvm:Interaction.Behaviors>
                    <dxwuin:FrameNavigationService />
                    <dxwuin:FrameDocumentUIService ShowSplashScreen="True">
                        <dxwuin:FrameDocumentUIService.SplashScreenService>
                            <dx:DXSplashScreenService x:Name="WaitScreenService" ViewTemplate="{StaticResource waitIndicator}" SplashScreenStartupLocation="CenterOwner" />
                        </dxwuin:FrameDocumentUIService.SplashScreenService>
                        <dxwuin:FrameDocumentUIService.PageAdornerControlStyle>
                            <Style TargetType="dxwui:PageAdornerControl">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="dxwui:PageAdornerControl">
                                            <ContentPresenter />
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </dxwuin:FrameDocumentUIService.PageAdornerControlStyle>
                    </dxwuin:FrameDocumentUIService>
                </dxmvvm:Interaction.Behaviors>
            </dxwui:NavigationFrame>
09:11:45,402 e ap 032 UnhandledException: System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
   at System.Windows.Threading.Dispatcher.VerifyAccess()
   at System.Windows.Media.Imaging.BitmapDecoder.ToString()
   at System.Windows.Media.Imaging.BitmapFrameDecode.ConvertToString(String format, IFormatProvider provider)
   at System.Windows.Media.ImageSource.ToString()
   at WpfAnimatedGif.AnimationCache.CacheKey.GetUri(ImageSource image)
   at WpfAnimatedGif.AnimationCache.CacheKey.ImageEquals(ImageSource x, ImageSource y)
   at WpfAnimatedGif.AnimationCache.CacheKey.Equals(CacheKey other)
   at WpfAnimatedGif.AnimationCache.CacheKey.Equals(Object obj)
   at System.Collections.Generic.ObjectEqualityComparer`1.Equals(T x, T y)
   at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
   at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
   at WpfAnimatedGif.ImageBehavior.GetAnimation(Image imageControl, BitmapSource source)
   at WpfAnimatedGif.ImageBehavior.InitAnimationOrImage(Image imageControl)
   at WpfAnimatedGif.ImageBehavior.ImageControlLoaded(Object sender, RoutedEventArgs e)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
   at System.Windows.BroadcastEventHelper.BroadcastEvent(DependencyObject root, RoutedEvent routedEvent)
   at System.Windows.BroadcastEventHelper.BroadcastLoadedEvent(Object root)
   at MS.Internal.LoadedOrUnloadedOperation.DoWork()
   at System.Windows.Media.MediaContext.FireLoadedPendingCallbacks()
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.Resize(ICompositionTarget resizedCompositionTarget)
   at System.Windows.Interop.HwndTarget.OnResize()
   at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.HwndSubclass.DefWndProcWrapper(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)

@thomaslevesque
Copy link
Member

Not sure what DevExpress is doing here, but I suspect DXSplashScreenService is showing a window on a separate thread. As I said, this is not a supported scenario, but you might be able to make it work by freezing the image before using it.

@thomaslevesque
Copy link
Member

Closing due to inactivity

@lsim
Copy link

lsim commented Apr 15, 2019

I too am affected by this. It seems not that unusual a thing to do - beyond the hobby project scope.

@thomaslevesque
Copy link
Member

Hi @lsim,

It's not a problem with the library, it's due to how WPF works. If an DependencyObject object is created on one thread, you can't manipulate it with another thread. There's nothing I can do about it.

Did you try what I suggested to @dusansmail and @igharrac? i.e. freezing the image? They never said if it worked for them.

@lsim
Copy link

lsim commented Apr 15, 2019

That part of our code isn't my specific area of expertise - so I can't say yet how we might work around the issue. My main source of bafflement is that even when window and controls are all created on the same STA thread (not the Main thread), it still exhibits this behavior. I'll study it some more - I'm probably overlooking something.

I'll look into if and when we create a DependencyObject - when I figure out what that is :)

.. and try to remember to report back here with my findings.

Thanks for a super speedy reply btw

@lsim
Copy link

lsim commented Apr 15, 2019

It works fine outside the main thread so long as all our gif-showing windows are run on the same STA thread. My concern was that even this wouldn't be enough.

Despite our efforts to clean up after each such window is closed, something relating to the gif must remain, which gives rise to the thread related exception when the next window initializes. Until now, we have spun up a new STA thread for each such window.

I think we can probably make this work in our project.

@thomaslevesque
Copy link
Member

@lsim this might be because animations are cached (so that if you have the same GIF images shown multiple times, the animation isn't reconstructed every time). Each animation is removed from the cache when the last instance of the GIF is cleared. Maybe try to clear the AnimatedSource before closing the window?

@lsim
Copy link

lsim commented Apr 15, 2019

Unfortunately, that experiment is probably out the scope of my current situation now that I have things working again. I would have to rewire the xaml bindings in a semi complicated setup.

If that cache is the root of the problem, maybe it could be a candidate for a ThreadLocal kind of thing? You would then take a perf hit if you use multiple threads, but things would work.

@thomaslevesque
Copy link
Member

maybe it could be a candidate for a ThreadLocal kind of thing?

If it's indeed the cause of the problem, yes, that would probably solve it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants