[GIS] Loading an extension-related mxd in ArcMap by double clicking it

arcgis-10.0arcobjectscextensionsmxd

I am currently working in a company offering GIS solutions, and using ArcGIS tools on a daily basis. One of their main products is an ArcMap extension, which I am assigned on to correct some remaining bugs (my configuration being Windows 8 , ArcGIS 10.1 and Visual Studio 2008). It was impossible to open a custom mxd file by double clicking it. The file was loaded without the custom solvers, displaying a "naked" network.

With lots of debugging and experimenting, I finally "solved" the problem using the ApplicationInitialized event, and loading the file from here with IApplication.OpenDocument if the application was launched from a double click. The idea was to let the file be loaded with the default method, and then load it the right way by triggering our own implementation of IPersistStream.Load.

It worked fine at first, but with a particular file the load fails in ArcMap by double clicking it, displaying the following message: screenshot here

The file can be loaded from ArcMap with File>Open or the initial browsing panel, is recent and not corrupted.

Since the problem is happening right after our implementation of IExtension.Startup, I tried anything I could in this method but I finally understood it isn't supposed to contain file manipulations. Then I tried to force ArcMap to always open a new file on start, but it didn't work either.

The class loading the extension:

 public class OurExtension : IExtension, IExtensionConfig, IPersistVariant
 {
    [ComRegisterFunction()]
    [ComVisible(false)]
    static void RegisterFunction(Type registerType)
    {
        // Required for ArcGIS Component Category Registrar support
        ArcGISCategoryRegistration(registerType);
    }

    [ComUnregisterFunction()]
    [ComVisible(false)]
    static void UnregisterFunction(Type registerType)
    {
        // Required for ArcGIS Component Category Registrar support
        ArcGISCategoryUnregistration(registerType);
    }

   public void Shutdown()
    {
        //TODO: Clean up resources
        application = null;
    }

   public void Startup(ref object initializationData)
    {
        application = initializationData as IApplication;

        if (application == null)
        {
            return;
        }

        //Listening to document events
        SetUpDocumentEvent(application.Document);

        //Listening to the initialized status event of the application 
        (application as IApplicationStatusEvents_Event).Initialized += new IApplicationStatusEvents_InitializedEventHandler(ApplicationInitialized);

        //Language configuration
        // ....

    }

    public void Load(IVariantStream stream)
    {
        //TODO: Load persisted data from document stream
        Marshal.ReleaseComObject(stream);
    }

    public void Save(IVariantStream stream)
    {
        //TODO: Save extension related data to document stream
        Marshal.ReleaseComObject(stream);
    }



    #region EVENTS

    //Wiring.
    private ESRI.ArcGIS.ArcMapUI.IDocumentEvents_Event m_docEvents = null;

    private void SetUpDocumentEvent(ESRI.ArcGIS.Framework.IDocument myDocument)
    {
        m_docEvents = myDocument as ESRI.ArcGIS.ArcMapUI.IDocumentEvents_Event;
        m_docEvents.NewDocument += new ESRI.ArcGIS.ArcMapUI.IDocumentEvents_NewDocumentEventHandler(OnNewDocument);
        m_docEvents.BeforeCloseDocument += new ESRI.ArcGIS.ArcMapUI.IDocumentEvents_BeforeCloseDocumentEventHandler(onBeforeCloseDocument);
    }

    public void OnNewDocument()
    {
        if (!newDocumentCreated)
        {
            //Not listening to this event anymore ensures no error will come from this handler later on
            m_docEvents.NewDocument -= new ESRI.ArcGIS.ArcMapUI.IDocumentEvents_NewDocumentEventHandler(OnNewDocument);
        }

        //If a new document is created, the boolean must become true to avoid re-loading the file from this class
        newDocumentCreated = true;
    }

    public bool onBeforeCloseDocument()
    {
        //If the new document hasn't been created and the current one is being closed, it means it was loaded as a NetworkAnalyst document
        if (!newDocumentCreated)
        {
            //Setting the current document as clean before closing avoids the opening of the Save Dialog Box
            IDocumentDirty2 dirtyDocument = application.Document as IDocumentDirty2;
            dirtyDocument.SetClean();

            //Not listening to this event anymore ensures no error will come from this handler later on
            m_docEvents.BeforeCloseDocument -= new ESRI.ArcGIS.ArcMapUI.IDocumentEvents_BeforeCloseDocumentEventHandler(onBeforeCloseDocument);
        }

        return false;
    }

    public void ApplicationInitialized()
    {
        System.Diagnostics.Debug.WriteLine("ApplicationInitialized");

        //When the application is fully initialized, if a new document hasn't been created, then the double clicked file isn't loaded properly yet
        if (!newDocumentCreated)
        {
            //Retrieve the double clicked file path from the application parameters
            ESRI.ArcGIS.Framework.ITemplates templates = application.Templates;
            doubleClickedFilePath = templates.get_Item(templates.Count - 1);

            //If the file path is correct, a new document is created (without user input and with the default template), then the double clicked file is opened
            //IApplicationDocument.OpenDocument is the method triggering the event that leads to calling the MobiAnalyst implementation of IPersistStream.Load (in Persist.cs)
            if (File.Exists(doubleClickedFilePath))
            {
                application.NewDocument(false, templates.get_Item(0));

                application.OpenDocument(doubleClickedFilePath);
            }
        }

        //Not listening to this event anymore ensures no error will come from this handler later on
        (application as IApplicationStatusEvents_Event).Initialized -= new IApplicationStatusEvents_InitializedEventHandler(ApplicationInitialized);
    }
}

