Ask a questionAsk a question
 

AnswerDo I have to copy all dlls to the Visual Studio directories

  • Friday, October 02, 2009 9:22 PMMichael Feingold Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    When creating a VS Extension, more specifically MEF Editor extension, what are my options in regards to where I can put the dlls? The standard deployment puts them in some internal Visual Studio subdirectory. Can I configure the vsixmanifest to take it from another spot, like my own installation directory?

Answers

  • Monday, November 02, 2009 7:44 PMAaron MartenMSFT, OwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Hi Michael,

    VSIX is a mechanism for distributing extensions to Visual Studio. As such, with a "pure VSIX" approach (where you simply distribute a .vsix file) there are only two possible locations for your binaries to end up at install time:

    %LocalAppData%\Microsoft\VisualStudio\10.0\Extensions\<ExtensionDirectory>
    %VSInstallLocation%\Common7\IDE\Extensions\<ExtensionDirectory>

    As you would expect, both of these are VS specific locations. If you need a more "general" location in Windows (e.g. your own Program Files directory, GAC, etc...) you can't use a "pure VSIX" as your approach (unless you go with something like Oleksiy suggests).

    If you really do need this, we would recommend that you go with MSI or some other full installer technology for targetting Windows. You can then just drop your VS-specific files (e.g. MEF assembly & .vsixmanifest file) to the proper locations for VS to pick them up. Just make sure you use the <InstalledByMsi>true<InstalledByMsi> in your vsixmanifest file (so Extension Manager doesn't allow the user to uninstall from within VS).

    Thanks,
    Aaron
    http://blogs.msdn.com/aaronmar

All Replies

  • Sunday, October 11, 2009 4:17 AMQuan ToMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    If you're using the VSIX model, the installation location is abstracted from the end user and placed under localappdata.

    why do you need to put it in your own install directory?  (the location under localappdata becomes your install directory)
  • Sunday, October 11, 2009 2:25 PMMichael Feingold Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Well, I have no problems with placing the actual editor dlls in the localappdata. As a matter of fact I want it this way, but

    The editor relies on a parser and the parser can and will be used runtime as well as design time. Placing runtime dlls into something so closely tied to VS does not seem reasonable to me, currently we place them in a directory of the customer choice, but how to make the parser dlls located in the installation directory available to the editor is a mistery to me. Currently we place them in both install directory and the localappdata directory - a solution which makes me cringe.

    Is there a better one?
  • Saturday, October 31, 2009 5:30 PMOleksiy G Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    Hi Michael,

    You can always create your own assembly resolver to handle situations with referenced DLLs which are located somewhere on the file system.
    At the very start of your package you have to create a resolver and give him a path where referenced DLLs are stored:

            AssemblyResolver.Create(@"C:\Program Files (x86)\Me\My Product\Assemblies");

    This is the source code for an advanced resolver I commonly use:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Sample.AssemblyResolution
    {
    	public sealed class AssemblyResolver : IDisposable
    	{
    		string[] _Paths;
    
    		public AssemblyResolver(Assembly assembly)
    			: this(GetAssemblyBasePath(assembly))
    		{
    		}
    
    		public AssemblyResolver(params string[] paths)
    		{
    			_Paths = paths;
    			AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    		}
    
            public static string GetAssemblyBasePath(Assembly assembly)
            {
                return Path.GetDirectoryName(new Uri(assembly.EscapedCodeBase).LocalPath);
            }
    
    		#region IDisposable Members
    
    		public void Dispose()
    		{
    			AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
    		}
    
    		#endregion
    
    		List<KeyValuePair<string, AssemblyName>> _AssembliesCache;
    
    		void _EnsureAssembliesCacheIsFilled()
    		{
                lock (this)
                {
                    if (_AssembliesCache != null)
                        return;
                    _AssembliesCache = new List<KeyValuePair<string, AssemblyName>>();
                    foreach (string path in _Paths)
                    {
                        if (!Directory.Exists(path))
                            continue;
                        List<string> files = new List<string>();
                        files.AddRange(Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly));
                        files.AddRange(Directory.GetFiles(path, "*.exe", SearchOption.TopDirectoryOnly));
                        foreach (string file in files)
                        {
                            AssemblyName definition;
                            try
                            {
                                definition = AssemblyName.GetAssemblyName(file);
                            }
                            catch (Exception)
                            {
                                continue;
                            }
                            _AssembliesCache.Add(new KeyValuePair<string, AssemblyName>(file, definition));
                        }
                    }
                }
    		}
    
    		static Dictionary<string, Assembly> _ResolvedAssembliesMap = new Dictionary<string, Assembly>(StringComparer.Ordinal);
    
            //[DebuggerNonUserCode]
    		Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    		{
                lock (_ResolvedAssembliesMap)
                {
                    string name = args.Name;
                    Assembly assembly;
                    if (_ResolvedAssembliesMap.TryGetValue(name, out assembly))
                        return assembly;
                    AssemblyName reference;
                    try
                    {
                        reference = new AssemblyName(name);
                    }
                    catch
                    {
                        return null;
                    }
                    _EnsureAssembliesCacheIsFilled();
                    foreach (var i in _AssembliesCache)
                    {
                        AssemblyName definition = i.Value;
                        if (AssemblyName.ReferenceMatchesDefinition(reference, definition))
                        {
                            assembly = Assembly.LoadFrom(i.Key);
                            break;
                        }
                    }
                    _ResolvedAssembliesMap.Add(reference.FullName, assembly);
                    return assembly;
                }
    		}
    
            public static AssemblyResolver Create(params string[] paths)
            {
                return new AssemblyResolver(paths);
            }
        }
    }
    
    

    Oleksiy Gapotchenko
  • Monday, November 02, 2009 7:44 PMAaron MartenMSFT, OwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Hi Michael,

    VSIX is a mechanism for distributing extensions to Visual Studio. As such, with a "pure VSIX" approach (where you simply distribute a .vsix file) there are only two possible locations for your binaries to end up at install time:

    %LocalAppData%\Microsoft\VisualStudio\10.0\Extensions\<ExtensionDirectory>
    %VSInstallLocation%\Common7\IDE\Extensions\<ExtensionDirectory>

    As you would expect, both of these are VS specific locations. If you need a more "general" location in Windows (e.g. your own Program Files directory, GAC, etc...) you can't use a "pure VSIX" as your approach (unless you go with something like Oleksiy suggests).

    If you really do need this, we would recommend that you go with MSI or some other full installer technology for targetting Windows. You can then just drop your VS-specific files (e.g. MEF assembly & .vsixmanifest file) to the proper locations for VS to pick them up. Just make sure you use the <InstalledByMsi>true<InstalledByMsi> in your vsixmanifest file (so Extension Manager doesn't allow the user to uninstall from within VS).

    Thanks,
    Aaron
    http://blogs.msdn.com/aaronmar
  • Tuesday, November 03, 2009 10:16 PMMichael Feingold Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thank you for the info, That answers the question from the standpoint of the installer functionality. I still have a related question - how can I make an object defined in the "shared" assembly - parser in my case available for MEF binding in by the Visual Studio.

    In other words how can I export (in MEF sense) my parser so that it can be imported by my designer dlls (sitting in Extensions directory, of course).

    As far as the Oleksiy suggestion goes the problem there is that it loads the assembly into a LoadFrom context, which is something I would like to avoid if possible