Ask a questionAsk a question
 

AnswerLoading external dll in DSL Tools

  • Thursday, October 15, 2009 3:52 PMNicolas Roux Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi,

    I would like to use some external library in my DSL Tools (VS 2008)
    My dll is not in the GAC, so I think I have to manage manually the dll loading; could you confirm that ? (I have some strange behavior; my dll is referenced in my dsl project, it works on debug, but not when I deploy on a third machine).

    where would be the best place to manage dll loading in the DSL library ?

    Thanks for any help,

    Nico
    •  

Answers

  • Tuesday, November 03, 2009 2:20 AMNathan Halstead [MSFT]ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Hi Nicolas,


    I'm not sure why you are omitting the asembly from your Release folder.  Assuming this is intentional, you could try hooking into Visual Studio's AssemblyResolveEvent handler.  To do this with VS 2008, add a key with a unique guid under:

    [$VSRoot$\BindingPaths\{guid}]
    "$AssemblyFolder$"=""

    For example:

    [$HKLM\Software\Microsoft\Visual Studio\9.0\BindingPaths\{00000000-0000-0000-0000-000000000000}]
    "C:\MyAssemblyFolder"=""

    When VS starts up, it will add C:\MyAssemblyFolder to the list of locations it uses in its AssemblyResolveEvent handler.

