This is a migrated thread and some comments may be shown as answers.

Map crash while disposing

19 Answers 308 Views
Map
This is a migrated thread and some comments may be shown as answers.
Peter
Top achievements
Rank 1
Veteran
Peter asked on 12 Apr 2019, 10:30 AM

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

Sort by
0
Peter
Top achievements
Rank 1
Veteran
answered on 10 May 2019, 02:21 PM

No update on this post since I raised it on 12th April. Would I be better raising a support ticket?

Pete

0
Vladimir Stoyanov
Telerik team
answered on 15 May 2019, 12:12 PM
Hello 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
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Peter
Top achievements
Rank 1
Veteran
answered on 24 Jun 2019, 10:24 AM

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

0
Vladimir Stoyanov
Telerik team
answered on 26 Jun 2019, 11:57 AM
Hello 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
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Peter
Top achievements
Rank 1
Veteran
answered on 26 Jun 2019, 12:43 PM

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

0
Peter
Top achievements
Rank 1
Veteran
answered on 08 Jul 2019, 12:44 PM

I tried that and it works in the scenario I described. 

Thanks for your help

Pete

0
Vladimir Stoyanov
Telerik team
answered on 09 Jul 2019, 10:25 AM
Hello 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
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Peter
Top achievements
Rank 1
Veteran
answered on 25 Oct 2019, 04:25 PM

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

 

0
Vladimir Stoyanov
Telerik team
answered on 30 Oct 2019, 12:09 PM

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

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Peter
Top achievements
Rank 1
Veteran
answered on 30 Oct 2019, 02:22 PM

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

0
Vladimir Stoyanov
Telerik team
answered on 01 Nov 2019, 01:36 PM

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

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Peter
Top achievements
Rank 1
Veteran
answered on 01 Nov 2019, 01:51 PM

 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

0
Vladimir Stoyanov
Telerik team
answered on 05 Nov 2019, 11:44 AM

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

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Peter
Top achievements
Rank 1
Veteran
answered on 11 Nov 2019, 09:23 AM

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

0
Vladimir Stoyanov
Telerik team
answered on 13 Nov 2019, 05:03 PM

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

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Peter
Top achievements
Rank 1
Veteran
answered on 14 Nov 2019, 04:43 PM

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

0
Vladimir Stoyanov
Telerik team
answered on 19 Nov 2019, 12:06 PM

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

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Jason D
Top achievements
Rank 1
Veteran
answered on 16 Dec 2019, 07:53 PM

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.

0
Peter
Top achievements
Rank 1
Veteran
answered on 16 Dec 2019, 09:03 PM

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

Tags
Map
Asked by
Peter
Top achievements
Rank 1
Veteran
Answers by
Peter
Top achievements
Rank 1
Veteran
Vladimir Stoyanov
Telerik team
Jason D
Top achievements
Rank 1
Veteran
Share this question
or