ThinkGeo.com    |     Documentation    |     Premium Support

Handle Count is increasing

We have an application using ThinkGeo for WPF version 14.2.6
The application is leaking Handles (Event) causing the application to crash after some days run.
We have identified that the Handles leak is coming from ThinkGeo map.
We have reproduced the behaviour in a demo WPF application (a main window, ThinkGeo Desktop map loading a ECW raster map and a timer moving an Feature on the map).

The Feature is show using a simple geometric form created via using PointStyle.CreateSimpleTriangleStyle

The Feature position is updated by removing the old feature and adding a new feature with the updated position:

GeoCollection coll = m_LayerNodes.InternalFeatures;
Feature feature = new Feature(longitude, latitude, KEY_Node);
feature.ColumnValues[NODE_TITLE_COLNAME] = “Test”;
feature.ColumnValues[NODE_STATE_COLNAME] = nodeState;

if (coll.Contains(feature.Id)) coll.Remove(feature.Id);
coll.Add(feature.Id, feature);

Via PerfMonitor we have monitored the Handler Count which is increasing

The same behaviour can be reproduced by panning the map.
We have read about the hotfix from 14.2.5 (Memory consumption with release 14) but this does not seem to work.

Due to the changes in the ThinkGeo API it is not possible to upgrade to ThinkGeo 14.3 whithout changes in our code.

Is there any solution/suggestion for our problem ?

Thanks
Domenico

Hi Domenico,

Thanks for reporting this!

  1. I couldn’t recreate the issue. To make sure we are on the same page, can you:
  • include this ResourceMonitor.cs (4.7 KB) add the following code to start logging the HandleCount?
 var monitor = new ResourceMonitor(interval: TimeSpan.FromSeconds(3),
     csvPath: @"d:\test\log.csv");
 monitor.Start();
  • Verify if you can see the HandleCount kept growing in the log
  • Please send over your demo to us.
  1. Instead of keep removing/adding a feature to update the position, I’d suggest checking out the following sample, in which it greatly improves the performance by using FeatureLayerWpfDrawingOverlay instead of a regular LayerOverlay.

  2. There are some API changes when upgrading to the latest v14.3 but it should be straightforward. Just let us know if you see any question. I really recommended you to upgrade to the latest stable if possible. Here are the changelog of v14.3.x for your reference.
    Changelog - ThinkGeo Docs

Thanks,
Ben

Hi Ben

Thanks for your Reply.
I am already logging the handle count; both in the demo app (start and current handle count) and via PerfMonitor.
WIN10_NET9_THINKGEO_14_2_6.zip (941.2 KB) WIN10_NET9_THINKGEO_14_3_2.zip (664.0 KB) DemoRasterMap_2025_08_12.zip (13.8 KB)
I have added the source code of the Demo App. The Demo App is extracted from our app, therefore there are some Projection code which is required by the official app, but the code could be different considering only the demo. I performed one build using the 14.2.6 version (used in the first test) and thereafter I upgraded the demo app to 14.3.2. I used the second build in my second test.

I have also added the complete PerfMonitor log for both tests.

The PerfMonitor log contains the Handle count, the Thread Count, the working set and the working set - private (which is very close to the data you requested).

In the file DemoRasterMap_2025_08_12.zip you can find the source code for the demo app.

In the demo app I have removed the ECW files since I have no permission at the moment to distribute the files. In the demo app I have also experimented with GeoTIFF but I got the same result. The used ECW are about 500 MB and 5 GB in size.

In the file WIN10_NET9_THINKGEO_14_2_6.zip you can find the PerfMonitor file and the screen dump of the app and the perf monitor. This test has been running for about 22 hours, and the app has reached a handle count over 11000 handles.

In the file WIN10_NET9_THINKGEO_14_3_2.zip you can find the PerfMonitor file and the screen dump of the app and the perf monitor. This test has been running only 17 hours, and the app has reached a handle count about 1700 handles.

In future release we will upgrade our official app to 14.3.x and I will also evaluate the FeatureLayerWpfDrawingOverlay as suggested.

