none
Using App.Config to Point to Different Version of Assembly RRS feed

  • Question

  • I have a Windows application using older versions of some Microsoft DLLs.  I had to use a newer version for one project only.  I am not at liberty to change the entire application, at this point.

    I used codeBase in my App.Config, and as long as I point to the entire application and assembly path, it works.  

         <codeBase version="15.0.0.0" href="file:///C:/Builds/CAD//Microsoft/Version  15_0_0_0/Microsoft.ReportViewer.Winforms.DLL"/>

    However, I need to use relative pathing to the application folder, which can vary from client to client.  I tried all the .\, ..\, ~\, etc., but nothing works.  Can't find the assemblies.  I suspect the base folder is not my application folder, but rather C:\Windows\System32.

    I also tried Probing

         <probing privatePath="Microsoft\Version 14_0_0_0;Microsoft\Version 15_0_0_0"/>

    But, per FusLogVw.Exe, keeps trying to apply the older DLL versions, which happen to be in the application root folder.     

         LOG: Assembly Name is: Microsoft.ReportViewer.WinForms, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91
    

    Note: I'm after version 15, but Probing tried version 11.

    I would rather not fix this in code, as config files are more easily adaptable if assembly requirements change in the future.

    NOTE: I also added references to the newer assemblies in the project.

    Ideas? 


    Thursday, October 31, 2019 3:22 PM

