This project has moved. For the latest updates, please go here.

Problems with loading dll's

Mar 10, 2010 at 7:37 AM

Hello, I hope it's not a dumb question as I don't have much experience with dll loading. I created some VST plugins using vst.net and I have problems loading them with common DAW's like WaveLab or Cubase. Both WaveLab and Cubase require that plugins are located in a special folder - in my case C:\Program Files\Steinberg\Vstplugins and they reject any other .dll in that folder that is NOT a vst plugin.

My problem is, I cannot load essential dll's to make my plugin work. In my recent projects (not VST) I used the AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve) mechanism to load dll's stored in a different folder than program's run folder. But those projets were stand-alone .net programs.

I can run my plugins without problems with DAW's that have the ability to manually specify VST plugin path (like ASIO FX Processor LE).

I think that AssemblyResolve event should work even in case of VST plugins, but I don't know when dll loader actually loads all necessary dll's. In case of stand-alone program I make subscription to AssemblyResolve event in the first code line of static void Main() routine.


public class MyVstInterface : IVstPluginCommandStub
{
   private VstPluginInfo _pluginInfo;
   private IVstHostCommandStub _hostStub;
   private WinFormsWrapper<MyVstControl> _editorCtrl; // MyVstControl implements all DSP stuff and references additional dll's.
   private float[] inputBuffer;
   private float[] outputBuffer;

   public MyVstInterface()
   {
      AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
      _editorCtrl = new WinFormsWrapper<MyVstControl>();
      // Custom code follows...
   }

   private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
   {
      System.Windows.Forms.MessageBox.Show("Dll loading failed!");
      return null;
   }

   // Implementation code follows...
}

If I delete some essential dll's for test purpose and run the plugin, I get VST.NET Error message box with quite long error description instead of "Dll loading failed!" message.

I do not like the solution with GAC or config files, but if it is the only one, I have to accept it.

Any suggestions? Tnx in advance, An

Coordinator
Mar 10, 2010 at 8:05 AM
Edited Mar 10, 2010 at 8:07 AM

Hi An,

I use Cubase (SX 2.0) myself as a test host and it works fine. I must say that my VST.NET assemblies are registered in the GAC.

But it should also be possible that all dependent assemblies (inlcuding VST.NET asemblies) are located in the plugin directory. So in theory you should be able to create a seperate (sub) folder for each individual plugin and version/manage all dependencies independent of other plugins.

The VST.NET boot-up code also hooks up onto the AssemblyResolve event on the current Domain (see the AssemblyLoader class in Interop). It uses the passed-in assembly name to look for it in the plugin folder (that is the same folder that contains the renamed Jacobi.Vst.Interop assembly as well as your custom managed plugin assembly).

So I would not hook onto the AssemblyResolve event yourself and return null. That might bypass the code thats in the VST.NET AssemblyLoader.

You could try to register the Jacobi.Vst.Core and Jacobi.Vst.Framework assemblies in the GAC and try again.

If nothing works, send me your assemblies and I will try to find out whats wrong (time permitting).

Hope it helps.

 Marc Jacobi

Mar 10, 2010 at 8:58 AM

Hi Marc, tnx for fast reply.

In my demo code I just returned null for compilers's sake to see whether execution steps inside... I svn the latest source code and I have problems compiling it. (Interop project). Compiler returns 60 build errors, for ex:

error C2039: 'MidiData' : is not a member of 'Jacobi::Vst::Core::VstMidiEvent'

error C2227: left of '->Length' must point to class/struct/union/generic type

tnx

Coordinator
Mar 10, 2010 at 9:35 AM
Edited Mar 10, 2010 at 9:36 AM

Oh shoot!

I refactored the VstEvent hierarchy but forgot to update the samples and Interop! I'll fix that tonight. Sorry 'bout that

Mar 10, 2010 at 9:54 AM

Hi again!

Using the 36975 build I managed to compiled it.

