Creating Map Within Windows Service - Access Violation

Topics: WinForms Controls
Aug 4, 2014 at 8:49 AM
Hi,

I am using SharpMap within a window service. I am using the library to generate a map and then create an image out of it and add it to an Excel file. The code runs within a Quartz.net task.

The error happens when I call SharpMap.Map.GetMap() and crushes my service (see error report and stack call below).

This only happens every now and then (maybe issues with data?). Also note, I had some issues with working with the map so I put some delay (System.Threading.Thread.Sleep(5000);) just before calling the function.

I am generating several such images and the error (last time it occurred) was after a few (~10) iterations. In order not to recreate and format the "static layers" of the map (SharpMap.Map) each time I just add and remove the "dynamic" layers and resize some features in each iteration.

Is there a way to find what the cause of the error?

Is there a way to gracefully handle such exception?

The Windows CMS error report + stack trace

Application: NePTune.WinService.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
Stack:
at System.Drawing.SafeNativeMethods+Gdip.GdipPathIterNextSubpathPath(System.Runtime.InteropServices.HandleRef, Int32 ByRef, System.Runtime.InteropServices.HandleRef, Boolean ByRef)
at System.Drawing.SafeNativeMethods+Gdip.GdipPathIterNextSubpathPath(System.Runtime.InteropServices.HandleRef, Int32 ByRef, System.Runtime.InteropServices.HandleRef, Boolean ByRef)
at System.Drawing.Drawing2D.GraphicsPathIterator.NextSubpath(System.Drawing.Drawing2D.GraphicsPath, Boolean ByRef)
at SharpMap.Rendering.Symbolizer.WarpPathToPath.Warp(System.Drawing.Drawing2D.GraphicsPath, System.Drawing.Drawing2D.GraphicsPath, Boolean, Single)
at SharpMap.Rendering.Symbolizer.WarpPathToPath.DrawString(System.Drawing.Graphics, System.Drawing.Pen, System.Drawing.Brush, System.String, System.Drawing.FontFamily, Int32, Single, System.Drawing.StringFormat, Boolean, System.Drawing.Drawing2D.GraphicsPath)
at SharpMap.Layers.LabelLayer.Render(System.Drawing.Graphics, SharpMap.Map)
at SharpMap.Map.RenderMap(System.Drawing.Graphics)
at SharpMap.Map.GetMap()
at NePTune.Logic.Algorithms.UlInterference.UlInterferenceReportWriter.CreateClusterMap(System.Collections.Generic.List1<NePTune.Data.SectorGeoDetails>, System.Collections.Generic.Dictionary2<Int32,Double>, Int32, System.Collections.Generic.IEnumerable1<NePTune.Data.SectorGeoDetails>, Boolean)
at NePTune.Logic.Algorithms.UlInterference.UlInterferenceReportWriter.CreateInterferenceClusterWorksheets(OfficeOpenXml.ExcelPackage, System.Collections.Generic.List
1<NePTune.Logic.Algorithms.UlInterference.UlInterferenceCluster>, System.Collections.Generic.Dictionary2<Int32,NePTune.Data.SectorGeoDetails>, System.Collections.Generic.Dictionary2<Int32,NePTune.Logic.Algorithms.UlInterference.CellMeasurementList>)
at NePTune.Logic.Algorithms.UlInterference.UlInterferenceReportWriter.CreateReport(System.Collections.Generic.List1<NePTune.Logic.Algorithms.UlInterference.CellUlInterferenceResults>, System.Collections.Generic.List1<NePTune.Logic.Algorithms.UlInterference.UlInterferenceCluster>, System.Collections.Generic.Dictionary2<Int32,NePTune.Data.SectorGeoDetails>, System.Collections.Generic.Dictionary2<Int32,NePTune.Logic.Algorithms.UlInterference.CellMeasurementList>)
at NePTune.Logic.Algorithms.UlInterference.UlInterferenceDriver.Execute(NePTune.Common.Configuration.GlobalConfiguration, NePTune.Logic.Algorithms.UlInterference.UlInterferenceConfiguration)
at NePTune.Logic.Algorithms.AlgorithmJobs.UlInterferenceJob.Execute(NePTune.Common.Configuration.GlobalConfiguration, NePTune.Logic.Algorithms.Common.IAlgorithmConfiguration, System.String, System.String, System.String)
at NePTune.Logic.Algorithms.AlgorithmJobs.UlInterferenceJob.Execute(Quartz.IJobExecutionContext)
at Quartz.Core.JobRunShell.Run()
at Quartz.Simpl.SimpleThreadPool+WorkerThread.Run()
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Threading.ThreadHelper.ThreadStart()
Coordinator
Aug 4, 2014 at 10:07 AM
Is it always this same error? If so, I assume we have to look at concurrency issues with the warping algorithm used within the label layer.
Aug 4, 2014 at 10:13 AM

