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.


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;

        private static extern IntPtr LoadLibrary(string dllname);

        private static extern bool FreeLibrary(IntPtr hModule);

        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();
                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
            // Update flag.
            this.m_disposed = true;
            // No need to finalize

        /// <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)
                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);
            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
                // p0's latlong koordinater
                p0.QueryCoords(out fromLong, out fromLat);

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

                // Lokal meridiankonvergens fra ESRI's 'projektionsmaskine' bibliotek, pe.dll
                                             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 = null;