// The handler that resolves assemblies.
System::Reflection::Assembly^ AssemblyLoader::LoadAssembly(System::Object^ sender, System::ResolveEventArgs^ e)
{
   array<System::String^>^ parts = e->Name->Split(',');
   //System::Windows::Forms::MessageBox::Show("WUHU : "+e->Name);
   System::String^ fileName = "D:/xx/"+parts[0] + ".dll"; // Here I specified my additional libs path...
   return System::Reflection::Assembly::LoadFile(System::IO::Path::Combine(_baseDir, fileName));
}

I added the path manually and now everything works. Only the two files of each plugin must now be in effect's folder; myPlugin.dll and myPlugin.net.vstdll. All my available (farely OLD) test DAWs accept that. Technically problem solved.

My custom subscription to the assembly resolve event in was in fact made too late. Question is, is it somehow possible that vst.net passes this event to the user-side (where actual plugin is implemented) so the user can handle resolve it in a custom way?

tnx an

Coordinator
Mar 10, 2010 at 10:23 AM

Great!

Its very hard to promote this event to the plugin, because it happens during the loading of the plugin and its dependencies. So there is no (user-side) object yet to call.

I could provide you with an option to configure extra (libs) dependency folders in .config? That could be a usefull feature. You would create a .config file with the same name as the host .exe. For instance: Cubase.exe.config and add the extra paths there.

Thats about the best I can think of right now.

Mar 10, 2010 at 11:48 AM

Hi Marc

Since it is impossible to do something with the thing if it does not exist yet, config file is obviously the only choice. In my case GAC would not work as I have also unmanaged dlls to load. GAC also requires signed assemblies.

myPlugin.dll.config should be the config file name. If it is present, AssemblyLoader should take care of dlls paths... otherwise all additional dlls are expected to be in the plugin folder. Simple, but how will format of cfg file look like?

I think config files for each host like you stated is not a good idea. Plugin sholud load allways in the same way no matter what the host is and therefore config is related to plugin not to actual host.

tnx an

Coordinator
Mar 10, 2010 at 12:01 PM

Yeah, a config file per plugin would be great, but I never got that to work ;-) But I can look into that again.

I was thinking of simply using one application setting in config that can hold ";"-separated paths...
Nice and simple.

Coordinator
Mar 10, 2010 at 6:00 PM
Edited Mar 10, 2010 at 6:01 PM

Hi An,

I hope the latest code will help you a bit. I have not looked into a per-plugin configuration yet, but at least you have some options now.

There is a vstnetProbePaths appSettings key that can be used in the (exe)config to specify a semi-colon separated list of global probe paths.

There are two new classes in Core.Plugin: The AssemblyLoader is a replacement for the class that was in Interop. It contains two collections of probe paths you can use to managed how assemblies are resolved. It also has a public CreateFileFinder method that returns a new FileFinder instance pre-filled with the probe paths from the AssemblyLoader. You can call this in you own code to find (custom) dependencies but still using the default probe paths. The FileFinder helps you to find files based on multiple paths and file extensions.

If you have any questions, don't hesitate to ask.

Hope it helps.

Mar 10, 2010 at 6:22 PM

Hello Marc and thanks for all your effort with this.

I have to check the latest source code. With build 36975 I managed to add some "ResolveClass" in Interop. that checks for possible library path resolves. But with that solution my custom "Jacobi.Vst.LibraryPath.config" must be present in run folder. It is then difficult to manage that config file in order to instal/uninstall plugins with Windows Installer. I'll check the latest.

Tnx alot, an

Coordinator
Mar 11, 2010 at 5:41 AM

I just realized that the current implementation is flawed...

Will try to fix it ASAP.

What is the problem with using a config file in combination with Windows Installer?

Mar 11, 2010 at 12:47 PM

Hi Marc!

Using the latest source code I am not able to open demo plugins any more (delay) with VST Host 1.47 and also any other unmanaged DAW. My managed (and Jacobi.Vst.Samples.Host.exe) host opens all my managed plugins without problems. I tried to attach to the process but with no success. With managed host I can go line by line through the code.