But at the moment for us it is important to find a solution for the current problem in a short time period since our customer are waiting for a solution.

Note the demo app is not maintaining any collection except for:
GeoCollection coll = m_LayerNodes.InternalFeatures;
In this collection the added/replaced node has always the same ID therefore the collection should contain only 1 entry

Finally we experimented also with panning obtaining a similar result.

Looking for your reply.

Best regards

Domenico

Hi Ben

One other observation when running DEBUG version with the Diagnostic tools. The count of the CancellationTokenSource related object is increasing during the time:

Diff 1:

Diff 2:

Diff 3:

This could also be a reason for the increasing Handler Count.

Best regards
Domenico

Hi Domenico,

Thanks for sending the demo project — that was a big help.

I’m wondering which version you were using when you noticed the CancellationTokenSource count increasing. We fixed an issue in v14.3.0 (“ThinkGeo.UI.Desktop: improve performance by avoiding creating a new CancellationTokenSource when panning the map”), so I’d like to confirm whether you saw this behavior in v14.3.2 or v14.2.x.

Based on your data — v14.2.6 → ~11,000 handles in 22 hours vs. v14.3.2 → ~1,700 handles in 17 hours — it looks like v14.3.2 already shows a significant improvement, likely due to the CancellationTokenSource change, but there are still some handles not being released.

I’ve been running your demo project for almost an hour, and I can already see handle counts increasing. Our plan is to address this in the latest version first, then see if the fix can be backported to your version. That said, backporting may be difficult, and upgrading to the latest version might ultimately be required.
image
Thanks again for your demo — we’ll keep you updated.

Thanks,
Ben

Hi Ben

Yes, you are right the last screen dumps with the CancellationTokenSource leaks are from the demo app using ThinkGeo 14.2.6.

I have not observed the same kind of leak (CancellationTokenSource) in the demo app using ThinkGeo 14.3.2.

It has been decided to upgrade our official application with the last version of ThinkGeo (14.3.2) and waiting for ThinkGeo Handle-Count-leak FIX before releasing to our customers.

Do you have any time estimate when a new ThinkGeo version with the bug fix will be available?

I assume that the new ThinkGeo version is a kind of PATCH/HOTFIX containing very few changes. Is this correct?

Best regards

Domenico

Hi Domenico,

We are actively working on this issue. I suppose we would find the cause by early next week.

We will first fix it in the latest beta and then we’ll see how hard it is to patch it on v14.3.2. Ideally it’s something can be accomplished on your side by overriding some methods, but let’s see.

Finally glad you guys decided to upgrade to the latest version!

Thanks,
Ben

Hi Ben

I am upgrading to the the last version (14.3.2) and I am observing the following exception in the DEBUG trace:

W 2025-08-14 09:49:35.773Z Damm.MapCtrl.WPF.View: Error setting the Scale [Scale: 100000]
EXCEPTION: A task was canceled.
at ThinkGeo.UI.Wpf.MapViewBase.REQ=(PointShape fromCenter, PointShape toCenter, Double fromResolution, Double toResolution, Double fromAngle, Double toAngle, UInt32 animationLength, EasingFunctionBase easing, Boolean fromMouseInteraction, CancellationToken cancellationToken)
at ThinkGeo.UI.Wpf.MapViewBase.cEQ=(PointShape targetCenter, Double targetScale, Double rotationAngle, MapAnimationSettings animationSettings, CancellationToken cancellationToken, Boolean fromMouseInteraction)
at ThinkGeo.UI.Wpf.MapViewBase.ZoomToAsync(Double targetScale, CancellationToken cancellationToken)
at Damm.MapCtrl.WPF.MapView.SetScaleAsync(Double scale, Boolean isInitialScale, PointShape centerAt, CancellationToken cancellation) in D:\Repos\Releases\Ver08_12\Damm\DammMapCtrl.WPF\MapView.xaml.cs:line 4976
END EXCEPTION --------------------------------------------------------------------

The task from ThinkGeo.UI.Wpf.MapViewBase.ZoomToAsync was cancelled but the view is still open and the map is still shown.

Do you have any suggestion for this exception ?

