Could not load file or assembly in Solidworks addin - c#

I'm attempting to create a Solidworks plugin with AngelSix's SolidDna library.
I've used the standard setup as per the examples:
public class Integration : AddInIntegration
{
// All overrides left blank
public override void ApplicationStartup()
{
}
public override void ConfigureServices(FrameworkConstruction construction)
{
}
public override void PreConnectToSolidWorks()
{
}
public override void PreLoadPlugIns()
{
}
}
public class MySolidDnaPlugin : SolidPlugIn
{
public override string AddInTitle { get; } = "foo";
public override string AddInDescription { get; } = "bar";
public override void ConnectedToSolidWorks()
{
// This works fine...
Boo.Lang.List l = new Boo.Lang.List();
// ....But this doesn't :(
Boo.Lang.Compiler.BooCompiler c = new Boo.Lang.Compiler.BooCompiler();
}
public override void DisconnectedFromSolidWorks()
{
}
}
I'm attempting to add support for scripting in Boo, so users can edit the scripts on the fly. I've added a reference to Boo.Lang.dll, Boo.Lang.Compiler.dll and Boo.Lang.Parser.dll, and all 3 DLLs are definitely copied to the /bin folder.
I'm able to use classes from the Boo.Lang namespace, but as soon as I try and use the Boo.Lang.Compiler namespace it throws an error:
Could not load file or assembly 'Boo.Lang, Version=2.0.9.4, Culture=neutral, PublicKeyToken-32c39770e9a21a67' or one of its dependencies. The system cannot file the file specified.
The same code/setup works fine when running outside Solidworks, e.g. in a console app.
The SolidDna docs show the process of registering the plugin DLLs with regasm.exe /codebase, so I tried running that with the 3 Boo DLLs, but that doesn't make any difference.
Any suggestions or pointers?

My guess would be that you added those binaries to addin folder, while process is looking for those files in solidworks.exe folder.
I suggest you to confirm it with ProcMon tool.
If that is the case there are multiple ways to resolve this:
1 add those binaries to solidworks folder
2 add those folders to any of the folders in the path environment variable
3 add your addin folder to path environment variable
4 If those binaries as well as your addin are .net assemblies you can use ilmerge to combine them into one.
5 If your addin is .net assembly and you have access to appdomain object you can subscribe to AssemblyResolve event and provide path dynamically.

Related

Resolve .net tlb reference

Right now I am writing a .net dll which should be useable within VBA code (Excel, Access etc.). The following setup is working fine:
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[ComVisible(true)]
[Guid("8079e4a4-1e4b-4788-92ba-9d5b017fa9be")] //Allocate your own GUID
public interface ICommunication
{
string TestProp { get; set; }
string Login();
string Disconnect();
}
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("19fd86e2-f1b9-478c-ba7a-bd76bdf19b85")] //Allocate your own GUID
[ProgId("TestDll.Communication")]
public class Communication : ICommunication
{
public string TestProp { get; set; }
public string Login()
{
// use of BouncyCastle here
return "logged in";
}
public string Disconnect()
{
// ...
return "disconnected";
}
}
By referencing the generated tlb file I can properly use the Property aswell Disconnect function however calling the Login function leads to problems ("File not found" messagebox in Excel) which I guess are related to the usage of referenced BouncyCastle.
Sub Button1_Click()
Dim o: Set o = CreateObject("TestDll.Communication")
Dim value As String
value = o.Login()
MsgBox value
End Sub
What is the best way to deal with references to other .net assemblies inside com visible libraries? So far I tried registering bouncycastle to the GAC with no success.
Thanks :)
It was indeed like suggested above a file which could not be read:
On each build I place a .txt in the bin directory of the project which is read runtime.
Using the dll within my solution the file could be found since the relative path was my bin directory however using the tlb the relative root path was the document folder of the signed in windows user.
Funny how I thought the whole time that the error was related to the way I setup my dll as com visible :).

C#: use "Class Library" Project for plugins