I think current implementation is not flawed. The fact is, DAWs don't like any other dlls except VST dlls in plugin folder and things must somehow get linked together. It is very difficult to do a tool that will handle all cases with ease. If we look to some commercial plugins... there is a lot of stuff istalled behind simple dreamPlugin.dll. But I think that per-host config file is very unpractical to the end users.

For my solution I added some helper class in your latest Core project that resolves for link paths according to "config" file that must be near myPlugind.dll and renamed interop copy. It must have name "VST.DLL.PathResolve.cfg". If not present things go default way. There is ONE config file for all managed plugins in given plugin folder. Per-plugin config file will be great, but this is also acceptable.

In the end I would just like to create installer, that drops plugins to given folder and creates appropriate config file (there will still be registry stuff involved). Plugins should then run without any effort. If someone manually moves plugin to different location -> not my problem.

There is in fact no problems with config files and Windows Installer. Install program must then have custom action program that creates apropriate config file according to "prerequisite" location.

tnx an

Mar 11, 2010 at 1:24 PM

Hi again!

I think I actually found "clean" solution for myself. If I comment the subscription to AssemblyResolve event in Core I can subscript to that event in my plugin implementation and handle things all by myself. If that actually works it is the best solution of all.

an

Coordinator
Mar 11, 2010 at 7:52 PM

Hi An,

I've checked in some new code that seems to be working ok. The Core assembly is now resolved by the Bootstrapper class (Interop) and the AssemblyLoader (in Core) takes over from there. The bootstrapper also looks at the vstnetProbePaths in the appSettings of the (exe)config.

My next step is to also allow plugin specific config. This would give you enough options to do your custom dependency loading I think, will it not?

NOTE: Remember that VST.NET has a LGPL license. That means you can use the binaries in any way you want, but as soon as you (re)use or change the source code, your code has to become open source as well!

Mar 11, 2010 at 8:28 PM

Tnx a lot!

I have read and I understand the LGPL license. My projects related to VST technology are (for now) for my personal usage only and are not intended to be exposed to the public in any case. I use vst net to learn the VST technology and to investigate the benefit of migrating my sealed DSP system to VST.  And right now I am quite far away from anything other than strictly personal usage.

Plugin specific config would be a great thing indeed, but in general I planed even more granular solution. This has nothing to do with vst .net. Actual plugin implementation must be responsible for its own linking and rigth now I work on dynamic runtime linking of all stuff my plugin needs.

an

Coordinator
Mar 12, 2010 at 9:58 AM

Personal use is ok of course ;-)

Even when the (managed) plugin wants to take care of its own dependency loading, I still want them to be able to reuse the plumbing VST.NET already does. That is why AssemblyLoader and FileFinder classes are public.

I already started work on the plugin specific config and its a lot simpeler than I thought. My current train of thought (and impl ;-) is:

- The config name is the same name as the renamed Jacobi.Vst.Interop name (so MyPlugin.dll.config) and should be located next to that assembly.
- The plugin specific config can also contain the vstnetProbePaths appSettings config and will be used by the AssemblyLoader (PrivateProbePaths).
- The full configuration is passed to the plugin (command stub) as a System.Configuration.Configuration object.

That should allow the plugin to extract any custom config you might need. Problem is that non of the standard configuration features of the .NET framework will work (as far as I can see). This means that you cannot configure (say) diagnostics trace listeners in the plugin specific config and expect the System.Diagnostics.Trace class to know about it. AFAIK the .NET framework alsway looks at the main (exe) config.

When i've got this working I also plan to introduce an appSettings key that allows you to name the managed plugin assembly so you can override the naming convention that VST.NET currently uses (.net.dll/.net.vstdll).

How does this sound? usefull?

Coordinator
Mar 12, 2010 at 7:09 PM

I (think I) got the plugin config working. There are still some small 'issues' I want to iron out, but it should be usable at its current state. 

Its pretty simple: next to your renamed Interop assembly you place a config file with the same name and a .config extension. So MyPlugin.dll and MyPlugin.dll.config.

The appSetting 'vstnetProbePaths' can be specified in the config file and will be used for assembly resolvement (also VST.NET assemblies when its the first time). The System.Configuration.Configuration object is assigned to the plugin Command Stub and accessable just after the ctor has executed.

