64/32 bit plugin .rhi package

Hi all,

I’m developing a plugin which depends on a .dll. In http://developer.rhino3d.com/guides/rhinocommon/plugin-installers-windows/ it is suggested that I include the 64 and 32bit versions of the .dll under x64 and x86 folders in the installer directory structure. At the moment, the structure is as follows:

Plugin
  Rhino 5.0
    x86
      Plugin.rhp
      Plugin.dll (32 bit version)
      Plugin.rui
    x64
      Plugin.rhp
      Plugin.dll (64bit version)
      Plugin.rui

Running the resulting .rhi installer seems to work fine, but I noticed that both Rhino 64bit and Rhino 32bit associate with the x64 version of the plugin. So in the 32bit version of Rhino the plugin is missing functionality, as it can’t load the dependent .dll.

Any thoughts on this? Since Rhino itself installs both versions simultaneously, I’d very much like to avoid getting into distributing a separate 32bit version of the plugin.

Thanks in advance,
Sean

Hi @sgeggie,

I’ve had luck omitting the Plugin and Rhino 5.0 folders.

Here is what has worked for me:

x64
  |_ plugin_x64.rhp
  |_ etc...
x86
  |_ plugin.rhp
  |_ etc...

Does this help?

– Dale

Sadly not. Contents are unpacked to “%APPDATA%/McNeel\Rhinoceros\5.0\Plug-ins\PluginName (GUID)[version]” as expected, but registry values under “Computer\HKEY_CURRENT_USER\Software\McNeel\Rhinoceros\5.0\Plug-Ins[GUID]\PlugIn” point to the x64 folder.

Interestingly, if I omit the x64 folder it points to the x86 one okay (but now 64bit Rhino has the issue). Wonder if I could hack some kind of workaround from that.

Thanks,
Sean

Hi @sgeggie, what platform is the RHP compiled for? Are you compiling two versions (64- and 32-bit) or a single AnyCPU version? The Rhino Installer Engine inspects each RHP to determine its bitness and uses this information when registering the plug-ins for each version of Rhino 5. It doesn’t look at any associated DLLs.

Thanks, @will! Very interesting. I’ve been building AnyCPU, so this is probably exactly what I’m running into. I just assumed the Install Engine would use the folder structure.

I’ll try this out, but it sounds like you’ve nailed it. Might be worth adding this to the Developer Docs?

Thanks for the feedback! This page goes into more detail about how the Rhino Installer Engine determines which Rhino to register a plug-in against. That said, the information is clearly not visible enough. I’ll add a note the the “Plugin Installers (Windows)” page which makes it clear that the folder structure is just an example and is actually irrelevant in determining compatibility.

Ah yes, obvious in hindsight now that I come to re-read that page.

Drat, I’m still having trouble.

I am now building separate x64 and x86 versions of the .rhp and placing those with their .dll counterparts like so:

Plugin
  Rhino 5.0
    x86
      Plugin.rhp (x86 version)
      Plugin.dll (x86 version)
      Plugin.rui
    x64
      Plugin.rhp (x64 version)
      Plugin.dll (x64 version)
      Plugin.rui

Wrapped that in a .rhi installer and ran it. 64bit Rhino was happy (although I had to manually enable my plugin’s toolbar - perhaps a separate question on this later).
32bit Rhino, however, was even worse off than before, giving a Plug-in Error (initialization failed). My guess is it’s trying to load the x64 .rhp. Indeed, looking in the registry I find that
Computer\HKEY_CURRENT_USER\Software\McNeel\Rhinoceros\5.0\Plug-Ins\GUID\FileName
points to the /x64/ path. If I edit this to the x86 path it works flawlessly.

Incidentally, I’m always picking the “Just me” install option.

Any thoughts? Anything I may be messing up in Visual Studio that could be fouling up the auto detection?

One more note: After switching to platform-specific builds, I have also tried @dale’s suggestion and reduced .rhi contents to just x86 and x64 folders. Same problem.

@sgeggie I hope by chiming in I don’t make it worse. If your plugin actually doesn’t do anything specific for x86 vs x64, then there is no reason to have the folder structure. I have one plugin (Iris) which has the following rhi structure:

Iris.Win.rhi/
├── Iris.Win.rhp (for Rhino 5 x86 and x64)
├── Iris.WIP.rhp (for Rhino WIP / BETA / 6)
├── Resources (a directory with stuff I need to distribute)
└── Newtonsoft.Json.dll

If there is nothing specific you need that differentiates the x86 and x64 versions, don’t make your life more complicated. If something simple like the above does not work, then maybe the issue is on our side?

One more question, which version and service release of Rhino are you working with? There have been some updates in the Rhino 5 service releases which might affect how things install (right @Will ?).

Would you mind sending me the .rhi installer to have a look at, either via direct message here, or to will@mcneel.com?

Thanks for the Input, Luis! It’s very welcome.

What you suggest is more or less where I started, since Rhino would ignore the x86 folder anyway. I’d be happy to have a single .rhp/single .dll approach, but it seemed that the plugin was unable to load the 64bit .dll in 32bit Rhino and vice versa.

The fact that this does work for you suggest that I should have a close look at how I load the .dll.

@will: I’ll wrap up an installer and send it to you at once. Much obliged!

@sgeggie, thanks again for sending over the .rhi installer. Very handy for testing.

Unfortunately, I’ve determined that we have a bug on our side where platform-specific .NET plug-ins are not correctly registered for 32-bit Rhino 5. I’m going to fix this in the Rhino Installer Engine that ships with Rhino 6, but this doesn’t exactly solve the problem that you’re facing. I’ve been looking for a workaround that doesn’t rely on this functionality in Rhino.

One option would be to compile your plug-in as AnyCPU (as it was originally). Then, assuming the DLL that you’re referencing is unmanaged, you could detect the platform at runtime and load the appropriate library.

Here are a couple of StackOverflow answers that should point you in the right direction:

The second, in particular, looks promising…

static class MyDll
{
    static MyDll()
    {            
        var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath;
        var myFolder = Path.GetDirectoryName(myPath);

        var is64 = IntPtr.Size == 8;
        var subfolder = is64 ? "\\win64\\" : "\\win32\\";

        LoadLibrary(myFolder + subfolder + "MyDll.dll");
    }

    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibrary(string dllToLoad);

    [DllImport("MyDll.dll")]
    public static extern int MyFunction(int var1, int var2);
}

Please let me know if this is useful, or not, or if I can help further!

Thanks so much for looking into this. As much as this is a bother, it’s always satisfying to boil things down to a specific bug. Glad to hear it’ll be fixed in Rhino 6.

Your runtime platform detection alternative is a good idea. Thanks for the code snippet.

Thank you for being understanding! I’ve created a sample RhinoCommon plug-in that uses the technique that I linked to above. Loading libraries in this way will of course work fine in Rhino 6 too.

RH-42886 is fixed in the latest BETA

Good to hear! We’re doing the AnyCPU dynamic load thing instead now, and will continue to while we support Rhino 5.

1 Like