It is the first time I realized that this crushes the service but I assume it is as the relevant code has not changed.

Can you elaborate on “we have to look into concurrency…”?

Editor
Aug 4, 2014 at 2:45 PM
can you post the bits of your code that cause this?

We run Sharpmap as a WMS service running inside a windows service with no errors.

One thing that might help is to call Map.Clone() before calling getMap.

It will slow the process down a little, but much less so than sleeping a thread for 5 seconds.

Make sure you dispose the clone once finished with it.
Marked as answer by tomercagan on 8/31/2014 at 12:23 AM
Aug 4, 2014 at 8:09 PM
Edited Aug 4, 2014 at 8:11 PM
Hi,

The code is a bit long but I will try to highlight what I am doing.

The function is being called within a loop so GetMap() below only creates map once and then uses the same (class level) reference for subsequent calls.

All functionality inside a "report writer" class that creates an Excel file and embed map images in it:
public Image CreateClusterMap(...)
{
    var map = this.GetMap(...);   //Get the map instance being used. 
                                                    // Create map instance, add static layers from shape files. 
                                                    // Add a dynamic layer based on data including a vector and label layers

    // add dynamic layer based on data. This is the data the changes between iterations
    var sectorsLayer = CreateSectorsLayer(clusterGeo, cellMaxRssi);
    map.Layers.Add(sectorsLayer);

    var labelLayer = new LabelLayer(string.Format("Cluster {0} Labels", clusterId));
    labelLayer.DataSource = sectorsLayer.DataSource;
    labelLayer.LabelColumn = "id";
    map.Layers.Add(labelLayer);

    // get the size of the new layer and calculate some sizes for other object on map (the dynamic layer from this.GetMap)
    var env = sectorsLayer.Envelope;
    var minDimension = Math.Min(env.Height, env.Width);
    int ellipeSize = (int)(minDimension * 0.04);
    Size sectorSize = new Size(ellipeSize, ellipeSize);
    // "zoom to extents"           
    map.ZoomToBox(env.Grow(500, 500));

    #region Update the size of a site (common dynamic layer) in the map
    var sitesLayer = map.Layers.FirstOrDefault(l => l.LayerName == "sites") as VectorLayer;

    if (sitesLayer != null)
    {
        for (uint i = 0; i < sitesLayer.DataSource.GetFeatureCount(); i++)
        {
            var feature = sitesLayer.DataSource.GetFeature(i);
            feature.Geometry = new Polygon(CreateEllipse(feature.Geometry.Centroid.X, feature.Geometry.Centroid.Y, sectorSize, 12));
         }
    } 
    #endregion

    System.Threading.Thread.Sleep(5000);   //TODO: probably use the close suggestion instead

    var img = map.GetMap();

    // Remove the two layers (to clean up the map and have it ready for next iteration)
    map.Layers.Remove(sectorsLayer);
    map.Layers.Remove(labelLayer);

    return img;
}
Regarding calling map.Clone() - how/why should it help? Also, I guess if I do that, I can just add the "iteration level" layers (sectorLayer and labelLayer) and then just dispose of the clone?
Editor
Aug 5, 2014 at 9:56 AM
Hi,

Using clones is quite common in these situations, as it can remove concurrency issues, because you are using copies of an object rather than trying to reuse the same one over and over again.

I would start by cloning the map, then calculate/add all you features to that map, then do the image export and then dispose of the clone.

I would remove the thread sleep call as there is no need for this.

Now that you are using clones you should be able to multi-thread this whole operation to make it much faster too.
Marked as answer by tomercagan on 8/31/2014 at 12:24 AM
Editor
Aug 12, 2014 at 5:12 PM
Did you manange to get it working?
Aug 12, 2014 at 5:19 PM
Edited Aug 31, 2014 at 8:24 AM
Hi,

Sorry for not getting back on this sooner (juggling several projects...)

I updated the code to use a cloned map as per the suggestion above.

I run unit tests for my program and today I deployed it. I guess we will know in several days.

Thanks!

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After more than two weeks this is still running without an issue - so this was a good solution - thanks!