Give it a spin and tell me what you think.

Mar 14, 2010 at 8:06 AM

Hello Marc!

I also noticed Trace class only looks after exe config. (To extract actual App domain I think....). Your current plugin-specific MyPlugin.dll.config seem very OK to me. I'll give it a try as soon as possible.

I tried things according to my latest post; -> Actual plugin implementation must be responsible for its own linking and rigth now I work on dynamic runtime linking of all stuff my plugin needs. This statement is easy to write down but complicated to implement. It is possible, but in order to achieve that I would have to change my code a lot. -> Working with System.Object class an invoking methods on it. UGLY. If you have one (stand alone) assembly to link my statement is OK. If that assembly internally references to other assemblies than you got UGLY things to do.

Just one comment: if you don't use that AssemblyResolve event in your classes, than users can resolve linking problems themselve using it. With config file you solved linking problems of course, but in your way (which is good of course) but someone will more like some Registry oriented solution rather config file.

an

Coordinator
Mar 14, 2010 at 1:46 PM

Yep: I use the standard TraceSource object and that only takes its 'orders' from the .exe.config. I dont think any standard .NET config can be placed in the plugin dll.config and work automatically. But custom config will. See how I use the plugin dll.config (very simple) in a new wrapper plugin sample.

I dont understand your dynamic linking comments. To my knowledge .NET only supports dynamic linking. It only becomes a problem when you deploy on non-default probe paths (like in VST.NET) and that can be fixed by handling the AssemblyResolve event. There is no need to abstract to system.object AFAIK.

Would it be a good idea to implement a Detach method on the AssemblyResolver, so a plugin can always decide to inject their own assembly resolvement logic?

Hope it helps.

Mar 14, 2010 at 5:29 PM

