在 Visual Studio 扩展中包含外部 .dll
Include external .dll in Visual Studio Extension
我的 visual studio 扩展使用外部库时遇到问题。目前我有自己的 NuGet 服务器,我在上面托管我的库,现在因为我不想要两次功能,所以我从我的扩展中提取了一些功能到现有的库中。
然而,问题是每当我想使用该程序集中的任何内容时,我都无法这样做,因为 visual studio 不包含 .vsix 包中的 .dll。
到目前为止我已经尝试过:
- 使用 Assets 从它们的包位置包含库。这在解决方案中创建了两个项目,它们的 "Include in VSIX" 属性 设置为 true,这没有用
- 包括项目,然后添加 BuiltProjectOutputGroup;BuiltProjectOutputGroupDependencies;GetCopyToOutputDirectoryItems;SatelliteDllsProjectOutputGroup;引用的 "Other Groups included in VSIX" 属性 无效
- 使用 Assets 将库作为项目包含在内,但这不起作用
所以现在我已经走到尽头了,因为似乎没有任何效果....
我找到的解决方案已经建议了我尝试过的所有步骤,但此时
我还发现了这两个 post,它们与我的问题相同,但这些答案并不像我说的那样对我有用。
this
this
好的,我找到了一种方法,它是我在堆栈溢出中找到的两个答案的组合。
虽然有点老套,但我认为这是唯一可行的方法。
所以我简单地使用了现有的 ManualAssemblyResolver 并根据我的需要对其进行了调整,结果是这样的:
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 来获取扩展的安装路径。看起来像这样
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
}
然后在 Package 的 Initialize() 方法中,您将需要创建 ManualAssemblyResolver 的实例并提供所需程序集的路径,如下所示:
#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。
无论如何,这似乎对我现在有用,但我希望有一种更简洁的方法来做到这一点。因此,如果有人有更好的方法(一种实际包含并调用 .vsix 包中的程序集的方法),请 post 回答。
编辑:
好吧,现在我找到了真正的问题,这仅仅是因为 dll 是卫星 dll(具有它们的汇编文化集)所以它们不可见....
然而,当它们仍然是 satillite dll 时,上述修复工作有效。
我的 visual studio 扩展使用外部库时遇到问题。目前我有自己的 NuGet 服务器,我在上面托管我的库,现在因为我不想要两次功能,所以我从我的扩展中提取了一些功能到现有的库中。
然而,问题是每当我想使用该程序集中的任何内容时,我都无法这样做,因为 visual studio 不包含 .vsix 包中的 .dll。
到目前为止我已经尝试过:
- 使用 Assets 从它们的包位置包含库。这在解决方案中创建了两个项目,它们的 "Include in VSIX" 属性 设置为 true,这没有用
- 包括项目,然后添加 BuiltProjectOutputGroup;BuiltProjectOutputGroupDependencies;GetCopyToOutputDirectoryItems;SatelliteDllsProjectOutputGroup;引用的 "Other Groups included in VSIX" 属性 无效
- 使用 Assets 将库作为项目包含在内,但这不起作用
所以现在我已经走到尽头了,因为似乎没有任何效果....
我找到的解决方案已经建议了我尝试过的所有步骤,但此时
我还发现了这两个 post,它们与我的问题相同,但这些答案并不像我说的那样对我有用。
this
this
好的,我找到了一种方法,它是我在堆栈溢出中找到的两个答案的组合。
虽然有点老套,但我认为这是唯一可行的方法。
所以我简单地使用了现有的 ManualAssemblyResolver 并根据我的需要对其进行了调整,结果是这样的:
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 来获取扩展的安装路径。看起来像这样
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
}
然后在 Package 的 Initialize() 方法中,您将需要创建 ManualAssemblyResolver 的实例并提供所需程序集的路径,如下所示:
#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。
无论如何,这似乎对我现在有用,但我希望有一种更简洁的方法来做到这一点。因此,如果有人有更好的方法(一种实际包含并调用 .vsix 包中的程序集的方法),请 post 回答。
编辑: 好吧,现在我找到了真正的问题,这仅仅是因为 dll 是卫星 dll(具有它们的汇编文化集)所以它们不可见....
然而,当它们仍然是 satillite dll 时,上述修复工作有效。