mbw@geus.dk

Could my use of Projection Engine cause Access Violation?

Discussion created by mbw@geus.dk on Oct 15, 2010
Hi Forum

I have made a little edit utility for ArcMap. It works well, but about once a day ArcMap will close unexpectedly, leaving a message like the attached png. Different dll's like nt.dll, EditTools.dll and Geometry.dll experience uncaught Access Violation Errors.
I am not sure if my utility is causing these errors, but they seem to occur more often, when this utility is used.
One of the things I am not sure of is my use of ESRI's projection Engine.

I followed the advices on this thread: Can I get the Geodesic Angle directly from projection Engine? and it does work.

1) I made a fairly simple class ProjectionEngineWrap as an excerpt from Richie Carmichael's example.  But should I give it a GUID and find it with findExtension()?

2) Should I do something special in order to make it threadsafe?

3) My Class Edithelper uses an instance of ProjectionEngineWrap. When I am finished with the instance, is it sufficient, as I do, to call dispose() and set the member variable to null, or should I do something more?

I am using ArcGis 9.3.1 and C# in Visual Studio

I would be happy if someone could point me in the direction of more information.

Regards

Marianne B. Wiese

/*  GEUS 2010
 *  Author: Marianne B. Wiese
 * 
 *  This Module uses the methods from Richie Carmichael's wrapper
 *  http://mrrichie.spaces.live.com/blog/cns!DD16C3F34F4D913E!1807.entry
 * 
 * */

using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace GEUS
{
    public class ProjectionEngineWrap : IDisposable 
    {
        private bool m_disposed = false;
        private IntPtr m_pe = IntPtr.Zero;

        private GeodesicDistance m_gd = null;

        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibrary(string dllname);

        [DllImport("kernel32.dll")]
        private static extern bool FreeLibrary(IntPtr hModule);

        [DllImport("kernel32.dll")]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

        private delegate void GeodesicDistance(
            [In] double semiMajorAxis,
            [In] double eccentricity,
            [In] double longitude1,
            [In] double latitude1,
            [In] double longitude2,
            [In] double latitude2,
            [Out] out double distance,
            [Out] out double azimuthForward,
            [Out] out double azimuthBack);

        public ProjectionEngineWrap()
        {
            string dll = getDesktopDll();

            // Load Projection Engine
            this.m_pe = ProjectionEngineWrap.LoadLibrary(dll);
            if (this.m_pe == IntPtr.Zero)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            // Get pointer to function call
            IntPtr gd = ProjectionEngineWrap.GetProcAddress(this.m_pe, "pe_geodesic_distance");
            if (gd == IntPtr.Zero) { 
                int err = Marshal.GetLastWin32Error();
                this.Dispose();
                throw new Win32Exception(err);
            }

            // Create delegate
            this.m_gd = (GeodesicDistance)Marshal.GetDelegateForFunctionPointer(gd, typeof(GeodesicDistance));
        }

        ~ProjectionEngineWrap() { this.Dispose(); }

        public void Dispose() {
            if (this.m_disposed) { return; }    
            if (this.m_pe != IntPtr.Zero)
            {
                // Release Memory
                ProjectionEngineWrap.FreeLibrary(this.m_pe);
            }
            // Update flag.
            this.m_disposed = true;
            // No need to finalize
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Returns the geodestic azimuth and distance between two geographic locations.
        /// http://edndoc.esri.com/arcsde/9.3/api/capi/geometry/coordsys/pegeodesicdistance.htm
        /// </summary>
        /// <param name="semiMajorAxis">Semi Major Axis</param>
        /// <param name="eccentricity">Globe Eccentricity</param>
        /// <param name="longitude1">From Longitude (radians)</param>
        /// <param name="latitude1">From Latitude (radians)</param>
        /// <param name="longitude2">To Longitude (radians)</param>
        /// <param name="latitude2">To Latitude (radians)</param>
        /// <param name="distance">Returned Geodetic Distance</param>
        /// <param name="azimuthForward">Returned Forward Azimuth (radians)</param>
        /// <param name="azimuthBack">Returned Reverse Azimuth (radians)</param>
        public void GetGeodesicDistance(
            double semiMajorAxis,
            double eccentricity,
            double longitude1,
            double latitude1,
            double longitude2,
            double latitude2,
            out double distance,
            out double azimuthForward,
            out double azimuthBack)
        {
            this.m_gd(
                semiMajorAxis,
                eccentricity,
                longitude1,
                latitude1,
                longitude2,
                latitude2,
                out distance,
                out azimuthForward,
                out azimuthBack);
        }

        private string getDesktopDll()
        {
            RegistryKey coreRuntime = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\ESRI\CoreRuntime", false);
            if (coreRuntime == null) { return null; }
            object installDir = coreRuntime.GetValue("InstallDir", null);
            coreRuntime.Close();
            if (installDir == null) { return null; }
            string folder = installDir.ToString();
            string bin = Path.Combine(folder, "bin");
            return Path.Combine(bin, "pe.dll");
        }
    }
}


        private float getMeridianConvergence(IPoint p0)
        {
            // GRS_1980. From File: North American Datum 1983.prj
            const double semiMajorAxis = 6378137;
            // Eccentricity squared of the spheroid e^2 = 2f-f^2
            // Calculated from flattening f = 1/298.257222101 
            // Inverse flattening from File: North American Datum 1983.prj
            const double eccentricity = 0.00669438002298586;

            if (peEngine == null)
                peEngine = new ProjectionEngineWrap();

            meridianConvergence = 0;   // float
            try
            {
                // p0's latlong koordinater
                p0.Project(geographicSpatialRef);
                p0.QueryCoords(out fromLong, out fromLat);

                // Koordinater fra et punkt 100 m nord for p0 (kort nord)
                p0.Project(map.SpatialReference);
                p0.QueryCoords(out X, out Y);
                p0.PutCoords(X, Y + 100);
                p0.Project(geographicSpatialRef);
                p0.QueryCoords(out toLong, out toLat);
                // Sæt tilbage, så anchorpoint i onVertexAdded() ikke forskydes
                p0.Project(map.SpatialReference);
                p0.PutCoords(X, Y);

                // Lokal meridiankonvergens fra ESRI's 'projektionsmaskine' bibliotek, pe.dll
                peEngine.GetGeodesicDistance(semiMajorAxis,
                                             eccentricity,
                                             ToRadians(fromLong),
                                             ToRadians(fromLat),
                                             ToRadians(toLong),
                                             ToRadians(toLat),
                                             out distance,
                                             out azimuthForward,
                                             out azimuthBack);

                meridianConvergence = 360 + (float)ToDegrees(azimuthForward);
                if (meridianConvergence > 359)
                    meridianConvergence -= 360;
            }
            catch (Exception e)
            {
                MessageBox.Show("GEUS EditHelper getMeridianConvergence():  \n" + e.Message);
            }
            return meridianConvergence;
        }


        void EditHelper_OnStopEditing(bool save)
        {
            if (peEngine != null)
            {
                peEngine.Dispose();
                peEngine = null;
            }

        }

Attachments

Outcomes