When trying things with the latest source code after running a plugin (with .dll.cnfig file

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="vstnetProbePaths" value="D:myTestFolder\VstKasRta\bin\Release"/>
  </appSettings>
</configuration>

I got the following error message:

Could not laod file or assembly 'XYZ, Version=1.0.3400.39959, Cultur=neutral, PublicKeyToke=null' or one of its dependencies. Invalid pointer (Exception from HRESULT: 0x80004003 (E_POINTER))

ALL assemblies are in the folder specified by the config file and the thing runs properly if I put all dll's with actual plugin dll together and run it with vsthost 1.47

?? An

 

Coordinator
Mar 14, 2010 at 5:51 PM

All assemblies are in that folder?

But not the renamed Interop and the config, right?

If that is the case I would like to dbug that scenario. could you email me the assemblies?

obiwanjacobi at hotmail dot com

Thanx.

Mar 14, 2010 at 10:39 PM

I created very simple demo project and the same thing happens. More info inside the sent project.

Comment to my previous post:

I tried things according to my latest post; -> Actual plugin implementation must be responsible for its own linking and rigth now I work on dynamic runtime linking of all stuff my plugin needs. This statement is easy to write down but complicated to implement. It is possible, but in order to achieve that I would have to change my code a lot. -> Working with System.Object class an invoking methods on it. UGLY. If you have one (stand alone) assembly to link my statement is OK. If that assembly internally references to other assemblies than you got UGLY things to do.

Code snippet explains...

private void doSomething()
{
   Assembly assembly = Assembly.LoadFile("D:/myMagicDll.dll");
   Type type = assembly.GetType("MagicLibrary.Processor");
   ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
   object obj = ci.Invoke(null);
   MethodInfo mi = type.GetMethod("Process");
   object processResult = mi.Invoke(obj, new object[] { 1, 2 });
   .
   .
   .
}

If I have to reorganize my code, to (programmaticaly) dinamically link things together (using no referenced assemblies) then interacting between objects would be done like in the snippet. UGLY. So any other way is better. But in fact there is no Assembly resolver needed.

an

Coordinator
Mar 15, 2010 at 7:24 AM

This late binding that you do, can be a real performance drain. Why do you not want to take a (compile time) dependency on the types in myMagigDll?

Another option could be to create a seperate assembly with just the interfaces (contracts) and use that to communicate. Thats basically what I do in VST.NET: Core contains the contracts, the managed plugin contains the implementation. The root class is created -the plugin command stub- by locating a public class that implements a certain interface. See the ManagedPluginFactory (Core.Plugin)

I would not advice you to rewrite your code to use reflection/late binding like that.

Mar 15, 2010 at 7:49 AM

Hello.

At first I don't know whether your answer relates to project I sent to you by email or to "doSomething" code snippet.

I agree that late binding could be a performance drain. I would not late-bind parts of code that gets executed many times in any way. The "do something" snippet is just an explanation to my post on Sun at 9:06 AM. If there is not any other chance to control binding (but there is, according to your per-plugin config file) then someone could actualy bind things together this way. This is NOT the way i bind things together.

An

Mar 15, 2010 at 10:45 AM

Hello again.

I changed my code in a way everything binds at a compile time during the ctor of actual plugin implementation. Still with no success (i got some sort of argument null exception...).

An

Coordinator
Mar 15, 2010 at 11:00 AM

The constructor of the Plugin Command Stub is very early in the creation process. I do not understand the type of problem you're now facing but you might try moving you code from the ctor to the GetPluginInfo method.

Perhaps a stack trace of the exception would clarify things for me?

Mar 15, 2010 at 11:38 AM

I actually did that. Things work now.

An

Coordinator
Mar 15, 2010 at 12:13 PM

Great!

Do I still need to look at the code you sent me?

Mar 15, 2010 at 12:46 PM

Hello Marc

Actually there's no need to look at that code... :). I think we are yet not far from things to run. When I try to load another managed plugin with my DAW I still get an exception. (VST.NET bootsraper something... quite long to write) When I load only one managed VST plugin thing work.

an

Coordinator
Mar 15, 2010 at 12:50 PM
Edited Mar 15, 2010 at 12:51 PM

You can copy the text from any message box to the clipboard by using Ctrl+Shift+C.

Bootstrapper error means that something goes wrong at a very early stage. Are you sure Jacobi.Vst.Core can be found:
- either next to the plugin dll (renamed interop)
- probe path in exe.config
- probe path in dll.config

Hope it helps.

Mar 15, 2010 at 10:02 PM

Hello

To make things clear I created test project and sent it to you by email. There are two VST projects VSTTest and VSTTest2. I can sucessfuly load each one of them using vsthost, but loading fails if I try to load both. All requirements are succeeded -> valid config file and vst.core at probe path. Exception I get:

System.IO.FileLoadException: Could not load file or assembly 'Jacobi.Vst.Core, Version=0.9.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Invalid pointer (Exception from HRESULT: 0x80004003 (E_POINTER))

File name: 'Jacobi.Vst.Core, Version=0.9.0.0, Culture=neutral, PublicKeyToken=null' ---> System.ArgumentNullException: Value cannot be null.

Parameter name: str

   at System.Security.Permissions.FileIOPermission.HasIllegalCharacters(String[] str)

   at System.Security.Permissions.FileIOPermission.AddPathList(FileIOPermissionAccess access, AccessControlActions control, String[] pathListOrig, Boolean checkForDuplicates, Boolean needFullPath, Boolean copyPathList)

   at System.Security.Permissions.FileIOPermission..ctor(FileIOPermissionAccess access, String path)

   at System.Reflection.Assembly.LoadFile(String path)

   at Jacobi.Vst.Core.Plugin.AssemblyLoader.CurrentDomain_AssemblyResolve(Object sender, ResolveEventArgs args)

   at System.AppDomain.OnAssemblyResolveEvent(String assemblyFullName)

   at Jacobi.Vst.Interop.VSTPluginMainInternal(Bootstrapper bootstrapper, IntPtr hostCallback)

   at Jacobi.Vst.Interop.VSTPluginMain(IntPtr hostCallback)

I have also 2 questions stated in GetPluginInfo method in the VSTTest project.

This demo project DIFFERS from the one I sent yesterday. Same thing happens also with my regular .net vst plugins

An

Coordinator
Mar 16, 2010 at 6:35 PM
Edited Mar 16, 2010 at 6:40 PM

Hi An,

I've checked in a fix for the problem of loading multiple VST.NET plugins at once. That should work now.

FYI: you can subscribe multiple events to the AssemblyResolve event (checked the code with Reflector): the first event handler that returns non-null is the winner ;-)

