Select to view content in your preferred language

GraphicTracker fails with AccessViolationException

3633
34
Jump to solution
02-27-2013 04:52 AM
DavidClarke1
Occasional Contributor
I am developing an application using the GraphicTracker to display moving icons.  I am using the Windows.Forms.Timer event to suspend the display updates for two seconds.  I have another couple subroutines that creates and removes GraphicTrackerSymbols to the tracker.  All this happens on the same thread as the Windows.Forms.Timer does excute on the main gui thread.  I have also verified this in debug.

Every so often the program will crash with an AccessViolationException while trying to set the GraphicTracker.SuspendUpdate property to FALSE; it ALWAYS fails on trying to set that property to "FALSE".  What I noticed was that if I do not remove any items from the GraphicTracker it does not fail.  When I am removing items here and there as needed at some point it will fail.  It may be 30 seconds or 2 minutes.  I use the exact same test data (geometry/symbols/coordinates) but it still fails at some "random" time.  The map no longer updates on the screen and the whole map is unusable.  I cannot seem to recover or continue on.  Catching the exception makes no difference as the map just will not work anymore.

Here is the timer code.  The timer itself is set to run every 2 seconds.

_gt.SuspendUpdate = False System.Threading.Thread.Sleep(300) _gt.SuspendUpdate = True


My thoughts are that there is some timing issue while using the "Remove" method on the GraphicTracker object that conflicts with the "SuspendUpdate" property.  I have tried setting the timer to be a longer or shorter interval and also changing the "Sleep" interval but nothing seems to help.
0 Kudos
34 Replies
LeoDonahue
Deactivated User
Here's a thought.  Rather than use a timer, what would happen if you added code to test whether the GraphicsTracker count was divisible by 10 with a remainder of 0, then unsuspend the auto-refresh and re-suspend it?  Then your display is refreshing for every 10 graphic objects you add, or you can set it to 20 or whatever.

Perhaps the threading is causing a problem.
0 Kudos
DavidClarke1
Occasional Contributor
The GPS data I get is sent to me.  I cannot poll it.  I have to process it as it comes in.  Depending on the customer's environment, I could have 2-5 records a second or I could have 2-5 records in 10-15 minutes.

While in debug I have seen as few as 10 items displayed on the screen when it fails.  Plus the map I am using is scaled down.  Even if I turn off every layer, it will fail.

I have been trying to build a very small application that will exhibit the same failure but it is not complete yet.  There are a few other things I am doing that may be factors (text masking for labels and using bitmaps for icons).  If I can get that going (or rather to fail), then I could post that code.


Here's a thought. Rather than use a timer, what would happen if you added code to test whether the GraphicsTracker count was divisible by 10 with a remainder of 0, then unsuspend the auto-refresh and re-suspend it? Then your display is refreshing for every 10 graphic objects you add, or you can set it to 20 or whatever.


That could work if I was always adding graphics.  However, most times I have items that move on the map so I am using the "MoveTo" method of the GraphicTracker.  The graphic count property of the GraphicTracker does not represent the number of transactions I am receiving.  Although I could have a counter for how many transactions I get.  But then I will have to take into account the times I do not get data for minutes.

Perhaps the threading is causing a problem.


The System.Windows.Forms.Timer object executes on the main GUI thread and I even verified this within debug because I thought maybe the MSDN documentation was lying.
0 Kudos
LeoDonahue
Deactivated User
David,

I have a working sample that is based on a Java Tool Add-in, but I believe you can tailor it to your data stream of geometries.

I launch ArcMap 10.0 SP5
I add a layer "ESRI_StreetMap_World_2D" from the services.argisonline.com
I click my Add-in Tool and start clicking in the map.

Initially, for the first five seconds, nothing shows up in the map where I've clicked.  After five seconds pass, the previous click points show up and as I click, the current points are added to GraphicTracker for the next 5 seconds, then disappear for 5 seconds, and so on.

I started just above the peninsula and went counter clockwise, zig zagged until I came back around.

[ATTACH=CONFIG]22281[/ATTACH]

Granted, I did not keep this process up for very long.  I did go throuh about 5 or 6 suspend/unsuspend iterations though without any problems.

import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