And a part of the Persist.cs file:

public partial class OurSolver : IOurSolver, INASolver, INASolverSettings2, IPersistStream
{
    public void GetClassID(out Guid classID)
    {
        classID = Type.GetTypeFromProgID("MobiAnalystSolverCOM.MobiAnalystSolver").GUID;
    }

    public int IsDirty()
    {
        if (PersistDirty)
            return 1;
        else
            return 0;
    }

    public void Load(IStream paramStream)
    {
        // Load properties into a property set
        IPropertySet properties = new PropertySetClass();
        (properties as IPersistStream).Load(paramStream);

        //Get back all loaded values into C# system arrays
        object propertyNames;
        object propertyValues;
        properties.GetAllProperties(out propertyNames, out propertyValues);
        System.Array propNameArray = (System.Array)propertyNames;
        System.Array propValuesArray = (System.Array)propertyValues;

        // Use this property set to populate variables
        //For each parameter, check existence first
        int index = System.Array.IndexOf(propNameArray, "m_eOutputConnectivity");
        if (index >= 0) { outputConnectivity = (OutputConnectivityEnum)(propValuesArray.GetValue(index)); }

        //And so forth
        // ...

    }   
}

So now that I have (I hope) clearly explained the context and the problem, my question is: is it possible to load a custom extension generated mxd with our own loading method right from the start, preventing ArcMap to load it any other way ? or to force it to launch from a double clicked file as it launches from the .exe ? Maybe even to correct the file if this is a known problem, but I couldn't find anything clear about it.

Best Answer

Ok, this is the most simplistic extension with persistence. Works as expected, see the comments.

[ComVisible(true)]
[ProgId("Test.TestExtension")]
[Guid("FD5E7EA9-DEFC-4AA5-81C8-A378BFA4D506")]
[ClassInterface(ClassInterfaceType.None)]
public class TestExtension : IExtension, IPersistVariant
{
    private IApplication _application;

    public void Startup(ref object initializationData)
    {
        _application = (IApplication)initializationData;
    }

    public void Shutdown()
    {
        _application = null;
    }

    public string Name
    {
        get { return GetType().FullName; }
    }

    public void Load(IVariantStream stream)
    {
        // when a document, which was previously saved with this extension (i.e. IPersistVariant.Save implemented
        // here was called with that document), is opened, this method gets called

        // it does not matter whether the document was opened from within ArcMap or by double-clicking it in the shell
    }

    public void Save(IVariantStream stream)
    {
        // this method gets called whenever a document is saved
    }

    public UID ID
    {
        get { return new UIDClass {Value = GetType().GUID.ToString("B")}; }
    }

    #region COM Registration Function(s)

    [ComRegisterFunction]
    [ComVisible(false)]
    static void RegisterFunction(Type registerType)
    {
        var regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
        ESRI.ArcGIS.ADF.CATIDs.MxExtension.Register(regKey);
    }

    [ComUnregisterFunction]
    [ComVisible(false)]
    static void UnregisterFunction(Type registerType)
    {
        var regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
        ESRI.ArcGIS.ADF.CATIDs.MxExtension.Unregister(regKey);
    }

    #endregion
}
Related Question