We've seen a crash in our application when the RadMap control is disposed. We've only seen the crash once but have been unable to reproduce. The stack trace is as follows:
Telerik.Windows.Controls.DataVisualization : System.NullReferenceException
Object reference not set to an instance of an object.
at Telerik.Windows.Controls.Map.Location.PixelToLogical(RadMap mapControl, Point pixel, Boolean useTileLayer)
at Telerik.Windows.Controls.Map.Location.PixelToLogical(RadMap mapControl, Point pixel, Boolean useTileLayer)
at Telerik.Windows.Controls.Map.MapMouseLocationIndicator.MapControl_MouseMove(Object sender, MouseEventArgs e)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
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.RaiseTrustedEvent(RoutedEventArgs args)
at System.Windows.Input.InputManager.ProcessStagingArea()
at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at System.Windows.Interop.HwndSource.InputFilterMessage(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.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at CNL.IPSecurityCenter.WindowsClient.App.Main()
We're currently using version 2017.2.614.45. Looking at the Telerik code it seems like the mouse event handler for MapMouseLocationIndicator doesn't get detached when RadMap is disposed. Is it possible there's some race condition that allows a mouse event to be processed in MapMouseLocationIndicator after its RadMap property has been set to null (which does happen when RadMap is disposed)?
Thanks
Pete
19 Answers, 1 is accepted
No update on this post since I raised it on 12th April. Would I be better raising a support ticket?
Pete
Thank you for the provided stacktrace.
I went through our backlog, however I am afraid that we have not received reports of such an exception. With this in mind and without being able to reproduce it, it is difficult to pinpoint what could be the reason for the exception based on the stacktrace only.
May I ask you to elaborate on the following questions? Can you share some sample xaml/code of the application's structure and mainly how the RadMap is defined? May I also ask you to elaborate on when and how is the RadMap disposed? This way I will hopefully be able to reproduce the observed behavior on my end and investigate it.
Thank you in advance for any help you can provide.
Regards,
Vladimir Stoyanov
Progress Telerik
Hi Vladimir,
It's a little difficult to explain. The user can select different maps by clicking on another control in the application. Each map they view is configured with one or more background layers (providers) that they can select from. In addition the map has several "entity" layers allowing us to configure various objects on the map (visibility for each layer can configured by the user). Some of these entity layers have dynamically changing data being updated on a dispatcher timer (see our support ticket: ID:1411532) Because the layers we need for different map providers can differ greatly, when they switch between maps we tear-down the previous map (i.e. disposing the map) and configure a new map view from scratch.
The xaml for the map control contains the following:
<
telerik:RadMap
x:Name
=
"RadMap"
IsEnabled
=
"False"
MiniMapExpanderVisibility
=
"Collapsed"
Background
=
"{Binding MainGridBackgroundColor, Converter={StaticResource ColorToBrushConverter}}"
MouseDragMode
=
"Drag"
MouseSelectionMode
=
"RaiseEvent"
MouseDoubleClickMode
=
"None"
MouseClickMode
=
"None"
ZoomLevel
=
"{Binding MapZoomLevel}"
MinZoomLevel
=
"{Binding MinZoomLevel}"
MaxZoomLevel
=
"{Binding MaxZoomLevel}"
>
<
telerik:RadContextMenu.ContextMenu
>
<
telerik:RadContextMenu
x:Name
=
"MapRadContextMenu"
ItemsSource
=
"{Binding ContextMenuViewModel.Items, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
PlacementTarget
=
"{Binding ElementName=RadMap}"
ItemContainerStyle
=
"{StaticResource MenuItemContainer}"
/>
</
telerik:RadContextMenu.ContextMenu
>
</
telerik:RadMap
>
<
telerik:RadExpander
x:Name
=
"RadExpander"
Margin
=
"0"
Padding
=
"0"
HorizontalAlignment
=
"Right"
VerticalAlignment
=
"Top"
Style
=
"{DynamicResource RadExpanderStyle2}"
>
<
telerik:RadExpander.Header
>
<
StackPanel
Width
=
"224"
>
<
TextBlock
Height
=
"5"
/>
</
StackPanel
>
</
telerik:RadExpander.Header
>
<
Border
BorderThickness
=
"2"
BorderBrush
=
"#CCAAAAAA"
>
<
telerik:MiniMap
x:Name
=
"MiniMap"
MapControl
=
"{Binding ElementName=RadMap}"
Width
=
"256"
Height
=
"200"
Background
=
"{Binding MainGridBackgroundColor, Converter={StaticResource ColorToBrushConverter}}"
Padding
=
"0"
Margin
=
"0"
/>
</
Border
>
</
telerik:RadExpander
>
The providers and layers for the map are all configured in code after the map control has loaded.
When we switch between maps the map is disposed something like this:
public
void
Dispose(
bool
disposing)
{
if
(disposing)
{
_disposed =
true
;
_logger.Debug(
"TelerikMap: (RadMap) disposing"
);
Enable(
false
);
Loaded -= OnLoaded;
Drop -= OnDrop;
DragOver -= OnDragOver;
DataContext =
null
;
// dispose reactive extensions subscription
_mouseMoveSubscription?.Dispose();
_mouseMoveSubscription =
null
;
//Workaround for modern client crash when disposing telerik control (switching between locations)
if
(RadMap?.Items?.Count > 0)
{
foreach
(var radMapItem
in
RadMap.Items)
{
var visualizationLayer = radMapItem
as
VisualizationLayer;
visualizationLayer?.Items?.Clear();
}
}
DetachRadMapHandlers();
DetachViewModelHandlers(_viewModel);
DisposeMapLayers();
RadMap.ItemsSource =
null
;
RadMap.Dispose();
RadMap.Provider =
null
;
_viewModel =
null
;
MapRadContextMenu.DataContext =
null
;
_logger.Debug(
"TelerikMap: (RadMap) disposed"
);
_alreadyLoadedViewModelScene =
false
;
}
}
Our application is fairly complex and it's difficult for me to generate a simpler sample app showing the problem. Given the stack trace it does seem like the problem could be fixed if the code in MapMouseLocationIndicator.MapControl_MouseMove had some null check for the RadMap control.
Alternatively can you suggest a different sequence for our disposal code that might allow us to workaround this issue?
Thanks
Pete
Thank you for the additional information and the sample code.
I went over it, however I am uncertain whether there is actually a need to use the Dispose method the RadMap. My current understanding is that you are giving the user an option to choose from different "views", which requires changing the providers and/or layers, but you are using the same RadMap instance. If that is indeed the case, I believe that it is not necessary to call the Dispose method of the RadMap. Note, that its idea is to be called when the RadMap is unloaded in order to dispose the resources used by the map.
That said, in your scenario you can simply clear and repopulate the provider, layers and anything else that you want to change dynamically. Can you give this approach a try and let me know how it goes?
Regards,
Vladimir Stoyanov
Progress Telerik
Hi Vladimir,
Thanks for the update. The structure of our application is such that users can launch new windows containing a map so we will still need to dispose the control at some times. However when we are not launching a new window I will try changing the code to fully reconfigure the RadMap instead of disposing it. Unfortunately this might not be a straightforward change given the current application structure but I will investigate if this can be done.
Thanks
Pete
I tried that and it works in the scenario I described.
Thanks for your help
Pete
It is good to know that the suggestion was helpful for your scenario.
Don't hesitate to reach out to us again, if you have any other questions or concerns.
Regards,
Vladimir Stoyanov
Progress Telerik
Hi Vladimir,
Thanks for your help so far. I've done as much as I can to reduce the necessity to dispose the map control. As I said in a previous message, there are occasions when we still need to dispose the map. Unfortunately we've seen another crash, this time it's in the Timer_Tick handler of MouseControl:
Telerik.Windows.Controls.DataVisualization : System.NullReferenceException
Object reference not set to an instance of an object.
at Telerik.Windows.Controls.Map.MouseControl.Timer_Tick(Object sender, EventArgs e)
at System.Windows.Threading.DispatcherTimer.FireTick(Object unused)
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.DispatcherOperation.InvokeImpl()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(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.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at CNL.IPSecurityCenter.WindowsClient.App.Main()
As far as I can work out by examining your code this can happen if RadMap is disposed, which sets MouseControl.MapControl to null in RadMap.Dispose. This leaves the possibility of the timer event firing and causing the NullReferenceException above. Because this exception is not handled, and it's on the UI thread, this can crash our application.
I can't see any way to workaround this issue.
Thanks
Pete
Hello Pete,
Thank you for the provided stacktrace.
I am afraid that I cannot suggest a workaround for this exception, since the Dispose method of the RadMap is supposed to clear all the resources utilized by the control. This method is intended to be invoked when the RadMap instance will not be needed anymore.
That is why my suggestion was to avoid calling the Dispose method when the RadMap instance will be in use. Alternatively, you can also create a new instance of the RadMap and configure it to your needs.
Regards,
Vladimir Stoyanov
Progress Telerik
Hi Vladimir,
I have done all I can to avoid disposing the RadMap but there are circumstances when (in common with any disposable .NET object) it's essential to be able to dispose the object without the application crashing. It seems to me that this is a clear bug in the Telerik map code. Causing a null reference exception in a timer thread is simply incorrect code.
My company has an ongoing license for the Telerik WPF Controls. Should I get one of our licensee holders to raise this as a bug?
Thanks
Pete
Hello Pete,
I discussed the scenario with our developers and we think that you are correct that the Dispose method should not lead to an exception. However we believe that an instance of RadMap (or any other class), which is disposed should not be used after that point. Note, that even if the exception is prevented with a null check, the behavior of the control may not be correct after that.
That said, I researched the scenario some more and I want to suggest something that you can try. You can invoke the Initialize and OnApplyTemplate methods of the RadMap when setting up the instance (after the call to the Dispose method). Both of the methods initialize the resources, which are disposed in the Dispose method. Can you give this approach a try and let me know how it goes?
Regards,
Vladimir Stoyanov
Progress Telerik
Hi Vladimir,
I think you misunderstand. Most of the our application has a single map control and we successfully reuse that (without disposing) as much as possible. However there are times when our user can launch another dialog showing another instance of the map control in a separate window. When they close that window we need to be able to dispose the map control without crashing the application otherwise we can get a memory leak. In no case do we try and reuse a disposed map control (as you point out that would be wrong).
Are there any plans to fix the possibility of a null reference exception in the Telerik code? I'd like to also mention the issue I raised at the start of this thread, which is another case of an exception which can't be caught in the application.
Thanks
Pete
Hello Peter,
Thank you for the additional information.
With it, I was able to reproduce the exception in the MouseControl class. I managed to do so by showing a Window with a RadMap as Content and disposing the RadMap in the Closed event of the Window. The exception only occurs, if the RadMap is clicked and then the window is closed quickly (faster than 0.3 seconds).
I have logged this behavior in our feedback portal: Map: NullReferenceException when clicking on a RadMap shown in a Window and then quickly closing the Window. You can follow the item in order to get notified for any developments. I have also updated your telerik points for bringing this to our attention.
As for the exception mentioned in the beginning of the thread, based on your reply on "08 Jul 2019" I was under the impression that the reason for it was the fact that the RadMap instance was used after it is disposed. If that is not the case, may I ask you to share a sample project demonstrating the exception so that I can investigate it?
Regards,
Vladimir Stoyanov
Progress Telerik
Hi Vladimir,
Thanks for confirming there's a bug in Telerik code. I'm not sure I can create a sample app to show the other issue but I'll continue to monitor it. I do think it's like the bug you've raised - there remains a possibility that it might happen depending on the timing of events if the map is disposed.
Thanks
Pete
Hello Pete,
I investigated the stacktrace connected to the other exception and the related code, however it originates from the MouseMove event of the RadMap, which should not be thrown, if the control is not being used. It is also necessary for us to be able to replicate the exception on our end in order to log a bug report and introduce changes into our source code.
Should you manage to reproduce the exception in a sample project, feel free to send it over and I will be glad to investigate the scenario further.
Regards,
Vladimir Stoyanov
Progress Telerik
Hi Vladimir,
I agree that the control shouldn't be used after it has been disposed and so MouseMove events shouldn't happen. I'm unable to explain how it happened originally. The only possible explanation I have is if the mouse event is delayed in the windows message loop because of some other UI operation and somehow it gets to the control after it is disposed. Ideally the control could ensure that code within a timer thread does not throw an exception that cannot be handled by the application.
In any event, it's possible that other changes in our code-base mean that it no longer happens and we don't need to worry about it.
Thanks
Pete
Hello Pete,
My guess is that the exception was due to the previous setup of the application, since as you mentioned the MouseMove event should not be fired when the map is not used. Note, that there isn't a timer in the code related to the MouseMove stacktrace. There was a timer involved with the exception, which I managed to reproduce, however that is a different scenario.
Regards,
Vladimir Stoyanov
Progress Telerik
Peter, so you are actually seeing memory issues if the map is not disposed? I've used the map for years and only recently noticed that map had a Dispose method. This was suprising because I thought WPF controls did not require disposal. I think I only noticed because I saw this item in the documentation:
https://docs.telerik.com/devtools/wpf/controls/radmap/how-to/disposing-map
I do have one map that is used in a popup window but never checked the memory usage before and after the window was closed.
Hi Jason,
I can't recall if we actually saw memory issues before disposing the map. I guess the need to call dispose may depend on the type of layers you have added to the map. but given that there is a Dispose method I think it is advisable to call it when it's no longer in use.
Pete