SlimDX 1 is contained in a single, monolithic C++/CLI assembly. SlimDX 2 is structured differently: it is split up into both public and private DLLs. The public DLL, which will be directly referenced by client code, is written in C# and contains the definitions for all the interfaces, enumerations, et cetera. The private DLL, written in C++/CLI, contains the interface implementations and all our internal machinery.
The primary motivation for organizing the library in this fashion was to eliminate the problems with building “Any CPU” applications using SlimDX.
Managed applications built in “Any CPU” mode are essentially asking the CLR to load them as processes most-appropriate to the host machine: if you’re running on the x64 architecture, you get a 64-bit process. On x86, you’d get a 32-bit process.
For native code, however, the target architecture needs to be decided at compile-time, which is why there are distinct 32-bit and 64-bit versions of the SlimDX assembly. Unfortunately, you cannot load a 32-bit DLL into a 64-bit process.
If you’re using the copies of SlimDX that our installer places in the GAC, you can avoid running afoul of this problem by editing your .csproj. If you’re a user who prefers to reference the SlimDX DLLs themselves, you are out of luck. You have to pick a specific version to reference, and you have to set the machine type of your project to match.
By using a two-part distribution, we can ship a public assembly configured for “Any CPU” use. This assembly can detect whether 32- or 64-bit code would be more appropriate and load the correct private implementation DLL into your process.
This approach also allows us to write more of SlimDX in C#, which is far more pleasant language to work with than C++/CLI, and has a much better toolchain.
It also lets us improve the health of the code we do need to write in C++/CLI. Because of a peculiarity in how templates (not generics) interact with managed types, the presence of a template type anywhere in a class hierarchy results in a complete lack of IntelliSense support for that class hierarchy… but there’s a lot of redundant boilerplate code in SlimDX, exactly the kind of code one would like to use templates to write just once. We had to compromise and use macros to provide that code for SlimDX 1.
However, with the split-assembly approach — along with with our adoption of interfaces — the classes “polluted” by templates exist entirely in the private assembly and are never exposed by their concrete types; the lack of IntelliSense becomes a non-issue.
There is one small downside, unfortunately. Since the implementation DLL is loaded manually, clients who reference and ship private copies of SlimDX (that is, anyone not using the GAC version) will need to add or link the SlimDX implementation DLLs (and PDBs) in their .csproj files in order to allow the build system to copy those files into the directory of the built application.
The downside seems pretty small compared to the final gain. I would be ready to annoy the programmer if the end user benefit from this.
I just had an idea, but am not sure whether it would work. Why don’t you put the native 32 and 64 bit mixed mode assemblies as embedded resources into the fully managed DLL and load them dynamically at runtime? It may even work without having to write them to disk at all.
That’s a really interesting idea. The immediate problem I see is that the
Assembly.Loadoverride we would need to use (the one taking byte arrays for the raw assembly and symbol files) expects the bytes to form a COFF image, not a PE, so we’d need to convert it. The project is also structured such that the private DLLs depend on the managed DLL (to gain access to the interfaces), so this would create a circular dependency.These seem like solvable issues, though, especially with our new build systems. We could probably always fall back to deserializing the private DLLs to a private local store someplace and loading them from disk. Although that’s not as elegant, it would solve the problem.
But the PE format includes a COFF header anyway, thus there is no need to convert it, i guess. I’ve used Assembly.Load many times to load default assemblies directly from data, yet i didn’t do it for mixed-mode assemblies. i don’t know if this is a special case.
Anyway, to solve the circular dependency you can assign to AppDomain.CurrentDomain.AssemblyResolve inside your fully managed assembly before calling Assembly.Load . As soon as the mixed-mode assembly gets loaded it will throw the event because it is looking to find your fully managed assembly. Catch it and just return typeof(SomeClass).Assembly and everything should be fine.
I tried the quick, obvious method of using
Assembly.Loadright before I replied to your first comment, and it failed, producing an exception that appeared to indicate a format issue… so I assumed the COFF header wasn’t enough. Taking another look at the exception details, it could also be related to security or verifiability, I suppose. It’s not very clear. I haven’t invested too much more time into researching the issue because I have more pressing things to take care of first — mainly, getting the SlimDX 2 prototype code to a point where it can do something and checking it in.The circular dependency issue I mentioned is the one that exists at build time, though: the public DLL
.csprojwould need to add the private DLLs as embedded resources, and so would need those DLLs built first. But the private DLLs reference the public DLL to pick up their interfaces, so they require the public DLL to be built.Any idea when 2.0 might see daylight? I’m wondering if I should hold off on a big SlimDX 1-based project.
I’m not sure you should wait. It will be while before v2.0 is ready for production use; I wouldn’t expect to see it until the end of the year. If you start on the project now, you shouldn’t have too much trouble migrating to 2.0 when you’re ready if you keep aware of the kinds of changes we’re making, most of which I’ll be writing about here as we finalize them.
These changes would be awesome, but I want it faster! I want to use SlimDx in my CadLib component, but I don’t want to have x86/x64 versions of my component. I can contribute man hours in May, please contact me to get some things done!
Thanks!
Wout
Thanks for the offer! Unfortunately, we don’t have a final plan for the design of v2 yet, and as such we’re not really looking for patches or contributions to it yet. It’s unlikely, given our respective schedules, that we’ll be in that position by the end of April, either.
However, the good news is that you don’t necessarily need to wait for v2 to build a “Any CPU” version of your product. Our documentation, in the “Programming Guide” chapter, has a section on “Handling 32- and 64 Bit Targets” which explains how you can configure a project file to target a version of SlimDX installed in the GAC.
Yes, I was aware of that (thanks for the link though). But I don’t want to force my customers to install the assemblies in the GAC, so the v2 design would be very very nice. I want my customers to keep all options open as to where to install the assemblies. I’m sure that a lot of people in the .NET realm would benefit as well.
Wout
Oh, the offer for contribution still stands. If we can help with design ideas that’s an option too. I mentioned May, but I should have said from May onwards, so later is OK too, but rather sooner than later ofcourse!
Wout
Change of plans, we’ll just wait with using SlimDX until next year when the version 2 wrapper comes out. Keep up the good work guys!
Wout