[C#, VB.net CONNECT Update 13] CreateDesignFile method causes an exception

Gents,

 I’m looking for solve an issue that occur in the following conditions:

  • a WPF Microstation Connect AddIn developed in C# is running
  • an external application (developed in vb.net) call the method Application.CreateDesignFile as follows:

 

Dgn = MstnApp.CreateDesignFile(Template, Filename, OpenDgn)

 

the error message is shown 6 times in the Microstation Text Window and it is the following:

OS could not load C:\Program Files\Bentley\MicroStation CONNECT Edition\MicroStation\mdlsys\asneeded\helpload.dll, error 126.

MDL Loader: Unable to load library (DLL or MDL shared library) helpload

 

Note that this error does not occur if the AddIn is not loaded.

The AddIn has been developed starting for the example WPFDemo.

 I’m using Microstation Connect Update 13, version 10.13.01.01

The AddIn and external application are developed using VS2019, version 16.8.3, target framework .Net framework 4.7.2.

 

Can someone heap me?

Many thanks

 

This is the function which creates a new design file and it does not catch any exception.

Public Function Mstn_CreateDgn(Template As String,
                                 Filename As String, Optional _
                                 OpenDgn As Boolean = True) As Integer
    If MstnApp.HasActiveDesignFile Then
      If String.Compare(MstnApp.ActiveDesignFile.FullName, Filename, True) = 0 Then
        MstnApp.ActiveDesignFile.Close()
      End If
    End If

    Try
      If IO.File.Exists(Filename) Then
        IO.File.Delete(Filename)
      End If
      Dgn = MstnApp.CreateDesignFile(Template, Filename, OpenDgn)
      Return 1

    Catch ex As Exception
      Return 0

    End Try

  End Function

  • This is the function which creates a new design file and it does not catch any exception

    Your .exe runs and completes successfully?

    I suggest that you divide and conquer!  For diagnostic purposes, divide your .exe into three, and test each part separately...

    Test ActiveDesignFile.Close

        If MstnApp.HasActiveDesignFile Then
          If String.Compare(MstnApp.ActiveDesignFile.FullName, Filename, True) = 0 Then
            MstnApp.ActiveDesignFile.Close()
          End If
        End If
    

    Test IO.File.Delete(Filename)

     If IO.File.Exists(Filename) Then
            IO.File.Delete(Filename)
          End If

    Test CreateDesignFile

    Dgn = MstnApp.CreateDesignFile(Template, Filename, OpenDgn)

     
    Regards, Jon Summers
    LA Solutions

  • Hi, Jan,

    Yes, the application written in vb.net is working without problems if the AddIn is not loaded. 
    I tested the three steps as you suggested with the AddIn loaded. The issue arises when the application calls the CreateDesignFile method, while no problems occurred when ActiveDesignFile.Close method is called and when the existing file is deleted. 
    The vb.net application runs successfully also when the issue is raised. In some cases, the vb.net application activates the AddIn to perform some works and also this processing is completed successfully. However, after several runs, some Microstation tools, like "Properties" raise an exception.

    Can this issue related to MicroStation Connect version, or a specific setting is required during Connect installation in order to avoid similar issues?

    the AddIn is configured to use x64 and during the compilation a warning appears because microstation is configured to use AMD64. Can this different configuration cause this issue?

    Thanks in advance

  • The issue arises when the application calls the CreateDesignFile method
    the AddIn is configured

    You may need to add some state handlers to your addin.  There are several .NET handlers that call your AddIn when certain events happen. For example...

    protected override int Run(string[] commandLine)
    {
        //  Application initialization code
        RegisterEventHandlers();
        ...
        return 0;
    }
    

    private void RegisterEventHandlers()
    {
        ReloadEvent += YourApp_ReloadEvent;
        UnloadedEvent += YourApp_UnloadedEvent;
        Session.Instance.OnMasterFileStart += OnMasterFileStart;
        Session.Instance.OnModelRefActivate += OnModelActivate;
    }
    

    Write those handlers and include a message in each, so you can see when they are called. 

     
    Regards, Jon Summers
    LA Solutions

  • Hi, Jan,

    first of all, thank you for your support. 

    The issue arises after the event OnModelRefActivated and before the event OnMasterFileStart. 
    However, during last debugging sessions, I noted some anomalies related to Microstation start. 
    The diagnostic shows among others the following messages:

    Assembly resolver failed to load assembly: "Bentley.DrawingScale.resources"

    Assembly resolver failed to load assembly: "DgnDisplayCSUI2.resources"

    Assembly resolver failed to load assembly: "Bentley.Visualization.LightsRibbonToolbar.resources"

    Assembly resolver failed to load assembly: "Bentley.ViewGroupHistory.resources"

    Assembly resolver failed to load assembly: "Bentley.ViewGroupList.resources"

    After these messages, Microstation apparently work correctly, but I cannot be sure.

     

    Maybe, before continuing the issue analysis, it would be better to reinstall Microstation Connect and verify if the above anomalies disappear.

  • The issue arises after the event OnModelRefActivated and before the event OnMasterFileStart

    Your code should not attempt anything until you receive the OnMasterFileStart message.  Call your startup code from that handler.

    If you start your AddIn using mdl load MyApp, then MicroStation has already started and the DGN file is open and stable.  In that case you can call your startup code in the Run method as you do now.

    protected override int Run (string[] commandLine)
    {
        // save a reference to our addin to prevent it from being garbage collected.
        s_AddIn = this;
    
        // Get the localized resources
        s_ResourceManager = Properties.Resources.ResourceManager;
    
        switch (this.ApplicationType)
        {
    	case MdlApplicationType.User:
    	    // Now it's safe to do things like this...
    	    UpdateGuids(Session.Instance.GetActiveDgnFile());
    	    Session.Instance.Keyin("MYAPP DIALOG OPEN");
    	    break;
        }
        return 0;
    }
    /// Called when a DGN file has been opened.  
    /// Equivalent to MicroStationAPI OnNewDesignFile.
    /// param name="dgnFile" The DGN file just opened.
    private static void OnMasterFileStart(DgnFile dgnFile)
    {
        string s = null;
        switch (s_AddIn.ApplicationType)
        {
    	case MdlApplicationType.DesignApp:
    	    s = $"Opening DGN file '{dgnFile.GetFileName()}' as DGNAPP";
    	    break;
    	case MdlApplicationType.User:
    	    s = $"Opening DGN file '{dgnFile.GetFileName()}' as USER";
    	    break;
    	case MdlApplicationType.InitApp:
    	    s = $"Opening DGN file '{dgnFile.GetFileName()}' as INITAPP";
    	    break;
        }
        //  Enable debug messages in MicroStation's Message Center to see MessageType.Debug
        MessageCenter.Instance.ShowMessage(MessageType.Debug, s, s, MessageAlert.None);
        UpdateGuids(dgnFile);
        AddIn.MicroStation.UI.MainPage.OpenWindow(null);
    }
    

     
    Regards, Jon Summers
    LA Solutions

  • The addin should do nothing when a drawing is closed, and another drawing is opened. I updated the code as you suggested but unfortunately the problem is still present.

    The addin has the command table and the external app (the vb.net app) runs the addin by simulating the user commands:  

     

    'AddIn loading

        MstnApp.CadInputQueue.SendCommand($"MDL LOAD ""{AppInfo.ExePath}Connect\MstnDrawAddIn"" ""{AppInfo.IniPath}""")

     'send draw sheet command

        MstnApp.CadInputQueue.SendCommand($"SHEET ""{Job.Name}"" ""{AppInfo.InputFile}""")

     

    When the addin starts for the first time no exception occurs. The addin performs the work and remains in idle condition.

    Then, the external app can select another drawing and repeat the process and, in such case, the exception occurs.

     

    Definitely, I need to verify the Microstation installation (carried out through an automatic batch script). My impression is that something is not working correctly.

  • Hi Jan,

    after many tests, hopefully I have identified the true source of the exception: the exception depends completely on the AddIn and not by the external application.

    The exception is raised under the following circumstances:

    • The user runs the addin (through “mdl load MstnRouteAddIn”)
    • The user closes the active design file
    • The user opens another design file

     Instead, the exception does not raise if the user changes the design file (because the addin is not unloaded).

     This AddIn uses an assembly named Sealib, which uses several other assemblies including a GDAL plugin available at https://www.nuget.org/packages/GDAL/2.4.4?_src=template

    Here there is the addin source code.

    /*--------------------------------------------------------------------------------------+
    |       ROUTING ADDIN
    +--------------------------------------------------------------------------------------*/
    using System;
    using System.Globalization;
    using System.Threading;
    using System.Resources;
    using System.Windows;
    using System.Windows.Threading;
    using Bentley.DgnPlatformNET;
    using Bentley.MstnPlatformNET;
    using SeaLib.Core;
    using SeaLib.Routing;
    
    namespace MstnRouteAddIn
        {
        /*====================================================================================**/
        ///
        /// <summary>Singleton AddIn class</summary>
        ///
        /*==============+===============+===============+===============+===============+======*/
        [AddInAttribute(MdlTaskID = "RouteAddIn")]
        public sealed class RouteApp : AddIn
            {
            private static RouteApp s_AddIn = null;
            private static ResourceManager s_ResourceManager;
            private static RoutingApplication appInfo = null;
            private static CRoutingLogin logingInfo = null;
    
            /*------------------------------------------------------------------------------------**/
            /// <summary>Constructor for the AddIn</summary>
            /*--------------+---------------+---------------+---------------+---------------+------*/
            private RouteApp (IntPtr mdlDesc) : base(mdlDesc) 
                {
                s_AddIn = this;
                }
    
            /*------------------------------------------------------------------------------------**/
            /// <summary>The Run method</summary>
            /*--------------+---------------+---------------+---------------+---------------+------*/
            protected override int Run
            (
            string[] commandLine
            )
                {
                // save a reference to our addin to prevent it from being garbage collected.
                s_AddIn = this;
    
                // register event handler
                RegisterEventHandlers();
    
                // Get the localized resources
                s_ResourceManager = Properties.Resources.ResourceManager;
    
                switch (this.ApplicationType)
                {
                    case MdlApplicationType.User:
                        // Now it's safe to do things like this...
    
                        // Initialization
                        InitializeRouting();
    
                        // open route Toolbar
                        string unparsed = "";
                        RouteKeyins.OpenRoute(unparsed);
                        break;
                }
                return 0;
                }
    
            /*------------------------------------------------------------------------------------**/
            /// <summary>Static method to get the AddIn</summary>
            /*--------------+---------------+---------------+---------------+---------------+------*/
            internal static RouteApp Instance
                {
                get { return s_AddIn; }
                }
    
            /*------------------------------------------------------------------------------------**/
            /// <summary>Static method to get the ResourceManager</summary>
            /*--------------+---------------+---------------+---------------+---------------+------*/
            internal static ResourceManager ResourceManager
                {
                get { return s_ResourceManager; }
                }
    
            internal static RoutingApplication RoutingApplication
            {
                get { return appInfo; }
            }
            internal static CRoutingLogin CRoutingLogin
            {
                get { return logingInfo; }
            }
            bool InitializeRouting()
            {
                //Initialize ApplicationInfo (static class in namespace Sealib.Core)
                ApplicationInfo.Initialize(1);
    
                //Configure GDAL (partial static class in the namespace Sealib.Routing)
                // This will raise an exception if:
                // - this AddIn is unloaded when a design file is closed
                // - a new design file is opened
                GdalConfiguration.ConfigureGdal();
                if (!GdalConfiguration.Usable)
                    throw new InvalidOperationException("GDAL unavailable");
    
                // Tested. Do not raise exception
                appInfo = new RoutingApplication();
                appInfo.Initialize();
                logingInfo = new CRoutingLogin();
                logingInfo.Initialize(appInfo);
                return true;
            }
    
            #region "Event Handlers"
            /*------------------------------------------------------------------------------------**/
            /// <summary>Static method to get the ResourceManager</summary>
            /*--------------+---------------+---------------+---------------+---------------+------*/
            private void RegisterEventHandlers()
            {
                ReloadEvent += ThisApp_ReloadEvent;
                UnloadedEvent += ThisApp_UnloadedEvent;
                Session.Instance.OnMasterFileStop += OnMasterFileStop;
                Session.Instance.OnMasterFileStart += OnMasterFileStart;
                Session.Instance.OnMasterFileChanging += OnMasterFileChaning;
                Session.Instance.OnModelRefActivated += OnModelActivated;
            }
    
            public void ThisApp_ReloadEvent(AddIn sender, ReloadEventArgs eventArgs)
            {
                System.Diagnostics.Trace.WriteLine($"Reload event {sender.MdlTaskId}");
            }
    
            public void ThisApp_UnloadedEvent(AddIn sender, UnloadedEventArgs eventArgs)
            {
                System.Diagnostics.Trace.WriteLine($"Unloaded Event {sender.MdlTaskId}");
            }
    
            public void OnMasterFileChaning(DgnFile dgnfile)
            {
                // for debugging: FileChanging does not raise any exception
                string s = $"OnMasterFileChaning {dgnfile.GetFileName()}";
                MessageCenter.Instance.ShowMessage(MessageType.Info, s, s, MessageAlert.None);
                System.Diagnostics.Trace.WriteLine(s);
            }
            public void OnMasterFileStop(DgnFile dgnfile)
            {
                string s = $"OnMasterFileStop {dgnfile.GetFileName()}";
                MessageCenter.Instance.ShowMessage(MessageType.Info, s, s, MessageAlert.None);
                System.Diagnostics.Trace.WriteLine(s);
            }
    
            public void OnMasterFileStart(DgnFile dgnFile)
            {
                string s = null;
                switch (s_AddIn.ApplicationType)
                {
                    case MdlApplicationType.DesignApp:
                        s = $"Opening DGN file '{dgnFile.GetFileName()}' as DGNAPP";
                        break;
                    case MdlApplicationType.User:
                        s = $"Opening DGN file '{dgnFile.GetFileName()}' as USER";
                        break;
                    case MdlApplicationType.InitApp:
                        s = $"Opening DGN file '{dgnFile.GetFileName()}' as INITAPP";
                        break;
                }
                //  Enable debug messages in MicroStation's Message Center to see MessageType.Debug
                MessageCenter.Instance.ShowMessage(MessageType.Debug, s, s, MessageAlert.None);
                System.Diagnostics.Trace.WriteLine(s);
            }
            public void OnModelActivated(DgnModelRef newModelRef, DgnModelRef oldModelRef)
            {
                System.Diagnostics.Trace.WriteLine($"OnModelActivated {newModelRef.GetDgnFile().GetFileName()}");
            }
            #endregion
        }
    }   
    

    GDAL plugin require a configuration before use. The assembly Sealib is written in vb and the GDAL configuration is setup using this class.

    '******************************************************************************
    '*
    '* Name:     GdalConfiguration.vb.pp
    '* Project:  GDAL VB.NET Interface
    '* Purpose:  A static configuration utility class to enable GDAL/OGR.
    '* Author:   Felix Obermaier
    '*
    '******************************************************************************
    '* Copyright (c) 2012-2018, Felix Obermaier
    '*
    '* Permission is hereby granted, free of charge, to any person obtaining a
    '* copy of this software and associated documentation files (the "Software"),
    '* to deal in the Software without restriction, including without limitation
    '* the rights to use, copy, modify, merge, publish, distribute, sublicense,
    '* and/or sell copies of the Software, and to permit persons to whom the
    '* Software is furnished to do so, subject to the following conditions:
    '*
    '* The above copyright notice and this permission notice shall be included
    '* in all copies or substantial portions of the Software.
    '*
    '* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    '* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    '* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    '* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    '* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    '* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    '* DEALINGS IN THE SOFTWARE.
    '*****************************************************************************/
    
    Option Infer On
    
    Imports System.IO
    Imports System.Reflection
    Imports System.Runtime.InteropServices
    Imports Gdal = OSGeo.GDAL.Gdal
    Imports Ogr = OSGeo.OGR.Ogr
    
    Namespace Routing
      ''' <summary>
      ''' Configuration class for GDAL/OGR
      ''' </summary>
      Partial Public Class GdalConfiguration
        Private Shared _configuredOgr As Boolean
        Private Shared _configuredGdal As Boolean
        Private Shared _usable As Boolean
    
        <DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
        Public Shared Function SetDefaultDllDirectories(directoryFlags As UInteger) As Boolean
    
        End Function
    
        '    LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_SYSTEM32
        Private Const DllSearchFlags As UInteger = &H400 Or &H800
    
        <DllImport("kernel32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)>
        Public Shared Function AddDllDirectory(lpPathName As String) As <MarshalAs(UnmanagedType.Bool)> Boolean
    
        End Function
    
        ''' <summary>
        ''' Construction of Gdal/Ogr
        ''' </summary>
        Shared Sub New()
          Dim nativePath As String = Nothing
          Dim executingDirectory As String = Nothing
          Dim gdalPath As String = Nothing
          Try
            If Not IsWindows Then
              Const notSet As String = "_Not_set_"
              Dim tmp As String = Gdal.GetConfigOption("GDAL_DATA", notSet)
              _usable = (tmp <> notSet)
              Return
            End If
    
            Dim executingAssemblyFile As String = New Uri(Assembly.GetExecutingAssembly.GetName.CodeBase).LocalPath
            executingDirectory = Path.GetDirectoryName(executingAssemblyFile)
            If String.IsNullOrEmpty(executingDirectory) Then
              Throw New InvalidOperationException("cannot get executing directory")
            End If
    
            'la cartella gdal potrebbe essere una sottocartella dell'eseguibile
            'oppure trovarsi anche in una cartella allo stesso livello delle executingfolder
            Dim parentExecuting As String = Path.GetDirectoryName(executingDirectory)
            Dim parentGDal As String = executingDirectory
            'cerco gdal nella executing e nella parentExecutin
            If Not Directory.Exists(Path.Combine(executingDirectory, "gdal")) Then
              If Directory.Exists(Path.Combine(parentExecuting, "gdal")) Then
                parentGDal = parentExecuting
              End If
            End If
    
            SetDefaultDllDirectories(DllSearchFlags)
            gdalPath = Path.Combine(parentGDal, "gdal")
            nativePath = Path.Combine(gdalPath, GetPlatform())
            If Not Directory.Exists(nativePath) Then
              Throw New DirectoryNotFoundException($"GDAL native directory not found at '{nativePath}'")
            End If
    
            If Not File.Exists(Path.Combine(nativePath, "gdal_wrap.dll")) Then
              Throw New FileNotFoundException($"GDAL native wrapper file not found at '{Path.Combine(nativePath, ", gdal_wrap.dll, ")}'")
            End If
    
            AddDllDirectory(nativePath)
            AddDllDirectory(Path.Combine(nativePath, "plugins"))
            ' Set the additional GDAL environment variables.
            Dim gdalData As String = Path.Combine(gdalPath, "data")
            Environment.SetEnvironmentVariable("GDAL_DATA", gdalData)
            Gdal.SetConfigOption("GDAL_DATA", gdalData)
            Dim driverPath As String = Path.Combine(nativePath, "plugins")
            Environment.SetEnvironmentVariable("GDAL_DRIVER_PATH", driverPath)
            Gdal.SetConfigOption("GDAL_DRIVER_PATH", driverPath)
            Environment.SetEnvironmentVariable("GEOTIFF_CSV", gdalData)
            Gdal.SetConfigOption("GEOTIFF_CSV", gdalData)
            Dim projSharePath As String = Path.Combine(gdalPath, "share")
            Environment.SetEnvironmentVariable("PROJ_LIB", projSharePath)
            Gdal.SetConfigOption("PROJ_LIB", projSharePath)
            _usable = True
          Catch e As Exception
            _usable = False
            Trace.WriteLine(e, "error")
            Trace.WriteLine($"Executing directory: {executingDirectory}", "error")
            Trace.WriteLine($"gdal directory: {gdalPath}", "error")
            Trace.WriteLine($"native directory: {nativePath}", "error")
            'throw;
          End Try
    
        End Sub
    
        ''' <summary>
        ''' Gets a value indicating if the GDAL package is set up properly.
        ''' </summary>
        Public Shared ReadOnly Property Usable As Boolean
          Get
            Return _usable
          End Get
        End Property
    
        ''' <summary>
        ''' Method to ensure the static constructor is being called.
        ''' </summary>
        ''' <remarks>Be sure to call this function before using Gdal/Ogr/Osr</remarks>
        Public Shared Sub ConfigureOgr()
          If Not _usable Then
            Return
          End If
    
          If _configuredOgr Then
            Return
          End If
    
          ' Register drivers
          Ogr.RegisterAll()
          _configuredOgr = True
          PrintDriversOgr()
        End Sub
    
        ''' <summary>
        ''' Method to ensure the static constructor is being called.
        ''' </summary>
        ''' <remarks>Be sure to call this function before using Gdal/Ogr/Osr</remarks>
        Public Shared Sub ConfigureGdal()
          If Not _usable Then
            Return
          End If
    
          If _configuredGdal Then
            Return
          End If
    
          ' Register drivers
          Gdal.AllRegister()
          _configuredGdal = True
          PrintDriversGdal()
        End Sub
    
        ''' <summary>
        ''' Function to determine which platform we're on
        ''' </summary>
        Private Shared Function GetPlatform() As String
          If (Environment.Is64BitProcess) Then Return "x64"
          Return "x86"
        End Function
    
        ''' <summary>
        ''' Gets a value indicating if we are on a windows platform
        ''' </summary>
        Private Shared ReadOnly Property IsWindows As Boolean
          Get
            Dim res = Not ((Environment.OSVersion.Platform = PlatformID.Unix) _
                                OrElse (Environment.OSVersion.Platform = PlatformID.MacOSX))
            Return res
          End Get
        End Property
    
        Private Shared Sub PrintDriversOgr()
    #If (DEBUG) Then
          If _usable Then
            Dim num = Ogr.GetDriverCount
            Dim i = 0
            Do While (i < num)
              Dim driver = Ogr.GetDriver(i)
              Trace.WriteLine($"OGR {i}: {driver.GetName()}", "Debug")
              i = (i + 1)
            Loop
    
          End If
    
    #End If
        End Sub
    
        Private Shared Sub PrintDriversGdal()
    #If (DEBUG) Then
          If _usable Then
            Dim num = Gdal.GetDriverCount
            Dim i = 0
            Do While (i < num)
              Dim driver = Gdal.GetDriver(i)
              Trace.WriteLine($"GDAL {i}: {driver.ShortName}-{driver.LongName}")
              i = (i + 1)
            Loop
    
          End If
    
    #End If
        End Sub
      End Class
    End Namespace

    There also the corresponding version of this class, written in c#

    /******************************************************************************
     *
     * Name:     GdalConfiguration.cs.pp
     * Project:  GDAL CSharp Interface
     * Purpose:  A static configuration utility class to enable GDAL/OGR.
     * Author:   Felix Obermaier
     *
     ******************************************************************************
     * Copyright (c) 2012-2018, Felix Obermaier
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included
     * in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     * DEALINGS IN THE SOFTWARE.
     *****************************************************************************/
    
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using Gdal = OSGeo.GDAL.Gdal;
    using Ogr = OSGeo.OGR.Ogr;
    
    namespace SeaLib.Routing.Tests
    {
        public static partial class GdalConfiguration
        {
            private static volatile bool _configuredOgr;
            private static volatile bool _configuredGdal;
            private static volatile bool _usable;
    
            [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
            static extern bool SetDefaultDllDirectories(uint directoryFlags);
            //               LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_SYSTEM32
            private const uint DllSearchFlags = 0x00000400 | 0x00000800;
    
            [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            static extern bool AddDllDirectory(string lpPathName);
    
            /// <summary>
            /// Construction of Gdal/Ogr
            /// </summary>
            static GdalConfiguration()
            {
                string executingDirectory = null, gdalPath = null, nativePath = null;
                try
                {
                    if (!IsWindows)
                    {
                        const string notSet = "_Not_set_";
                        string tmp = Gdal.GetConfigOption("GDAL_DATA", notSet);
                        _usable = tmp != notSet;
                        return;
                    }
    
                    string executingAssemblyFile = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath;
                    executingDirectory = Path.GetDirectoryName(executingAssemblyFile);
    
                    if (string.IsNullOrEmpty(executingDirectory))
                        throw new InvalidOperationException("cannot get executing directory");
    
    
                    // modify search place and order
                    SetDefaultDllDirectories(DllSearchFlags);
    
                    gdalPath = Path.Combine(executingDirectory, "gdal");
                    nativePath = Path.Combine(gdalPath, GetPlatform());
                    if (!Directory.Exists(nativePath))
                        throw new DirectoryNotFoundException($"GDAL native directory not found at '{nativePath}'");
                    if (!File.Exists(Path.Combine(nativePath, "gdal_wrap.dll")))
                        throw new FileNotFoundException(
                            $"GDAL native wrapper file not found at '{Path.Combine(nativePath, "gdal_wrap.dll")}'");
    
                    // Add directories
                    AddDllDirectory(nativePath);
                    AddDllDirectory(Path.Combine(nativePath, "plugins"));
    
                    // Set the additional GDAL environment variables.
                    string gdalData = Path.Combine(gdalPath, "data");
                    Environment.SetEnvironmentVariable("GDAL_DATA", gdalData);
                    Gdal.SetConfigOption("GDAL_DATA", gdalData);
    
                    string driverPath = Path.Combine(nativePath, "plugins");
                    Environment.SetEnvironmentVariable("GDAL_DRIVER_PATH", driverPath);
                    Gdal.SetConfigOption("GDAL_DRIVER_PATH", driverPath);
    
                    Environment.SetEnvironmentVariable("GEOTIFF_CSV", gdalData);
                    Gdal.SetConfigOption("GEOTIFF_CSV", gdalData);
    
                    string projSharePath = Path.Combine(gdalPath, "share");
                    Environment.SetEnvironmentVariable("PROJ_LIB", projSharePath);
                    Gdal.SetConfigOption("PROJ_LIB", projSharePath);
    
                    _usable = true;
                }
                catch (Exception e)
                {
                    _usable = false;
                    Trace.WriteLine(e, "error");
                    Trace.WriteLine($"Executing directory: {executingDirectory}", "error");
                    Trace.WriteLine($"gdal directory: {gdalPath}", "error");
                    Trace.WriteLine($"native directory: {nativePath}", "error");
    
                    //throw;
                }
            }
    
            /// <summary>
            /// Gets a value indicating if the GDAL package is set up properly.
            /// </summary>
            public static bool Usable
            {
                get { return _usable; }
            }
    
            /// <summary>
            /// Method to ensure the static constructor is being called.
            /// </summary>
            /// <remarks>Be sure to call this function before using Gdal/Ogr/Osr</remarks>
            public static void ConfigureOgr()
            {
                if (!_usable) return;
                if (_configuredOgr) return;
    
                // Register drivers
                Ogr.RegisterAll();
                _configuredOgr = true;
    
                PrintDriversOgr();
            }
    
            /// <summary>
            /// Method to ensure the static constructor is being called.
            /// </summary>
            /// <remarks>Be sure to call this function before using Gdal/Ogr/Osr</remarks>
            public static void ConfigureGdal()
            {
                if (!_usable) return;
                if (_configuredGdal) return;
    
                // Register drivers
                Gdal.AllRegister();
                _configuredGdal = true;
    
                PrintDriversGdal();
            }
    
    
            /// <summary>
            /// Function to determine which platform we're on
            /// </summary>
            private static string GetPlatform()
            {
                return Environment.Is64BitProcess ? "x64" : "x86";
            }
    
            /// <summary>
            /// Gets a value indicating if we are on a windows platform
            /// </summary>
            private static bool IsWindows
            {
                get
                {
                    var res = !(Environment.OSVersion.Platform == PlatformID.Unix ||
                                Environment.OSVersion.Platform == PlatformID.MacOSX);
    
                    return res;
                }
            }
            private static void PrintDriversOgr()
            {
    #if DEBUG
                if (_usable)
                {
                    var num = Ogr.GetDriverCount();
                    for (var i = 0; i < num; i++)
                    {
                        var driver = Ogr.GetDriver(i);
                        Trace.WriteLine($"OGR {i}: {driver.GetName()}", "Debug");
                    }
                }
    #endif
            }
    
            private static void PrintDriversGdal()
            {
    #if DEBUG
                if (_usable)
                {
                    var num = Gdal.GetDriverCount();
                    for (var i = 0; i < num; i++)
                    {
                        var driver = Gdal.GetDriver(i);
                        Trace.WriteLine($"GDAL {i}: {driver.ShortName}-{driver.LongName}");
                    }
                }
    #endif
            }
        }
    }

    I went through GDAL configuration static class, but I couldn’t find anything that should cause an exception. May the calls SetDefaultDllDirectories or AddDllDirectory (in kernel32.dll) alter something in the Microstation environment?

    thanks 

  • Hi Paolo,

    thanks for the details Thumbsup

    I read again the whole conversation and right now I am not sure whether I understood the situation right, because:

    • When MicroStation addin is loaded, its entry point is a class, derived from Addin (RouteApp in your case). Such application has full access to both Interop and new NET APIs.
    • When external application access MicroStation, it's done (typically) using COM, in NET code through Interop library that encapsulate COM. In this case no Addin class is involved. It's not clear whether your VB.NET code is this case.

    How these two pieces of the code, that run in separate processes (addin shares MicroStation process, external app is another process) communicates?

    Instead, the exception does not raise if the user changes the design file (because the addin is not unloaded).

    Yes, it's correct. When design file is closed, addins have to be loaded again.

    A standard solution, when particular addin should be loaded automatically (and the application is not loaded through key-in), is to configure MicroStation to load it, e.g. through MS_DGNAPPS variable.

    With regards,

      Jan

  • Hi Jon, Jan,

    Thanks for your support once again.

    Initially, I thought that the exception is caused when an external application applies the method CreateDesignFile, but after many tests, I understood that this not the root source of error.

    The root source of error appears to be entirely in the AddIn and more specifically related to the static class necessary to configure GDAL plugin.

    Currently this AddIn opens a tollbar and does not do anything else. If GDAL configuration class is called, the exception raises, otherwise not.

    Moreover, note that the use of other classes ApplicationInfo (static class), RoutingApplication and CRoutingLogin belonging to the same assembly SeaLib do not raise exceptions.

    The classes ApplicationInfo and GDAL configuration look like similar for some elements, so I cannot understand why the GDAL configuration class raises the exception.

    Here you can find the ApplicationInfo class.

    Option Explicit On
    Option Strict On
    Imports System.Reflection
    Imports System.IO
    
    Namespace Core
    
      Public Class ApplicationInfo
        ''' <summary>
        ''' simple shared class for basic info about an application avoiding the use of VB specific objects
        ''' </summary>
        Private Shared NotInitialized As Boolean = True
        Private Shared _Name As String = ""
        Private Shared _ProductName As String = ""
        Private Shared _ExePath As String = ""
        Private Shared _Description As String = ""
        Private Shared _Title As String = ""
        Private Shared _Version As String = ""
        Private Shared _VersionBuilt As String = ""
        Private Shared _TradeMark As String = ""
        Private Shared _CompanyName As String = ""
    
        Public Shared ReadOnly Property Name() As String
          Get
            If NotInitialized Then
              Throw New Exception("ApplicationInfo not initialized")
            End If
            Return _Name
          End Get
        End Property
    
        Public Shared ReadOnly Property ExePath As String
          Get
            If NotInitialized Then
              Throw New Exception("ApplicationInfo not initialized")
            End If
            Return _ExePath
          End Get
        End Property
        Public Shared ReadOnly Property Description As String
          Get
            If NotInitialized Then
              Throw New Exception("ApplicationInfo not initialized")
            End If
            Return _Description
          End Get
        End Property
    
        Public Shared ReadOnly Property ProductName As String
          Get
            If NotInitialized Then
              Throw New Exception("ApplicationInfo not initialized")
            End If
            Return _ProductName
          End Get
        End Property
        Public Shared ReadOnly Property Title As String
          Get
            If NotInitialized Then
              Throw New Exception("ApplicationInfo not initialized")
            End If
            Return _Title
          End Get
        End Property
    
        Public Shared ReadOnly Property TradeMark As String
          Get
            If NotInitialized Then
              Throw New Exception("ApplicationInfo not initialized")
            End If
            Return _TradeMark
          End Get
        End Property
    
        Public Shared ReadOnly Property CompanyName As String
          Get
            If NotInitialized Then
              Throw New Exception("ApplicationInfo not initialized")
            End If
            Return _CompanyName
          End Get
        End Property
    
        Public Shared ReadOnly Property Version As String
          Get
            If NotInitialized Then
              Throw New Exception("ApplicationInfo not initialized")
            End If
            Return _Version
          End Get
        End Property
        Public Shared ReadOnly Property VersionBuilt As String
          Get
            If NotInitialized Then
              Throw New Exception("ApplicationInfo not initialized")
            End If
            Return _VersionBuilt
          End Get
        End Property
    
        ''' <summary>
        ''' Initializes this class. this method shall be called before any other request
        ''' </summary>
        ''' <param name="EntryOrCalling">defines if the info shall be recovered by the main application or from the addin</param>
        Public Shared Sub Initialize(EntryOrCalling As Integer)
    
          NotInitialized = False
    
          Dim asm As Assembly
    
          If EntryOrCalling = 0 Then
            asm = Assembly.GetEntryAssembly
          Else
            asm = Assembly.GetCallingAssembly
          End If
    
          _Name = Path.GetFileNameWithoutExtension(Application.ExecutablePath)
          _Name = asm.GetName.Name
    
          Dim w = asm.GetName.Version
          _Version = w.Major & "." & w.Minor
          _VersionBuilt = Version & "." & w.Revision
    
          'Executing path
          Dim executingAssemblyFile = New Uri(asm.GetName().CodeBase).LocalPath
          _ExePath = Path.GetDirectoryName(executingAssemblyFile)
    
          Try
            _ProductName = Application.ProductName
            Dim attr = DirectCast(asm.GetCustomAttribute(GetType(AssemblyProductAttribute)), AssemblyProductAttribute)
            _ProductName = attr.Product
          Catch ex As Exception
          End Try
    
          'Application Title
          Try
            _Title = Application.ProductName
            Dim attrTitle = asm.GetCustomAttribute(GetType(AssemblyTitleAttribute))
            If attrTitle IsNot Nothing Then
              _Title = DirectCast(attrTitle, AssemblyTitleAttribute).Title
            End If
          Catch ex As Exception
          End Try
    
          'Application Description 
          _Description = ""
          Try
            Dim attrDescr = asm.GetCustomAttribute(GetType(AssemblyDescriptionAttribute))
            If attrDescr IsNot Nothing Then
              _Description = DirectCast(attrDescr, AssemblyDescriptionAttribute).Description
            End If
          Catch ex As Exception
          End Try
    
          'Company 
          _CompanyName = Application.CompanyName
          Try
            Dim attrCompany = asm.GetCustomAttribute(GetType(AssemblyCompanyAttribute))
            If attrCompany IsNot Nothing Then
              _CompanyName = DirectCast(attrCompany, AssemblyCompanyAttribute).Company
            End If
          Catch ex As Exception
          End Try
    
          'Trademark
          _TradeMark = ""
          Try
            Dim attrTradeMark = asm.GetCustomAttribute(GetType(AssemblyTrademarkAttribute))
            If attrTradeMark IsNot Nothing Then
              _TradeMark = DirectCast(attrTradeMark, AssemblyTrademarkAttribute).Trademark
            End If
    
          Catch ex As Exception
          End Try
    
        End Sub
      End Class
    End Namespace