All Replies

  • Thursday, October 15, 2009 5:37 PMNicolas Roux Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    I add some more information.

    My intention is to add the following code somewhere:
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

    and:

          
     Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
     {
     	if (args.Name.StartsWith("MyLibrary"))
    	{
    		string assemblyDirectory = this.GetType().Assembly.Location;
    
                    return Assembly.LoadFrom(string.Format(@"{0}\{1}", assemblyDirectory, "MyLibrary.dll"));
            }
    
            return null;
     }
    


    Where should I put this code in my DSL project ?
  • Monday, October 19, 2009 9:54 AMNancy ShaoMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Hi Nico,

     

    Have you checked the external library was installed on a third machine after deployed ? What is the exception code while deploy on a third machine?

     

    If you don’t want install the assembly in the global assembly cache (GAC), you can use another two methods:

     

    ·         Use an application configuration (.config) file with the <codeBase> tags

    ·         Use the AssemblyResolve event

     

    For more information, please refer to:

     

    How to load an assembly at runtime that is located in a folder that is not the bin folder of the application

     

    Best Regards,

    Nancy


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
  • Monday, October 19, 2009 3:38 PMNicolas Roux Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi Nancy,

    Thank you for your reply.

    What I'd like to do is to use the AssemblyResolve event (as shown in my example).

    But I don't know where to put this code in the DSL project. Is there any intialisation code where I could add my code ?

    I think on debug, the dll is loaded from the bin/debug folder, where my library also is. But on release, the DSL dlls are loaded from the GAC, whereas my library is not in the GAC.
  • Tuesday, October 20, 2009 8:32 AMDuncanPMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Proposed AnswerHas Code

    For a DSL, you should add the assembly resolve event in the [MyLanguage]Package.Initialize event and unregister it in [MyLanguage]Package.Dispose.

    FYI the version of the DSL SDK that ships with Visual Studio 10 Beta 2 automatically generates an assembly resolver so that any assemblies that are referenced directly by the DSL Package or by the DSL will be resolved if necessary.

    I've included an example of the new generated code below which is generated into the [MyLanguage]PackageBase class. There is nothing specific about this code to Visual Studio 10. It's slightly more complicated than your version as it includes also extension points so that the target designer author (i.e. you!) can customise assembly resolution without having to change the generated code.

    One significant difference from your version is that the generated version does not explicitly load the requested assembly using Assembly.LoadFrom. Instead, it assumes that the requested assembly will have already been loaded in the current app domain so it just finds and returns it.

    Regards,
    Duncan

    /// <summary>
    /// Initialization method called by the package base class when this package is loaded.
    /// </summary>
    protected override void Initialize()
    {
    	base.Initialize();
    
    	// (generated code removed)...
    	
    	// Initialize the assembly resolver
    	this.InitializeAssemblyResolution();
    
    	// (generated code removed)...
    
    
    }
    
    
    
    #region Assembly resolution
    
    /// <summary>
    /// List of assembly names to resolve
    /// </summary>
    protected global::System.Collections.Generic.IEnumerable<string> AssemblyNamesToResolve { get; set; }
    
    /// <summary>
    /// Subscribe to the AssemblyResolve event and sets the list of assemblies to be resolved
    /// in order to be able to map the assemblies from the LoadFrom context to the Load context.
    /// </summary>
    /// <remarks>This is necessary when registering the Dsl Package with a codebase, as it and its pre-requisite assemblies 
    /// are then loaded with LoadFrom, and are not otherwise accessible from the Load context. The Dsl assembly needs to be accessible
    /// from the Load context in order to be able to deserialize ElementGroupPrototypes when dropping or pasting elements onto the 
    /// designer, and also for VS to be able to resolve custom type descriptors for the property grid.
    ///
    /// Only assemblies that are directly referenced by this assembly or the dsl assembly will be resolved.	
    /// </remarks>
    protected virtual void InitializeAssemblyResolution()
    {
    	global::System.AppDomain.CurrentDomain.AssemblyResolve += this.ResolveAssembly;
    
    	// Set the list of assemblies to resolve
    	global::System.Reflection.Assembly dslAssembly = typeof(global::Fabrikam.TestMinLanguage1.TestMinLanguage1DomainModel).Assembly;
    	this.AssemblyNamesToResolve = dslAssembly.GetReferencedAssemblies().Concat(this.GetType().Assembly.GetReferencedAssemblies()).Select(name => name.FullName);
    
    }
    
    /// <summary>
    /// Attempts to resolve the requested assembly.
    /// </summary>
    /// <returns>The resolved assembly, or null if it could not be resolved</returns>
    /// <remarks>Only assemblies that are directly referenced by this assembly or the dsl assembly
    /// will be resolved.</remarks>
    [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security","CA2109:ReviewVisibleEventHandlers", Justification="Extension point - can be overridden to provide custom behavior.")]
    protected virtual global::System.Reflection.Assembly ResolveAssembly(object sender, global::System.ResolveEventArgs args)
    {
    	global::System.Reflection.Assembly resolvedAssembly = null;
    	string requestedName = args.Name;
    
    	// Check whether the requested assembly is in the list of assemblies to resolve
    	if (this.AssemblyNamesToResolve != null && 
    		this.AssemblyNamesToResolve.Any(asmName => global::System.StringComparer.OrdinalIgnoreCase.Compare(asmName, requestedName) == 0))
    	{
    		// It is - attempt to resolve it
    		resolvedAssembly = global::System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(asm => global::System.StringComparer.OrdinalIgnoreCase.Compare(requestedName, asm.FullName) == 0);
    	}
    
    	return resolvedAssembly;
    }
    
    /// <summary>
    /// Unregister event handlers
    /// </summary>
    protected override void Dispose(bool disposing)
    {
    	if (disposing)
    	{
    		global::System.AppDomain.CurrentDomain.AssemblyResolve -= this.ResolveAssembly;
    	}
    
    	base.Dispose(disposing);
    }
    
    #endregion
    

     

  • Monday, October 26, 2009 2:06 PMNicolas Roux Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks for your Help Duncan.

    Unfortunatly, it does not work on my DSL.

    In debug mode, I don't have any problem, since my library is in the Debug Folder.

    In release, my Resolver is never called, and I don't know why.

    If you have any further idea...
  • Monday, October 26, 2009 5:47 PMNicolas Roux Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    When I set Assembly Binding (in the registry), I have the following error in my DSL File Error List:

    Error	1	Running transformation: System.IO.FileNotFoundException: Could not load file or assembly 'AspectizeDAL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c17398e2e59c2536' or one of its dependencies. The system cannot find the file specified.
    File name: 'AspectizeDAL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c17398e2e59c2536'
       at Aspectize.EntityDesigner.Diagram.GenerateModel2(Boolean CSharp)
       at Microsoft.VisualStudio.TextTemplating47306E8D910CE283B1F0072BC4B42751.GeneratedTextTransformation.TransformText()
    
    === Pre-bind state information ===
    LOG: User = VS2005_SP1\Administrator
    LOG: DisplayName = AspectizeDAL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c17398e2e59c2536
     (Fully-specified)
    LOG: Appbase = file:///C:/Program Files/Microsoft Visual Studio 9.0/Common7/IDE/
    LOG: Initial PrivatePath = NULL
    Calling assembly : Aspectize.EntityDesigner2008.Dsl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c17398e2e59c2536.
    ===
    LOG: This bind starts in default load context.
    LOG: Using application configuration file: C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe.Config
    LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
    LOG: The same bind was seen before, and was failed with hr = 0x80070002.
    


    The Library AspectizeDAL.dll is in the DSL installation folder, but not in the GAC.
    My Event Handler AssemblyResolve is never called.
    I can see my calling Assembly is DSL and not DSLPackage; could it be a reason of the issue ? 

     
  • Tuesday, November 03, 2009 2:20 AMNathan Halstead [MSFT]ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Hi Nicolas,


    I'm not sure why you are omitting the asembly from your Release folder.  Assuming this is intentional, you could try hooking into Visual Studio's AssemblyResolveEvent handler.  To do this with VS 2008, add a key with a unique guid under:

    [$VSRoot$\BindingPaths\{guid}]
    "$AssemblyFolder$"=""

    For example:

    [$HKLM\Software\Microsoft\Visual Studio\9.0\BindingPaths\{00000000-0000-0000-0000-000000000000}]
    "C:\MyAssemblyFolder"=""

    When VS starts up, it will add C:\MyAssemblyFolder to the list of locations it uses in its AssemblyResolveEvent handler.
  • Tuesday, November 03, 2009 3:44 PMDuncanPMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Nicholas,

    It looks from your message of Monday 26th October that you are running a text template. If this is correct, the assembly resolver you set up in the your DSL designer won't be called because text transformations occur in a separate AppDomain.

    If Nathan's suggestion doesn't work for you, you could try including an assembly directive processor in the T4 template that specifies where the assembly can be located e.g.   <#@assembly name="[assembly name]" #>

    In VS2008, [assembly name] can be an absolute path, the strong name of an assembly in the GAC, or the name of an assembly referenced by the current project (i.e. the project containing the .tt).

    In VS2010, the list of assemblies reference by the project can't be used. Instead, you will be able to use environment variables and VS macros (i.e. the macros you can use in Pre/Post-Build events) to specify assembly locations.
  • Saturday, November 07, 2009 4:52 PMNicolas Roux Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi,

    Nathan's suggestion seems to work; I have to do some more test to validate it, but my library is correctly loaded.

    Thanks very much for your help !