Fastest Way To Get Plugin Info

Jul 9, 2015 at 8:49 PM
Just wondering what the fastest way to iterate through plugins would be...

I need to get information, particularly whether the plugin is an effect, or an instrument.

Is there an approach that can just do a once over pass on the plugins without loading them up, or should I load each plugin up, gather then information in an XML file, and then dispose of all the plugins in order to create a repository of plugins?
Jul 9, 2015 at 11:22 PM
My approach is to run some heuristics to determine if I'm dealing with a 32 or 64 bit library, oddly detecting the architecture of a dll with 100% certainty is a difficult problem. After that I load the plugin and query it's properties. If loading fails then I consider it's not a valid plugin. The only reliable definition of an effect is a plugin that takes audio inputs. That includes instruments who behave as effects. I find the definition between the two to be largely semantic because they share the same structure and operate just the same. I wouldn't recommend disposing all the plugins at once because loading a library takes up quite a bit of memory. You should load one plugin at a time gather the info in a structure then immediately dispose of the plugin. If you're looking for high performance I'd recommend to keep that loop as tight as possible. After that you can serialize your structure to xml. I can't think of any reliable way to read metadata from a vst plugin dll without loading the dll. The metadata isn't sitting in the data section at a predefined offset. It's usually hardcoded inside the functions so the only way to get that information is to load the method table, locate the function and call it.
Jul 9, 2015 at 11:42 PM
Edited Jul 9, 2015 at 11:42 PM
As a side note scanning the hard drive for plugins is likely to be the performance bottleneck in your scenario. Haven't tried Windows 10 but I'm inclined to believe their file system caching and indexing services are still lagging behind the competition and there's little you can do to speed up the directory file listing process.
Jul 9, 2015 at 11:53 PM
Edited Jul 9, 2015 at 11:54 PM
That hard drive scanning scenario got me thinking. I don't do that in my host because I think the process is very slow, innefective and buggy. If I were to implement it, I would first spawn it in a separate process because scanning many unknown dll has a big chance of crashing your main application process. I'd put more heuristics to eliminate common vendors dll by checking the library manifest metadata first. Some quick research should identify big offenders like microsoft, oracle, adobe etc.. There's more techniques that could be applied to determine if a dll is unlikely to be a vst plugin.
Jul 11, 2015 at 12:13 AM
Thanks Yury!

This is all excellent advice. I think you've answered my fundamental problem.

Of course, the user experience should be fast and bug free, so I think the only way is to build a database of plugins in 1 painful step, and then only use valid plugins from that database afterwards. I will start coding!
Jul 11, 2015 at 1:28 AM
I think you might have the problem backward.

First you need to identify which drive to target, local, network, share, cd-rom, floppy. After that ignore windows directory using proper API and scan for architecture if running on a 64 bit machine and ignore common vendor manifest. Only after that I'd go for scanning the plugin.
public static bool GetPluginInfo(String pluginPath, out String pluginName, out String company, out String category, out int audioInputCount)
{
    bool infoLoaded = false;
    VstPluginContext plugin = null;
    pluginName = company = category = String.Empty;
    audioInputCount = 0;

    try
    {
        PresetHostCommandStub hostCmdStub = new PresetHostCommandStub();
        plugin = VstPluginContext.Create(pluginPath, hostCmdStub);
        plugin.PluginCommandStub.Open();
        pluginName = plugin.PluginCommandStub.GetEffectName();
        company = plugin.PluginCommandStub.GetVendorString();
        category = Convert.ToString(plugin.PluginCommandStub.GetCategory(), CultureInfo.InvariantCulture);
        audioInputCount = plugin.PluginInfo.AudioInputCount;
        infoLoaded = true;
    }
    catch (Exception)
    {
    }
    finally
    {
        if (plugin != null)
            plugin.Dispose();
    }

    return infoLoaded;
}

private enum MachineType : ushort
{
    IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
    IMAGE_FILE_MACHINE_AMD64 = 0x8664,
    IMAGE_FILE_MACHINE_I386 = 0x14c,
    IMAGE_FILE_MACHINE_IA64 = 0x200
}

// Returns true if the dll is 64-bit, false if 32-bit, and null if unknown
public static bool? UnmanagedDllIs64Bit(string dllPath)
{
    try
    {
        switch (GetDllMachineType(dllPath))
        {
            case MachineType.IMAGE_FILE_MACHINE_AMD64:
            case MachineType.IMAGE_FILE_MACHINE_IA64:
                return true;
            case MachineType.IMAGE_FILE_MACHINE_I386:
                return false;
            default:
                return null;
        }
    }
    catch (Exception)
    {
        return null;
    }
}

private static MachineType GetDllMachineType(string dllPath)
{
    MachineType machineType = MachineType.IMAGE_FILE_MACHINE_UNKNOWN;

    //see http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
    //offset to PE header is always at 0x3C
    //PE header starts with "PE\0\0" =  0x50 0x45 0x00 0x00
    //followed by 2-byte machine type field (see document above for enum)
    using (FileStream fs = new FileStream(dllPath, FileMode.Open, FileAccess.Read))
    using (BinaryReader br = new BinaryReader(fs))
    {
        fs.Seek(0x3c, SeekOrigin.Begin);
        Int32 peOffset = br.ReadInt32();
        fs.Seek(peOffset, SeekOrigin.Begin);
        UInt32 peHead = br.ReadUInt32();

        if (peHead == 0x00004550) // "PE\0\0", little-endian
            machineType = (MachineType)br.ReadUInt16();
    }

    return machineType;
}
Jul 12, 2015 at 12:46 AM
Hey Yury. This code will come in handy in the long run - especially the code for checking the DLL bit type.

But, at the end of that, the user has to select their VST folder anyway - or can I get that from the registry? So, it's really just a matter of scanning that folder, and handling the exceptions that come up. It will only do a full scan once, and then update scans after that which won't be too bad. Maybe occasionally a repair scan, but your strategy is good.
Jul 12, 2015 at 12:53 AM
Could be anywhere but the following are common defaults values of installers:
C:\Program Files\Steinberg\Vstplugins\
C:\Program Files (x86)\Steinberg\Vstplugins\
Jul 12, 2015 at 7:08 AM
Thanks Yury. Your code was a big help. I am attaching my library. This library will allow you to scan a given folder and give you information about the plugins. It also gives you its progress as it is working, and will serialise everything to XML for you so you can reload it later. I've tested this today, and it works well.

https://onedrive.live.com/redir?resid=47321C0630B57E1D!4941&authkey=!AFNCnDLB0-2R-WQ&ithint=file%2c7z