Best regards
Domenico

Hi Domenico,

This is by design. TaskCanceledException is thrown when MapView.ZoomToAsync is canceled.

It can be canceled in any of these cases:

  1. User interaction interrupts the animation (pan, mouse-wheel zoom, etc).
  2. Another programmatic change starts (e.g., a second ZoomToAsync, or RefreshAsync).
  3. mapView.CancellationTokenSource.Cancel() is called.

Other async APIs may swallow the cancellation, but ZoomToAsync intentionally throws so callers can tell the map status (CurrentScale, CenterPoint, etc) exactly when the map is being canceled.

You can just eat the exception if you don’t need it.

try
{
    await mapView.ZoomToAsync(targetScale, cancellationToken);
}
catch (TaskCanceledException)
{
    // Expected if the zoom was interrupted by user input or another request.
    // Safe to ignore (map remains valid). 
}

Thanks,
Ben

After upgraded our application to ThinkGeo 14.3.2 I am observing the cancellation exception as mentioned before. In some cases, the UI is not updated correctly when the cancellation operation is thrown.

For example, in our application the user can press the zoom buttons indicated by the red arrow.

In some cases when the exception is thrown then the UI panting operation seems to be stopped.

In the following log the line containing “Zoom the map to the selected Scale” is written just before the ZoomToAsync is called. From the timestamp in the log there is a time difference between the 2 calls of about 1 second (the user press 2 times on the button).

I 2025-08-15 13:52:33.276Z Damm.MapCtrl.WPF.View: Zoom the map to the selected Scale [Requested Scale: 7500][Corrected Scale: 7500][Current Extent: 9.794884334932853,54.920608499413156,9.817885628262747,54.90964359715807][Current Center: 9.8063849815978,54.915126048285615,0][Reason: ViewModel.RequestChangeCurrentScale]
I 2025-08-15 13:52:34.195Z Damm.MapCtrl.WPF.View: Zoom the map to the selected Scale [Requested Scale: 10000][Corrected Scale: 10000][Current Extent: 9.789134011600398,54.92334972497692,9.823635951595202,54.906902371594306][Current Center: 9.8063849815978,54.915126048285615,0][Reason: ViewModel.RequestChangeCurrentScale]
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
W 2025-08-15 13:52:34.462Z Damm.MapCtrl.WPF.View: Error setting the Scale [Scale: 10000][Reason: ViewModel.RequestChangeCurrentScale]
EXCEPTION: A task was canceled.
   at ThinkGeo.UI.Wpf.MapViewBase.REQ=(PointShape fromCenter, PointShape toCenter, Double fromResolution, Double toResolution, Double fromAngle, Double toAngle, UInt32 animationLength, EasingFunctionBase easing, Boolean fromMouseInteraction, CancellationToken cancellationToken)
   at ThinkGeo.UI.Wpf.MapViewBase.cEQ=(PointShape targetCenter, Double targetScale, Double rotationAngle, MapAnimationSettings animationSettings, CancellationToken cancellationToken, Boolean fromMouseInteraction)
   at ThinkGeo.UI.Wpf.MapViewBase.ZoomToAsync(Double targetScale, CancellationToken cancellationToken)
   at Damm.MapCtrl.WPF.MapView.SetScaleAsync(Double _scale, Boolean _isInitialScale, Boolean _doCorrectScale, String _reason, PointShape _centerAt, CancellationToken _cancellation) in D:\Repos\Releases\Ver08_12\Damm\DammMapCtrl.WPF\MapView.xaml.cs:line 4843
END EXCEPTION --------------------------------------------------------------------

Code:
DammLogger.Log(DammLoggerLevel.Info, TAG,
"Zoom the map to the selected Scale " +
“[Requested Scale: {0}]” +
“[Corrected Scale: {1}]” +
“[Current Extent: {2}][Current Center: {3}]” +
“[Reason: {4}]”,
_scale, correctedScale,
currenExtent, currenExtent.GetCenterPoint(),
_reason
);
await Map.ZoomToAsync(correctedScale, _cancellation);

Do you have any suggestion about how we can avoid the bad refreshing in the UI ?

