问题描述
我的 Visual Studio 扩展使用外部库时遇到问题.目前我有自己的 NuGet 服务器,我在上面托管我的库,现在因为我不想要功能两次,所以我从扩展中提取了一些功能到现有库中.
I have a problem using external libraries for my visual studio extension. Currently I have my own NuGet server on which I host my libraries, now since I dont want functionality twice I extracted some functions out of my extension into an existing library.
然而,问题是,每当我想使用该程序集中的任何内容时,我都无法这样做,因为 Visual Studio 不包含 .vsix 包中的 .dll.
The problem is however that whenever I want to use anything from that assembly I can not do so, since visual studio does not include the .dlls in the .vsix package.
到目前为止我已经尝试过:
So far I have tried:
- 使用资产从其包位置包含库.这会在解决方案中创建两个项目,它们的包含在 VSIX"属性设置为 true,这不起作用
- 包括项目,然后添加 BuiltProjectOutputGroup;BuiltProjectOutputGroupDependencies;GetCopyToOutputDirectoryItems;SatelliteDllsProjectOutputGroup;参考的VSIX 中包含的其他组"属性,这不起作用
- 使用 Assets 将库作为项目包含在内,这不起作用
所以现在我结束了,因为似乎没有任何工作......
So now I am at my end since there is nothing that seems to work....
我找到的解决方案已经建议了我尝试过的所有步骤,但此时
The solutions I found are already suggesting all the steps I tried, but at this point
我还发现这两个帖子与我的问题相同,但这些回答并没有像我说的那样对我有用.
I also found these two post which are the same as my question but those answerts did not work for me like I said.
推荐答案
好的,我找到了一种方法,这是我在堆栈溢出时找到的两个答案的组合.
Okay I found a way to do it, its a combonation of two answers I found here on stack overflow.
尽管它有点老套,但我认为这是唯一可能的方法.
And eventhough its a bit hacky I suppose its the only way possible.
所以我只是使用了现有的 ManualAssemblyResolver 并根据我的需要对其进行了调整,结果如下:
So I simply used the existing ManualAssemblyResolver and adjusted it to my needs, the Result being this:
public class ManualAssemblyResolver : IDisposable
{
#region Attributes
/// <summary>
/// list of the known assemblies by this resolver
/// </summary>
private readonly List<Assembly> _assemblies;
#endregion
#region Properties
/// <summary>
/// function to be called when an unknown assembly is requested that is not yet kown
/// </summary>
public Func<ResolveEventArgs, Assembly> OnUnknowAssemblyRequested { get; set; }
#endregion
#region Constructor
public ManualAssemblyResolver(params Assembly[] assemblies)
{
_assemblies = new List<Assembly>();
if (assemblies != null)
_assemblies.AddRange(assemblies);
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
}
#endregion
#region Implement IDisposeable
public void Dispose()
{
AppDomain.CurrentDomain.AssemblyResolve -= OnAssemblyResolve;
}
#endregion
#region Private
/// <summary>
/// will be called when an unknown assembly should be resolved
/// </summary>
/// <param name="sender">sender of the event</param>
/// <param name="args">event that has been sent</param>
/// <returns>the assembly that is needed or null</returns>
private Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
{
foreach (Assembly assembly in _assemblies)
if (args.Name == assembly.FullName)
return assembly;
if (OnUnknowAssemblyRequested != null)
{
Assembly assembly = OnUnknowAssemblyRequested(args);
if (assembly != null)
_assemblies.Add(assembly);
return assembly;
}
return null;
}
#endregion
}
之后我使用了一个Addition ExtensionManager来获取扩展的安装路径.看起来像这样
After that I used an Addition ExtensionManager to get the installation path of the extension. Which looks like this
public class ExtensionManager : Singleton<ExtensionManager>
{
#region Constructor
/// <summary>
/// private constructor to satisfy the singleton base class
/// </summary>
private ExtensionManager()
{
ExtensionHomePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Definitions.Constants.FolderName);
if (!Directory.Exists(ExtensionHomePath))
Directory.CreateDirectory(ExtensionHomePath);
SettingsFileFullname = Path.Combine(ExtensionHomePath, Definitions.Constants.SettingsFileName);
InstallationPath = Path.GetDirectoryName(GetType().Assembly.Location);
}
#endregion
#region Properties
/// <summary>
/// returns the installationPath
/// </summary>
public string InstallationPath { get; private set; }
/// <summary>
/// the path to the directory where the settings file is located as well as the log file
/// </summary>
public string ExtensionHomePath { get; private set; }
/// <summary>
/// the fullpath to the settingsfile
/// </summary>
public string SettingsFileFullname { get; private set; }
#endregion
}
然后在包的 Initialize() 方法中,您将需要创建 ManualAssemblyResolver 的一个实例,并提供您需要的程序集的路径,如下所示:
Then in the Initialize() method of the Package you will need to create an instance of the ManualAssemblyResolver and provide the Path to the assemblies you need like this:
#region Attributes
private ManualAssemblyResolver _resolver;
#endregion
#region Override Microsoft.VisualStudio.Shell.Package
/// <summary>
/// Initialization of the package; this method is called right after the package is sited, so this is the place
/// where you can put all the initialization code that rely on services provided by VisualStudio.
/// </summary>
protected override void Initialize()
{
_resolver = new ManualAssemblyResolver(
Assembly.LoadFrom(Path.Combine(ExtensionManager.Instance.InstallationPath, Definitions.Constants.NameOfAssemblyA)),
Assembly.LoadFrom(Path.Combine(ExtensionManager.Instance.InstallationPath, Definitions.Constants.NameOfAssemblyB))
);
请注意,您需要在任何其他甚至涉及引用程序集中的任何内容之前调用它,否则将抛出 FileNotFoundException.
Note that you will need to call this before anythingelse that even touches anything from the referenced assemblies, otherwise a FileNotFoundException will be thrown.
无论如何,这现在似乎对我有用,但我希望有一种更干净的方法来做到这一点.因此,如果有人有更好的方法(一种实际上包含并从 .vsix 包中提取程序集的方法),请发布答案.
In any case this seems to work for me now but I wish there was a cleaner way to do it. So if anybody has a better way (a way that actually includes and lloks up the assemblies from the .vsix package) then please post an answer.
好的,现在我发现了真正的问题,只是因为 dll 是卫星 dll(设置了它们的程序集文化)所以它们不可见......
Okay now I found the real issue, it was simply the fact that the dlls were satellite dll (the had their assembly culture set) so they were not visible....
但是,当它们仍然是卫星 dll 时,上述修复程序有效.
However the above fix worked when they were still satillite dlls.
这篇关于在 Visual Studio 扩展中包含外部 .dll的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!