I'm in dll hell.
I'm building a plugin for a huge, ancient and very powerful software suite called ANSYS. They have a plugin framework. I had hoped that they would magically handle everything for me via AssemblyContexts or AppDomains or some other clever dotnet device that I don't understand. They do not.
The result is that I've created an application that depends on GRPC.core 1.16.0 via nuget. I wrote a little application that drives my plugin with a winform host. It loads and works perfectly, finding my library in ~/myproject/bin/debug/grpc.core.1.1.16.dll that exists right beside the class-library that is my plugin, no problem.
When I run my plugin in the ANSYS process space, which happens to also depend on grpc 1.0.0.0, the linker finds C:\Program FIles\ANSYS\...\WIN64\grpc.core.dll. No Good.
One odd thing about the Nuget GRPC package is that it adds a reference with a "reference version" of 1.0.0.0, where most other nuget packages have their reference version match the nuget package version. If i manually change the reference version the compiler wont find the library.
<Reference Include="Grpc.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d754f35622e28bad">
<HintPath>..\packages\Grpc.Core.1.16.1\lib\net45\Grpc.Core.dll</HintPath>
</Reference>
edit: the key is in the above line. The Nuget published Grpc.core artifact is at
AssemblyInformationVersion=1.16.1.0, AssemblyFileVersion=1.16.1.0, AssemblyVersion=1.0.0.0. I logged this as a request against GRPC. More Below.
Thus I need to tell the runtime linking facilities not to use grpc.core...dll found in ANSYS's own binary directoryWhats more, there is exactly one dll (and its dependents) that I wish to load from my parent processes context: and that's ANSYS API dlls themselves, which are probably already in the GAC. In my project I've included this as a non-nuget reference with "build action: do not copy" selected.
So my questions:
is there something simple and easy I can do at runtime to tell the runtime-linker "when somebody loads a type from an assembly you think should begrpc.core, do not load 1.0.0.0, find 1.16.0.0 exactly"?the runtime was already matching the needed library by "strong name". The problem is that the 1.16.0 is a misnomer. That version string was informational, but the assembly itself was version 1.0.0.0. Fusion was loading the library I wanted by exact match already.
- is there something smarter I can do with appdomains or contexts or another C# device to explictly enter some kind of nested scope? Could I go so far as to log this as a bug in ANSYS's API?
I've tried digging into this myself, but I'm not a dotnet expert and finding out whether I'm looking at a nuget package configuration option --which isn't relevant to me, or an old-fashioned dotnet runtime option, has been very tricky.
update 1:
I've tried using AppDomain.CreateDomain, and it does indeed solve my problem, but it also requires me to provide a marshalling strategy for the already-loaded API objects. In other words, if you're programming against a plugin framework that has an api similar to:
public void DoMyPluginsFunctionality(ApiProvidedInputContext context){
var myPlugin = AppDomain.Create(
strongName: "MyCompany.MyPlugin.; Version=1.2.3.4 ...",
baseDirectory: "C:\\Program Files\\MyPlugin\\bin"
)
//success! MyCompany.MyPlugin loads the version of GRPC I want!
myPlugin.unWrapAsDynamicProxy().doFunctionality(context)
//error: No marshalling strategy and/or not serializable and/or swizzling errors
}
Then the runtime will require you to marshall (serialize) the context variable, because .net will not let you share memory across AppDomain boundaries.
So my new question:
- given I cant use AppDomains myself
- given that Grpc.core is always published as AssemblyVersion=1.0.0.0
What are my options?
- Stop using newer features of GRPC.core and live in fear of my parent processes dependencies
- use a strategy similar to shading. Is there something like shading in the .net world?
- Edit the published binary's version metadata. Can I dynamically edit a published binaries version?
- rebuild GRPC myself with the version string updated --effectively a private fork of GRPC.
update 2:
The GRPC build system seems like its quite large and well maintained, so I'm hoping I can simply build it and change a vcproj file to include an updated version string.
Unfortunately it also seems quite complex, and I haven't quite got the targeting/cross-compiling (x64 targeting x86) worked out.