import com.esri.arcgis.addins.desktop.Tool;
import com.esri.arcgis.arcmapui.IMxDocument;
import com.esri.arcgis.carto.IActiveView;
import com.esri.arcgis.display.IDisplayTransformation;
import com.esri.arcgis.display.IScreenDisplay;
import com.esri.arcgis.display.ISymbol;
import com.esri.arcgis.display.RgbColor;
import com.esri.arcgis.display.SimpleMarkerSymbol;
import com.esri.arcgis.display.esriSimpleMarkerStyle;
import com.esri.arcgis.enginecore.GraphicTracker;
import com.esri.arcgis.enginecore.IGraphicTrackerSymbol;
import com.esri.arcgis.framework.IApplication;
import com.esri.arcgis.geometry.IGeoTransformation;
import com.esri.arcgis.geometry.IGeometry;
import com.esri.arcgis.geometry.IPoint;
import com.esri.arcgis.geometry.ISpatialReference;
import com.esri.arcgis.geometry.Point;
import com.esri.arcgis.geometry.SpatialReferenceEnvironment;
import com.esri.arcgis.interop.AutomationException;


public class GTTool extends Tool {


    /**
     * Called when the tool is activated by clicking it.
     * 
     * @exception java.io.IOException if there are interop problems.
     * @exception com.esri.arcgis.interop.AutomationException if the component throws an ArcObjects exception.
     */
    @Override
    public void activate() throws IOException, AutomationException {
        
    }

    @Override
    public void init(IApplication app) throws IOException, AutomationException {
        super.init(app);
        mxDoc = (IMxDocument)app.getDocument();
    }