Best regards
Domenico

Hi Ben

I made an example about the task cancellation problem I am facing.
As mentioned the map rendeing is an un-terminated state when this happen.
In the demo I reconfigure the scale when the windows change the size.
Sometimes the exception is thrown when the scale is reconfigued due to the windows is maximized.
DemoMapZoom_2025_08_18.zip (16.9 KB)
Note I have removed the raster image from the project.
Can you suggest me what I am doing wrong when I invoke the Map.ScaleToAsync.
Thanks

Best regards
Domenico

Hi Domenico,

The last line in your UpdateNodePosition(), can you use m_OverlayNodes.RefreshAsync(); instead?

Reason: mapView.RefreshAsync() cancels any in-flight rendering . If UpdateNodePosition() fires while the background is still drawing, that global cancel makes the background appear half-rendered—exactly what you’re seeing.
m_OverlayNodes.RefreshAsync() only repaints that overlay and doesn’t cancel other rendering, so it’s lighter and avoids interrupting the base map.

Two extra tips:

  • The RefreshAsync() is not awaited (because it’s kicked off by a timer) so multiple refresh might happen at the same time.
  • Please play with FeatureLayerWpfDrawingOverlay (mentioned earlier in this thread) when having a chance, which works better for this scenario

Thanks,
Ben

Hi Ben

Thanks for your reply.

I think I have found a solution for my “cancellation exception”.

I implemented the suggestions from your last post, but they did help.

Then I selected a different approach. I am now using a Boolean controlling if the Map operation is still running before to call a new one.

For example, this is my case where I scale the map on a view resize action. In my SizeChanged event handler I invoked ZoomToAsync.

I believe that the ZoomTo calculation is very heavy and slow, and it is performed on a different thread than the UI in ThinkGeo core. Therefore, I am able to get multiple UI events before the previous started operation was terminated (awaiter invoked). In this way invoking a new ZoomToAsync raised the cancellation exception.

Using a Boolean controlling if the previous event handler is terminated then I can prevent multiple invocations. I can just use a Boolean in the event handler code since it is executed in the UI thread.

Best regards

Domenico

Hi Domenico,

Nice find! Here’s a tighter rundown of ZoomToAsync and the knobs you can tweak:

  1. What happens under the hood

    • Animation: moves the view (extent/center) on the UI thread.
    • Rendering: draws the map on a worker thread, then composites the bitmap back to the UI.
  2. Duration / turning animation off

    • Default duration is ~170 ms.

    • You can override per call or globally:

      // Per call
      var anim = new MapAnimationSettings { Duration = TimeSpan.FromMilliseconds(300) };
      await mapView.ZoomToAsync(..., anim);
      
      // Globally (affects double-tap, mouse wheel, etc. Set it to 0 to avoid any animation)
      MapView.DefaultAnimationSettings = new MapAnimationSettings {
          Duration = TimeSpan.Zero   // no animation
      };
      
  3. When to draw: before vs during the animation

    • Default is DrawAfterAnimation (animate first, then render).

    • Switch to DrawWithAnimation to render while animating:

      var anim = new MapAnimationSettings {
          Duration = TimeSpan.FromMilliseconds(300),
          Type = MapAnimationType.DrawWithAnimation
      };
      await mapView.ZoomToAsync(..., anim);
      
    • There are animation progress events you can hook if you need status updates.

For a concrete reference, the HowDoI → Map Navigation → VehicleNavigation sample shows these pieces in action.

Thanks,
Ben

Hi Ben

Thanks for the extra information.

At the moment I have disabled the animation in our application since we do not need that.

I am also serializing the calls to the map async methods (like refresh, zoom and center) in the application in order to be sure that they do not get aborted due to a long running previous async/await call.

Do you have any estimate when you are ready with the fix for the increasing handle count in the ThinkGeo 14.3.2 you mentioned in a previous post ?

Best regards
Domenico

Hi Domenico,

We are still working on the handle count increasing issue. It’s time consuming to test after every change, and takes longer than we expected, let us give you an update mid-next week.

Thanks,
Ben