The problem with resolving the dependencies you bring in at runtime (button) is that all the temporary paths that the bootstrapper and AssemblyLoader use are cleared when the plugin is loaded. So You have to grab these paths from the AssemblyLoader.PrivateProbePaths either during the ctor of the plugin command stub or the GetPluginInfo method. You could also call the CreateFileFinder method on the AssemblyLoader at that point to retrieve an instance that also contains the global paths.

With this information (either the private probe paths or the file finder) and your own event handler registered at the AppDomain.AssemblyResolve event, you should be able to locate these runtime dependencies.

Let me know if you run into any more trouble and thanx for sticking with this and helping me to get this working properly.

BTW: the path to the renamed interop assembly is added to the private probe paths during loading, so a config with a vstnetProbePath setting specifying the same path, does not really add anything to the resolvement logic. So in the examples you sent me, you can delete the config files.

Hope it helps.

Mar 16, 2010 at 8:55 PM

Hello Marc,

I just checked latest source code. It works now. Tnx for all your effort. I understand now how to even create custom way linking. In the "first" (and in most cases the only) phase things gets linked according to your bootstraper config file implementation. In the possible "second" phase things can get linked through AssemblyResolve event. (This is useful when you want to somehow "hide" dlls you use and pack them in .xy file)... Cool thing, very cool.

I have just one q for now... in fact it is to be in a separate topic, but is related to example I sent to you yesterday. Cubase LE 1.0.10 Build 110, built on feb 20, 2006 doesn't show any properties of these demo plugins and marks them red -> "invalid" VST plugin. Vsthost displays wrong number of inputs and outputs also. That happens with all my managed vst plugins. Is it possible to give a look at what am I doing wrong?

Tnx an

Coordinator
Mar 17, 2010 at 6:55 AM
Edited Mar 17, 2010 at 7:02 AM

Actually, both the Bootstrapper and the AssemblyLoader use the AppDomain.AssemblyResolve event and both are always used. The job of the bootstrapper basically, is to bring in the Core assembly, where the AssemblyLoader is located. Once thats done it unregisters from the AssemblyResolve event and hands proceedings over to the AssemblyLoader. Big difference is that the bootstrapper is instantiated for every plugin that is loaded, while the AssemblyLoader is created only once and is static. So for subsequent plugins that are loaded, the bootstrapper is loaded and will only be called upon when the AssemblyLoader cannot find the dependencies.

All plugin capabilities are communicated to the host using the VstPluginInfo and the implementation of the CanDo method. So double check if you setup your VstPluginInfo correctly and if you want to respond to some of the CanDo's (there's an enum with most common options and a helper to convert those to VST-compatible strings).

Also all plugins must have an unique ID. The ID I saw in your code is not 'typical' (too short). Use the FourCharacterCode class (Core) to create the ID. Pick 4 characters (a-zA-Z) and instantiate the FourCharacterCode class with them and then call the ToInt32() method on the class instance. Disclaimer: Officially you should apply for a Plugin ID at Steinberg.

You might want to consider using the Framework. The Framework takes a lot of this low-level stuff out of your hands. You only need to implement interfaces of the features you want to support. So you can focus more on the functional aspects not on the technical ones. It also provides you with some base classes you can inherit from, speeding up development even more. And it has facilities for Programs and Parameters (and Persistence). Is that (Parameters) what you mean by properties?

Take a look at the sample plugins that come with VST.NET (other than the CorePlugin ;-). They are all build on this Framework.

 

Hope it helps.

Mar 17, 2010 at 9:20 AM

Tnx a lot, I'll take a look over framework and framework samples.

an