    @Override
    public void mousePressed(MouseEvent mouseEvent) {
        super.mousePressed(mouseEvent);
        
        try {
            // Create a point based on the user's mouse click.
            Point p1 = new Point();
            p1.putCoords((double)mouseEvent.getX(), (double)mouseEvent.getY());

            if(gt == null){
                // Instantiate a GraphicTracker and initialize it once.
                //  GraphicTracker by default will refresh after each geometry add
                gt = new GraphicTracker();
                gt.initialize(mxDoc.getActiveView().getFocusMap());
                
                // Tell the GraphicTracker that we want to suspend auto refresh.
                gt.setSuspendUpdate(true);
                
                // Create a timer task to toggle the automatic graphic tracker refresh for the duration in milliseconds.
                //  intially for 5 seconds and every 5 seconds after that.
                Timer timer = new Timer();
                timer.scheduleAtFixedRate(new UnSuspendTimerTask(), 5000, 5000);
            }
            
            // Create a color object that is red
            RgbColor color = new RgbColor();
            color.setRed(255);

            // Create a simplemarkersymbol to be used as the symbol for the graphic placed by GraphicTracker
            twoDSymbol = new SimpleMarkerSymbol();
            twoDSymbol.setStyle(esriSimpleMarkerStyle.esriSMSSquare);
            twoDSymbol.setSize(4);
            twoDSymbol.setColor(color);
            
            // Tell the GraphicTracker to create a symbol and assign it to the GraphicTrackerSymbol object
            gts = gt.createSymbol((ISymbol)twoDSymbol, null);
            
            // Conver the user's map click point to something useful..
            Point mapPoint = (Point)getMapCoordinatesFromScreenCoordinates(p1, mxDoc.getActiveView());
            
            // Declare ESRI named constants for projecting data between WGS1984 and NAD83 HARN AZ Central
            int wgs84 = com.esri.arcgis.geometry.esriSRGeoCSType.esriSRGeoCS_WGS1984;
            int geotransformId = com.esri.arcgis.geometry.esriSRGeoTransformationType.esriSRGeoTransformation_NAD1983_To_WGS1984_1;
            int geotransformDirection = com.esri.arcgis.geometry.esriTransformDirection.esriTransformForward;
            
            // Create a SpatialReferenceEnvironment, ISpatialReference and IGeoTransformation
            SpatialReferenceEnvironment spatialRef = new SpatialReferenceEnvironment();
            ISpatialReference gcswgs84 = spatialRef.createGeographicCoordinateSystem(wgs84);
            IGeoTransformation iGeoTransformation = (IGeoTransformation)spatialRef.createGeoTransformation(geotransformId);
            
            // Set the spatial reference of the point to the spatial reference of the map window (activeView)
            mapPoint.setSpatialReferenceByRef(mxDoc.getActiveView().getScreenDisplay().getDisplayTransformation().getSpatialReference());
            
            // Project the point from whatever the activeView spatial reference is to GCS_WGS_1984 World projection - Decimal Degrees
            mapPoint.projectEx(gcswgs84, geotransformDirection, iGeoTransformation, false, 0.0, 0.0);
            
            // Add the geometry and symbol to the GraphicTracker
            gt.add((IGeometry) mapPoint, gts);
            
        } catch (AutomationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    public static IPoint getMapCoordinatesFromScreenCoordinates(IPoint screenPoint, IActiveView activeView)throws Exception{

        if (screenPoint == null || screenPoint.isEmpty() || activeView == null)
            return null;

        IScreenDisplay screenDisplay = activeView.getScreenDisplay();
        IDisplayTransformation displayTransformation = screenDisplay.getDisplayTransformation();
        
        // Returns a point in whatever coordinate system the DataFrame is set to
        return displayTransformation.toMapPoint((int)screenPoint.getX(), (int)
            screenPoint.getY());
    }

    public static GraphicTracker gt = null;
    private IGraphicTrackerSymbol gts;
    private SimpleMarkerSymbol twoDSymbol;
    public static IMxDocument mxDoc;
}

class UnSuspendTimerTask extends TimerTask{

    @Override
    public void run() {
        try {
            if(GTTool.gt.isSuspendUpdate()){
                // make it auto refresh
                GTTool.gt.setSuspendUpdate(false);
            } else {
                // don't make it auto refresh
                GTTool.gt.setSuspendUpdate(true);
            }
        } catch (AutomationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
0 Kudos
DavidClarke1
Occasional Contributor
I will take a look and compare with what I am doing.

The failure only seems to happen if I am deleting graphics from the GraphicsTracker.  I never have a problem just adding graphics.  Since the failure ALWAYS happens while accessing the "SuspendUpdate" property it seems odd to me that it only fails if I have deletes occurring.
0 Kudos
LeoDonahue
Deactivated User
If you are deleting graphics, and you get the failure, it would explain your AccessViolationException.  Basically, you're reading from memory something that is not there anymore.

Have you revisited your logic when it comes to removing graphics by the id integer?  Perhaps the integer id you are trying to remove is not valid?
0 Kudos
DavidClarke1
Occasional Contributor
I have reviewed my logic for days and longer.  While I am willing to accept I've made mistake I cannot see it.  I am sure the ID (GLID) is valid before deleting.  I'll have to re-explain a little bit because it might not have been clear before.

The error only occurs while accessing the "SuspendUpdate" property not on the "Remove" method.  If you try to remove a graphic from the Tracker with an invalid ID as you suggest, it throws a different exception on the "Remove" method.

While my code executes, graphics are added and sometimes removed from the map.  At some point while setting the "SuspendUpdate" property, it fails.  I cannot pinpoint if this happens directly after removing a graphic.  But the code can successfully remove many items before failure.

If I comment the code to remove graphics then it works fine and I haven't seen a failure.  While this must be a vital clue it doesn't tell me why the "SuspendUpdate" is blowing up in a separate part of the code.  It makes me think the "Remove" method of the GraphicTracker is bugged.  Since the error isn't consistent in when it is thrown it is hard to say for sure.
0 Kudos
DavidClarke1
Occasional Contributor
I was able to code a small project that demonstrates the problem.  Run it and open an MXD.  Eventually, it will fail.

It was done with Visual Studio 2010.
0 Kudos
LeoDonahue
Deactivated User
In MainForm.vb, Sub PrepareMap(), are you sure this is right?

_gpsThread = New System.Threading.Thread(New System.Threading.ParameterizedThreadStart(Sub() CreateGPSData(mapCenter.X, mapCenter.Y)))


When I look at examples, I haven't seen an example that creates a thread like this.  The way you did it in Sub CreateGPSData looks like the examples I've seen.

http://msdn.microsoft.com/en-us/library/system.threading.parameterizedthreadstart.aspx?cs-save-lang=...
0 Kudos
DavidClarke1
Occasional Contributor
I found that as an alternate way to pass parameters when creating a thread.  Someone suggested this as a superior method than passing with the "Start" method on the Thread object.  Honestly, I am not sure about that.

For this code example I can pass the parameters either way and the code still fails.  I originally did pass on the "Start" method.  I have tried MANY things to attempt to resolve this issue.
0 Kudos
LeoDonahue
Deactivated User
I'm out of suggestions except one.

Can you output (to the console or somewhere) the values of key, coord, coord.GLID before you make the remove call in UpdateGLID?  That woud help you sort out what values are trying to be used when it crashes.
0 Kudos