All replies

  • Hello,

    Have you considered AppDomain.AssemblyResolve Event ?

    Put the path to the DLL and name in app.config

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup>
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
        </startup>
      <appSettings>
        <add key="DllFolder" value="C:\Program Files (x86)\Payne1" />
        <add key="DllName" value="ConcreteClasses.dll" />
      </appSettings>
    </configuration>

    Add a reference to System.Configuration. Add the following to get the setting above.

    Imports System.Configuration
    
    Namespace My
        Partial Friend Class MyApplication
            Public ReadOnly Property CustomerDllFileName() As String
                Get
                    Return ConfigurationManager.AppSettings("DllName")
                End Get
            End Property
            ''' <summary>
            ''' 
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks>
            ''' </remarks>
            Public ReadOnly Property DllFolder() As String
                Get
                    Return ConfigurationManager.AppSettings("DllFolder")
                End Get
            End Property
    
        End Class
    End Namespace

    Add the following code module (note the namespace is one level down from the root, this is not required)

    Imports System.IO
    Imports System.Reflection
    
    Namespace Modules
        Module Resolver
            ''' <summary>
            ''' This handler is called only when the common language runtime tries to bind to the assembly and fails.        
            ''' </summary>
            ''' <param name="sender"></param>
            ''' <param name="args"></param>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Function ResolveEventHandler(sender As Object, args As ResolveEventArgs) As Assembly
    
                Dim executingAssemblies As Assembly = Assembly.GetExecutingAssembly()
                Dim referencedAssembliesNames() As AssemblyName = executingAssemblies.GetReferencedAssemblies()
                Dim assemblyName As AssemblyName
                Dim dllAssembly As Assembly = Nothing
    
                For Each assemblyName In referencedAssembliesNames
    
                    'Look for the assembly names that have raised the "AssemblyResolve" event.
                    If (assemblyName.FullName.Substring(0, assemblyName.FullName.IndexOf(",", StringComparison.Ordinal)) = args.Name.Substring(0, args.Name.IndexOf(",", StringComparison.Ordinal))) Then
                        ' Build path to place DLL
                        Dim tempAssemblyPath As String = Path.Combine(My.Application.DllFolder, args.Name.Substring(0, args.Name.IndexOf(",", StringComparison.Ordinal)) & ".dll")
                        dllAssembly = Assembly.LoadFrom(tempAssemblyPath)
    
                        Exit For
    
                    End If
                Next
    
                Return dllAssembly
    
            End Function
        End Module
    End Namespace

    In the code below I created a Person class in ConcreteClasses. Note the form's New constructor AddHandler

    Imports ConcreteClasses
    
    Public Class Form1
        Public Sub New()
    
            InitializeComponent()
    
            AddHandler AppDomain.CurrentDomain.AssemblyResolve,
                AddressOf Modules.ResolveEventHandler
    
        End Sub
        Private Sub GetPersonButton_Click(sender As Object, e As EventArgs) Handles GetPersonButton.Click
    
            Dim person As New Person With {.Identifier = 1, .FirstName = "Karen", .LastName = "Payne"}
            MessageBox.Show(person.ToString())
    
        End Sub
    End Class
    

    If the DLL is not located in the usual locations the resolver attempts to find it and if the DLL exists from the path in app.config this should solve your issue. I tried it out flip-flopping between two path under C:\Program Files (x86).

    Now the sticky part might be you want to look upwards for a relative path, best thought on that is when installing the app modify the config file.

    In closing this may not work for you but thought it prudent to provide just in case it might be helpful.


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    StackOverFlow
    profile for Karen Payne on Stack Exchange

    Thursday, October 31, 2019 11:44 PM
    Moderator
  • Thanks for the response.

    I see what you are doing, but just surprised I have to go through all this.  Seems I could use a relative path in codeBase or Probing somehow.  I just haven't figured it out yet.  The assemblies will always be in the same subfolders.  Just the application folder might vary.

    ========================================================

    Hello,

    Have you considered AppDomain.AssemblyResolve Event ?

    Put the path to the DLL and name in app.config

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup>
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
        </startup>
      <appSettings>
        <add key="DllFolder" value="C:\Program Files (x86)\Payne1" />
        <add key="DllName" value="ConcreteClasses.dll" />
      </appSettings>
    </configuration>

    Add a reference to System.Configuration. Add the following to get the setting above.

    Imports System.Configuration
    
    Namespace My
        Partial Friend Class MyApplication
            Public ReadOnly Property CustomerDllFileName() As String
                Get
                    Return ConfigurationManager.AppSettings("DllName")
                End Get
            End Property
            ''' <summary>
            ''' 
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks>
            ''' </remarks>
            Public ReadOnly Property DllFolder() As String
                Get
                    Return ConfigurationManager.AppSettings("DllFolder")
                End Get
            End Property
    
        End Class
    End Namespace

    Add the following code module (note the namespace is one level down from the root, this is not required)

    Imports System.IO
    Imports System.Reflection
    
    Namespace Modules
        Module Resolver
            ''' <summary>
            ''' This handler is called only when the common language runtime tries to bind to the assembly and fails.        
            ''' </summary>
            ''' <param name="sender"></param>
            ''' <param name="args"></param>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Function ResolveEventHandler(sender As Object, args As ResolveEventArgs) As Assembly
    
                Dim executingAssemblies As Assembly = Assembly.GetExecutingAssembly()
                Dim referencedAssembliesNames() As AssemblyName = executingAssemblies.GetReferencedAssemblies()
                Dim assemblyName As AssemblyName
                Dim dllAssembly As Assembly = Nothing
    
                For Each assemblyName In referencedAssembliesNames
    
                    'Look for the assembly names that have raised the "AssemblyResolve" event.
                    If (assemblyName.FullName.Substring(0, assemblyName.FullName.IndexOf(",", StringComparison.Ordinal)) = args.Name.Substring(0, args.Name.IndexOf(",", StringComparison.Ordinal))) Then
                        ' Build path to place DLL
                        Dim tempAssemblyPath As String = Path.Combine(My.Application.DllFolder, args.Name.Substring(0, args.Name.IndexOf(",", StringComparison.Ordinal)) & ".dll")
                        dllAssembly = Assembly.LoadFrom(tempAssemblyPath)
    
                        Exit For
    
                    End If
                Next
    
                Return dllAssembly
    
            End Function
        End Module
    End Namespace

    In the code below I created a Person class in ConcreteClasses. Note the form's New constructor AddHandler

    Imports ConcreteClasses
    
    Public Class Form1
        Public Sub New()
    
            InitializeComponent()
    
            AddHandler AppDomain.CurrentDomain.AssemblyResolve,
                AddressOf Modules.ResolveEventHandler
    
        End Sub
        Private Sub GetPersonButton_Click(sender As Object, e As EventArgs) Handles GetPersonButton.Click
    
            Dim person As New Person With {.Identifier = 1, .FirstName = "Karen", .LastName = "Payne"}
            MessageBox.Show(person.ToString())
    
        End Sub
    End Class
    

    If the DLL is not located in the usual locations the resolver attempts to find it and if the DLL exists from the path in app.config this should solve your issue. I tried it out flip-flopping between two path under C:\Program Files (x86).

    Now the sticky part might be you want to look upwards for a relative path, best thought on that is when installing the app modify the config file.

    In closing this may not work for you but thought it prudent to provide just in case it might be helpful.


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    StackOverFlow
    profile for Karen Payne on Stack Exchange


    Friday, November 1, 2019 11:17 AM