this question follows my previous question.
I have a c# ASP.NET application and i want to provide support for plugins. Plugins can be custom c# classes, javascript, html, css, images, etc.
I see no problem as long as my application is extended with c# classes because all the user has to do is create a new "class library" project in visual studio and implement the interfaces, i provide. Then build a dll out of it and upload it to my server. The plugin-developer can add static files (html, js, css, etc.) into this project as well but i found some problems with that:
Every static file i add to the plugin project gets the build action "content" and it seems i cannot read those files from my server. (see my previously answered question). I have to manually select "Embedded Resource" on each file, so it is packed with the plugin dll.
I want to support Typescript for the plugins. The Typescript compiler generates javascript files in the same directory as the typescript-files. But the javascript files are not included in the project and therefore i have to include these in the plugin project and then set the correct build action. I don't want the plugin developers to do that all the time.
If the static files have the build action "enbedded resources", then the server can pickup these files by using the assembly.GetManifestResourceNames() method. This method returns the resources as a string. The path is not separated by \ or / but with a dot instead. So i am not able to distinguish between file path (this is relevant) or filename (also relevant to pickup the correct files), because the original filename can also have dots.
So i am starting to question the "class library" project type is right for my needs. Is there a way to get around of my issues or do i have to use another project type?
Thank you for any help!
Edit: Changed the question a little bit so it is better to understand.
You could make a zip package with the plugin dll and files. NuGet also uses this mechanism. A .nupkg is also just a zip file.
I would start by looking at MEF (Managed Extensibility Framework).
MSDN information can be found here: https://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx
From that link you can get more information and I believe there is a tutorial as well.
Oh, for me it seems very simple.
Let the developer create the plugin freestyle and put all the additional files in a directory, let's call it extras
To implement the your interface they will need your assembly so I guess you will ship it via nuget, or just some link. No matter what the case, provide them with some powershell script what will be required to run before the final build
The script would create zip archive from the extras directory and add it to the ClassLibrary project as EmbeddedResource.
As you mentioned earlier, you can access EmbeddedResource. So all you would do is to unpack it and you would have the exact directory tree.
The best idea would be to provide project template with script included, and also the empty zip archive added as embedded resource (it will be easier to just pack the files in the script and replace the file), and pre-build action set to run the script.
Am I missing something?
What about this.
In your web application, you could add a function that loop into your plugin directory and find DLL implementing an Iplugin (name is up to you) interface.
The interface is defined in a class library that both your web application and plugins have to implement.
You can use the Httpcontext Server mappath to read javascript and other files.
Here is a very basic implementation
First, you have the plugin interface (a class library implemented both by the web application and the individual plugins) I implemented sample properties and methods...
using System.Web;
public interface IPlugin
{
string Name { get; set; }
string Output { get; set; }
void Load(ref httpcontext Context);
void Dispose();
void Display();
}
Next, you have the Actual plugin class library we want to implement.
using System.Web;
using IPlugins;
public class AwesomePlugin : IPlugins.IPlugin
{
private string _Name = "AwesomePlugin";
private HttpContext _Context;
public string Name {
get { return _Name; }
set { _Name = value; }
}
public string Output {
get { return "Yay !!!"; }
set {
throw new NotImplementedException();
}
}
public void Display()
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Load(ref Web.HttpContext Context)
{
}
}
Finally, you dynamically load your plugins so you can use them in your application.
private Dictionary<string, IPlugins.IPlugin> _Plugins = new Dictionary<string, IPlugins.IPlugin>();
public void LoadPlugins()
{
lock (static_LoadPlugins_IpluginType_Init) {
try {
if (InitStaticVariableHelper(static_LoadPlugins_IpluginType_Init)) {
static_LoadPlugins_IpluginType = typeof(IPlugins.IPlugin);
}
} finally {
static_LoadPlugins_IpluginType_Init.State = 1;
}
}
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
dynamic Plugins = io.Directory.GetFiles(ServerPath);
foreach (string PluginPath in Plugins) {
dynamic Assembly = system.Reflection.Assembly.LoadFile(PluginPath);
Type PluginClass = Assembly.GetTypes.Where(T => T.GetInterface("IPlugin") != null).First;
IPlugins.IPlugin MyPlugin = Activator.CreateInstance(PluginClass);
MyPlugin.Load(httpcontext.Current);
_Plugins.#add(PluginClass.ToString, MyPlugin);
}
}
static bool InitStaticVariableHelper(Microsoft.VisualBasic.CompilerServices.StaticLocalInitFlag flag)
{
if (flag.State == 0) {
flag.State = 2;
return true;
} else if (flag.State == 2) {
throw new Microsoft.VisualBasic.CompilerServices.IncompleteInitialization();
} else {
return false;
}
}
That way, you can implement whatever you want in your plugin.
I believe you could load your plugins in a separate appdomain with restricted permissions to everything.
The files (Javascript / CSS / Html) should be available by accessing the full path of the file.
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
If the resources is embedded into the plugin DLL, you could read the stream from the loaded assembly or let the plugin manage its own embedded files.
For question Number 2, you can use
MS Build
to change the contenttype during build process.
You have to make yourself confident with MS Build

Class not available in namespace after build

I have a VS solution set up using build scripts to copy the compiled DLL into another project. The base project builds fine, and copies correctly to the target.
However, the target project won't compile as it can not find a particular class within the namespace:
foo.cs
namespace foo {
public class bar {
public static string myVar {
get { return "A string"; }
}
}
}
myPage.aspx.cs
using foo;
namespace foo.foo2 {
partial class bar2 {
protected void Page_Load(object sender, EventArgs e) {
// can access foo.bar here in the source project but not once the DLL is compiled and copied to target
var myVar = bar.myVar; // The name 'bar' does not exist in the current context
}
}
}
Why would this compile correctly in the source project, but prevent the target from building?
EDIT: Second project builds fine if I exclude myPage.aspx from the project. But I shouldn't have to do that.
You have very likely an incorrect assembly referenced.
Make sure you use the correct physical assembly. Sometimes, a dead (old) version lies around (such as in the GAC) and this one is referenced rather than the believed new version.
Easiest way to confirm is to rename the assembly file to something else and reference the newly named assembly. bar.myVar should show up immediately.

c# Class Library Project - Load DLL from same folder?

I'm working on a plugin for a existing C# .NET Program. It's structured in a manner where you put your custom .dll file in Program Root/Plugins/your plugin name/your plugin name.dll
This is all working well, but now I'm trying to use NAudio in my project.
I've downloaded NAudio via Nuget, and that part works fine, but the problem is that it looks for the NAudio.dll in Program Root, and not in the folder of my plugin.
This makes it hard to distribute my plugin, because it would rely on users dropping the NAudio.dll in their Program Root in addition to putting the plugin into the "Plugins" folder.
Source:
SettingsView.xaml:
<Button HorizontalAlignment="Center"
Margin="0 5"
Width="120"
Command="{Binding SoundTestCommand,
Source={StaticResource SettingsViewModel}}"
Content="Sound Test" />
SettingsViewModel.cs:
using NAudio.Wave;
.
.
.
public void SoundTest()
{
IWavePlayer waveOutDevice;
WaveStream mainOutputStream;
WaveChannel32 inputStream;
waveOutDevice = new WaveOut();
mainOutputStream = new Mp3FileReader(#"E:\1.mp3");
inputStream = new WaveChannel32(mainOutputStream);
inputStream.Volume = 0.2F;
waveOutDevice.Init(mainOutputStream);
waveOutDevice.Play();
}
How can I get C# to look for NAudio in Program Root/Plugins/my plugin name/NAudio.dll instead of looking for it in Program Root/NAudio.dll ?
I'm using VS Express 2013, Target Framework is 4.5 and Output type is Class Library.
Edit:
I found 2 ways to make this work ( I'm not sure what the pros and cons of both methods are - if anyone knows I would appreciate additional information ).
Using the NuGet Package Costura.Fody.
After installing the NuGet package, I simply had to set all other References "Copy Local" to "False" and then set "Copy Local" for NAudio to "True".
Now when I build, the NAudio.dll is compressed and added to my own DLL.
Using the AssemblyResolver outlined below.
It didn't work right away though, so here is some additional information that may help anyone facing the same issue:
I put Corey's code as he posted it into the Helpers folder.
My entry point is Plugin.cs, the class is public class Plugin : IPlugin, INotifyPropertyChanged
In there, the entry method is public void Initialize(IPluginHost pluginHost), but simply putting PluginResolver.Init(path) did not work.
The host program uses WPF and is threaded and I had to use a dispatcher helper function of the host program to get it to work: DispatcherHelper.Invoke(() => Resolver.Init(path));
As mentioned, I'm currently unsure which method to use, but I'm glad I got it to work. Thanks Corey!
You can use the PATH environment variable to add additional folders to the search path. This works for native DLLs, but I haven't tried to use it for .NET assemblies.
Another option is to add a hook to the AssemblyResolve event on the current application domain and use a custom resolver to load the appropriate assembly from wherever you find it. This can be done at the assembly level - I use it in NAudio.Lame to load an assembly from a resource.
Something along these lines:
public static class PluginResolver
{
private static bool hooked = false;
public static string PluginBasePath { get; private set; }
public static void Init(string BasePath)
{
PluginBasePath = BasePath;
if (!hooked)
{
AppDomain.CurrentDomain.AssemblyResolve += ResolvePluginAssembly;
hooked = true;
}
}
static Assembly ResolvePluginAssembly(object sender, ResolveEventArgs args)
{
var asmName = new AssemblyName(args.Name).Name + ".dll";
var assemblyFiles = Directory.EnumerateFiles(PluginBasePath, "*.dll", SearchOption.AllDirectories);
var asmFile = assemblyFiles.FirstOrDefault(fn => string.Compare(Path.GetFileName(fn), asmName, true) == 0);
if (string.IsNullOrEmpty(asmFile))
return null;
return Assembly.LoadFile(asmFile);
}
}
(Usings for the above: System.IO, System.Reflection, System.Linq)
Call Init with the base path to your plugins folder. When you try to reference an assembly that isn't loaded yet it will search for the first file that matches the base name of the assembly with dll appended. For instance, the NAudio assembly will match the first file named NAudio.dll. It will then load and return the assembly.
No checking is done in the above code on the version, etc. and no preference is given to the current plugin's folder.

Could not load file or assembly 'SharpSvn-DB44-20-Win32.dll'

I'm using ASP.NET MVC3, StructureMap and SharpSvn.
Here's the code that I'm using:
public class SvnFileReader : ISvnFileReader
{
private readonly string _svnAddress;
public SvnFileReader(string svnAddress)
{
_svnAddress = svnAddress;
}
public void DownloadFiles(DirectoryInfo destination)
{
using (var svnClient = new SvnClient())
{
// checkout the code to the specified directory
svnClient.CheckOut(new Uri(_svnAddress), destination.FullName);
}
}
}
When I execute this code:
_svnFileReader.DownloadFiles(new System.IO.DirectoryInfo(#"d:\test"));
I get the following error message:
Could not load file or assembly
'file:///D:\Projects\SvnFileReaderDemo\bin\SharpSvn-DB44-20-Win32.dll'
or one of its dependencies. The module
was expected to contain an assembly
manifest.
Any help would be greatly appreciated!
You should exclude the SharpSvn DLLs from StructureMap automatic assembly scanning for dependencies. This is an unmanaged library but because you have configured StructureMap to look for types in all dlls when it tries to load this one it breaks.
UPDATE:
If you are running this code on a x64 bit OS you may try downloading the specific x64 SharpSvn which uses SharpSvn-DB44-20-x64.dll.

Categories